version 1.18, 2014/04/24 09:14:43 |
version 1.19, 2014/05/13 07:34:35 |
|
|
#include "tmux.h" |
#include "tmux.h" |
|
|
/* |
/* |
* Stack of paste buffers. Note that paste buffer data is not necessarily a C |
* Set of paste buffers. Note that paste buffer data is not necessarily a C |
* string! |
* string! |
*/ |
*/ |
|
|
ARRAY_DECL(, struct paste_buffer *) paste_buffers = ARRAY_INITIALIZER; |
u_int paste_next_index; |
|
u_int paste_next_order; |
|
u_int paste_num_automatic; |
|
RB_HEAD(paste_name_tree, paste_buffer) paste_by_name; |
|
RB_HEAD(paste_time_tree, paste_buffer) paste_by_time; |
|
|
/* Return each item of the stack in turn. */ |
int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *); |
struct paste_buffer * |
RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); |
paste_walk_stack(u_int *idx) |
RB_GENERATE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); |
|
|
|
int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *); |
|
RB_PROTOTYPE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); |
|
RB_GENERATE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); |
|
|
|
int |
|
paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b) |
{ |
{ |
struct paste_buffer *pb; |
return (strcmp(a->name, b->name)); |
|
} |
|
|
pb = paste_get_index(*idx); |
int |
(*idx)++; |
paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b) |
return (pb); |
{ |
|
if (a->order > b->order) |
|
return (-1); |
|
if (a->order < b->order) |
|
return (1); |
|
return (0); |
} |
} |
|
|
/* Get the top item on the stack. */ |
/* Walk paste buffers by name. */ |
struct paste_buffer * |
struct paste_buffer * |
paste_get_top(void) |
paste_walk(struct paste_buffer *pb) |
{ |
{ |
if (ARRAY_LENGTH(&paste_buffers) == 0) |
if (pb == NULL) |
return (NULL); |
return (RB_MIN(paste_time_tree, &paste_by_time)); |
return (ARRAY_FIRST(&paste_buffers)); |
return (RB_NEXT(paste_time_tree, &paste_by_time, pb)); |
} |
} |
|
|
/* Get an item by its index. */ |
/* Get the most recent automatic buffer */ |
struct paste_buffer * |
struct paste_buffer * |
paste_get_index(u_int idx) |
paste_get_top(void) |
{ |
{ |
if (idx >= ARRAY_LENGTH(&paste_buffers)) |
struct paste_buffer *pb; |
|
|
|
pb = RB_MIN(paste_time_tree, &paste_by_time); |
|
if (pb == NULL) |
return (NULL); |
return (NULL); |
return (ARRAY_ITEM(&paste_buffers, idx)); |
return (pb); |
} |
} |
|
|
/* Free the top item on the stack. */ |
/* Free the most recent buffer */ |
int |
int |
paste_free_top(void) |
paste_free_top(void) |
{ |
{ |
struct paste_buffer *pb; |
struct paste_buffer *pb; |
|
|
if (ARRAY_LENGTH(&paste_buffers) == 0) |
pb = paste_get_top(); |
|
if (pb == NULL) |
return (-1); |
return (-1); |
|
return (paste_free_name(pb->name)); |
|
} |
|
|
pb = ARRAY_FIRST(&paste_buffers); |
/* Get a paste buffer by name. */ |
ARRAY_REMOVE(&paste_buffers, 0); |
struct paste_buffer * |
|
paste_get_name(const char *name) |
|
{ |
|
struct paste_buffer pbfind; |
|
|
free(pb->data); |
if (name == NULL || *name == '\0') |
free(pb); |
return (NULL); |
|
|
return (0); |
pbfind.name = (char*)name; |
|
return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); |
} |
} |
|
|
/* Free an item by index. */ |
/* Free a paste buffer by name. */ |
int |
int |
paste_free_index(u_int idx) |
paste_free_name(const char *name) |
{ |
{ |
struct paste_buffer *pb; |
struct paste_buffer *pb, pbfind; |
|
|
if (idx >= ARRAY_LENGTH(&paste_buffers)) |
if (name == NULL || *name == '\0') |
return (-1); |
return (-1); |
|
|
pb = ARRAY_ITEM(&paste_buffers, idx); |
pbfind.name = (char*)name; |
ARRAY_REMOVE(&paste_buffers, idx); |
pb = RB_FIND(paste_name_tree, &paste_by_name, &pbfind); |
|
if (pb == NULL) |
|
return (-1); |
|
|
|
RB_REMOVE(paste_name_tree, &paste_by_name, pb); |
|
RB_REMOVE(paste_time_tree, &paste_by_time, pb); |
|
if (pb->automatic) |
|
paste_num_automatic--; |
|
|
free(pb->data); |
free(pb->data); |
|
free(pb->name); |
free(pb); |
free(pb); |
|
|
return (0); |
return (0); |
} |
} |
|
|
/* |
/* |
* Add an item onto the top of the stack, freeing the bottom if at limit. Note |
* Add an automatic buffer, freeing the oldest automatic item if at limit. Note |
* that the caller is responsible for allocating data. |
* that the caller is responsible for allocating data. |
*/ |
*/ |
void |
void |
paste_add(char *data, size_t size, u_int limit) |
paste_add(char *data, size_t size) |
{ |
{ |
struct paste_buffer *pb; |
struct paste_buffer *pb, *pb1; |
|
u_int limit; |
|
|
if (size == 0) |
if (size == 0) |
return; |
return; |
|
|
while (ARRAY_LENGTH(&paste_buffers) >= limit) { |
limit = options_get_number(&global_options, "buffer-limit"); |
pb = ARRAY_LAST(&paste_buffers); |
RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) { |
free(pb->data); |
if (paste_num_automatic < limit) |
free(pb); |
break; |
ARRAY_TRUNC(&paste_buffers, 1); |
if (pb->automatic) |
|
paste_free_name(pb->name); |
} |
} |
|
|
pb = xmalloc(sizeof *pb); |
pb = xmalloc(sizeof *pb); |
ARRAY_INSERT(&paste_buffers, 0, pb); |
|
|
|
|
pb->name = NULL; |
|
do { |
|
free(pb->name); |
|
xasprintf(&pb->name, "buffer%04u", paste_next_index); |
|
paste_next_index++; |
|
} while (paste_get_name(pb->name) != NULL); |
|
|
pb->data = data; |
pb->data = data; |
pb->size = size; |
pb->size = size; |
|
|
|
pb->automatic = 1; |
|
paste_num_automatic++; |
|
|
|
pb->order = paste_next_order++; |
|
RB_INSERT(paste_name_tree, &paste_by_name, pb); |
|
RB_INSERT(paste_time_tree, &paste_by_time, pb); |
} |
} |
|
|
|
/* Rename a paste buffer. */ |
|
int |
|
paste_rename(const char *oldname, const char *newname, char **cause) |
|
{ |
|
struct paste_buffer *pb; |
|
|
|
if (cause != NULL) |
|
*cause = NULL; |
|
|
|
if (oldname == NULL || *oldname == '\0') { |
|
if (cause != NULL) |
|
*cause = xstrdup("no buffer"); |
|
return (-1); |
|
} |
|
if (newname == NULL || *newname == '\0') { |
|
if (cause != NULL) |
|
*cause = xstrdup("new name is empty"); |
|
return (-1); |
|
} |
|
|
|
pb = paste_get_name(oldname); |
|
if (pb == NULL) { |
|
if (cause != NULL) |
|
xasprintf(cause, "no buffer %s", oldname); |
|
return (-1); |
|
} |
|
|
|
RB_REMOVE(paste_name_tree, &paste_by_name, pb); |
|
|
|
free(pb->name); |
|
pb->name = xstrdup(newname); |
|
|
|
if (pb->automatic) |
|
paste_num_automatic--; |
|
pb->automatic = 0; |
|
|
|
RB_INSERT(paste_name_tree, &paste_by_name, pb); |
|
|
|
return (0); |
|
} |
|
|
/* |
/* |
* Replace an item on the stack. Note that the caller is responsible for |
* Add or replace an item in the store. Note that the caller is responsible for |
* allocating data. |
* allocating data. |
*/ |
*/ |
int |
int |
paste_replace(u_int idx, char *data, size_t size) |
paste_set(char *data, size_t size, const char *name, char **cause) |
{ |
{ |
struct paste_buffer *pb; |
struct paste_buffer *pb; |
|
|
|
if (cause != NULL) |
|
*cause = NULL; |
|
|
if (size == 0) { |
if (size == 0) { |
free(data); |
free(data); |
return (0); |
return (0); |
} |
} |
|
if (name == NULL) { |
|
paste_add(data, size); |
|
return (0); |
|
} |
|
|
if (idx >= ARRAY_LENGTH(&paste_buffers)) |
if (*name == '\0') { |
|
if (cause != NULL) |
|
*cause = xstrdup("empty buffer name"); |
return (-1); |
return (-1); |
|
} |
|
|
pb = ARRAY_ITEM(&paste_buffers, idx); |
pb = paste_get_name(name); |
free(pb->data); |
if (pb != NULL) |
|
paste_free_name(name); |
|
|
|
pb = xmalloc(sizeof *pb); |
|
|
|
pb->name = xstrdup(name); |
|
|
pb->data = data; |
pb->data = data; |
pb->size = size; |
pb->size = size; |
|
|
|
pb->automatic = 0; |
|
pb->order = paste_next_order++; |
|
|
|
RB_INSERT(paste_name_tree, &paste_by_name, pb); |
|
RB_INSERT(paste_time_tree, &paste_by_time, pb); |
|
|
return (0); |
return (0); |
} |
} |