Here's updated diff. I renamed the compare funcs and make some other minor tweaks but didn't change the sticky/unsticky bits.
Why do you limit the buffer names to 16 characters? 16 characters is too short, but why limit them at all? Also not certain about limiting them to printables, seems like that will stop people using UTF-8 in buffer names. Index: cmd-capture-pane.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-capture-pane.c,v retrieving revision 1.26 diff -u -p -r1.26 cmd-capture-pane.c --- cmd-capture-pane.c 24 Apr 2014 09:14:43 -0000 1.26 +++ cmd-capture-pane.c 24 Apr 2014 09:44:42 -0000 @@ -38,7 +38,7 @@ char *cmd_capture_pane_history(struct a const struct cmd_entry cmd_capture_pane_entry = { "capture-pane", "capturep", "ab:CeE:JpPqS:t:", 0, 0, - "[-aCeJpPq] [-b buffer-index] [-E end-line] [-S start-line]" + "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] [-S start-line]" CMD_TARGET_PANE_USAGE, 0, NULL, @@ -165,7 +165,7 @@ cmd_capture_pane_exec(struct cmd *self, struct client *c; struct window_pane *wp; char *buf, *cause; - int buffer; + const char *bufname; u_int limit; size_t len; @@ -193,22 +193,15 @@ cmd_capture_pane_exec(struct cmd *self, server_push_stdout(c); } else { limit = options_get_number(&global_options, "buffer-limit"); - if (!args_has(args, 'b')) { - paste_add(buf, len, limit); - return (CMD_RETURN_NORMAL); - } - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(buf); - free(cause); - return (CMD_RETURN_ERROR); - } + bufname = NULL; + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); - if (paste_replace(buffer, buf, len) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); + if (paste_set(buf, len, limit, bufname, &cause) != 0) { + cmdq_error(cmdq, "%s", cause); free(buf); + free(cause); return (CMD_RETURN_ERROR); } } Index: cmd-choose-buffer.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-choose-buffer.c,v retrieving revision 1.17 diff -u -p -r1.17 cmd-choose-buffer.c --- cmd-choose-buffer.c 24 Apr 2014 09:14:43 -0000 1.17 +++ cmd-choose-buffer.c 24 Apr 2014 09:44:42 -0000 @@ -75,19 +75,20 @@ cmd_choose_buffer_exec(struct cmd *self, action = xstrdup("paste-buffer -b '%%'"); idx = 0; - while ((pb = paste_walk_stack(&idx)) != NULL) { + pb = NULL; + while ((pb = paste_walk(pb)) != NULL) { cdata = window_choose_data_create(TREE_OTHER, c, c->session); - cdata->idx = idx - 1; + cdata->idx = idx; cdata->ft_template = xstrdup(template); - format_add(cdata->ft, "line", "%u", idx - 1); format_paste_buffer(cdata->ft, pb, utf8flag); - xasprintf(&action_data, "%u", idx - 1); + xasprintf(&action_data, "%s", pb->name); cdata->command = cmd_template_replace(action, action_data, 1); free(action_data); window_choose_add(wl->window->active, cdata); + idx++; } free(action); Index: cmd-command-prompt.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-command-prompt.c,v retrieving revision 1.27 diff -u -p -r1.27 cmd-command-prompt.c --- cmd-command-prompt.c 10 Oct 2013 12:00:19 -0000 1.27 +++ cmd-command-prompt.c 24 Apr 2014 09:44:42 -0000 @@ -76,6 +76,9 @@ cmd_command_prompt_key_binding(struct cm self->args = args_create(1, "select-window -t ':%%'"); args_set(self->args, 'p', "index"); break; + case 'P': + self->args = args_create(1, "paste-buffer -b '%%'"); + break; default: self->args = args_create(0); break; Index: cmd-delete-buffer.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-delete-buffer.c,v retrieving revision 1.11 diff -u -p -r1.11 cmd-delete-buffer.c --- cmd-delete-buffer.c 24 Apr 2014 09:14:43 -0000 1.11 +++ cmd-delete-buffer.c 24 Apr 2014 09:44:42 -0000 @@ -41,23 +41,16 @@ enum cmd_retval cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - char *cause; - int buffer; + const char *bufname; if (!args_has(args, 'b')) { paste_free_top(); return (CMD_RETURN_NORMAL); } + bufname = args_get(args, 'b'); - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - - if (paste_free_index(buffer) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); + if (paste_free_name(bufname) != 0) { + cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } Index: cmd-list-buffers.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-list-buffers.c,v retrieving revision 1.20 diff -u -p -r1.20 cmd-list-buffers.c --- cmd-list-buffers.c 24 Apr 2014 09:14:43 -0000 1.20 +++ cmd-list-buffers.c 24 Apr 2014 09:44:43 -0000 @@ -44,17 +44,15 @@ cmd_list_buffers_exec(unused struct cmd struct args *args = self->args; struct paste_buffer *pb; struct format_tree *ft; - u_int idx; char *line; const char *template; if ((template = args_get(args, 'F')) == NULL) template = LIST_BUFFERS_TEMPLATE; - idx = 0; - while ((pb = paste_walk_stack(&idx)) != NULL) { + pb = NULL; + while ((pb = paste_walk(pb)) != NULL) { ft = format_create(); - format_add(ft, "line", "%u", idx - 1); format_paste_buffer(ft, pb, 0); line = format_expand(ft, template); Index: cmd-load-buffer.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-load-buffer.c,v retrieving revision 1.28 diff -u -p -r1.28 cmd-load-buffer.c --- cmd-load-buffer.c 24 Apr 2014 09:14:43 -0000 1.28 +++ cmd-load-buffer.c 24 Apr 2014 09:44:43 -0000 @@ -50,30 +50,20 @@ cmd_load_buffer_exec(struct cmd *self, s struct client *c = cmdq->client; struct session *s; FILE *f; - const char *path; + const char *path, *bufname; char *pdata, *new_pdata, *cause; size_t psize; u_int limit; - int ch, error, buffer, *buffer_ptr, cwd, fd; + int ch, error, cwd, fd; - if (!args_has(args, 'b')) - buffer = -1; - else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - } + bufname = NULL; + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); path = args->argv[0]; if (strcmp(path, "-") == 0) { - buffer_ptr = xmalloc(sizeof *buffer_ptr); - *buffer_ptr = buffer; - error = server_set_stdin_callback(c, cmd_load_buffer_callback, - buffer_ptr, &cause); + bufname, &cause); if (error != 0) { cmdq_error(cmdq, "%s: %s", path, cause); free(cause); @@ -118,13 +108,10 @@ cmd_load_buffer_exec(struct cmd *self, s fclose(f); limit = options_get_number(&global_options, "buffer-limit"); - if (buffer == -1) { - paste_add(pdata, psize, limit); - return (CMD_RETURN_NORMAL); - } - if (paste_replace(buffer, pdata, psize) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); + if (paste_set(pdata, psize, limit, bufname, &cause) != 0) { + cmdq_error(cmdq, "%s", cause); free(pdata); + free(cause); return (CMD_RETURN_ERROR); } @@ -140,10 +127,10 @@ error: void cmd_load_buffer_callback(struct client *c, int closed, void *data) { - int *buffer = data; - char *pdata; - size_t psize; - u_int limit; + char *pdata, *cause; + const char *bufname = data; + size_t psize; + u_int limit; if (!closed) return; @@ -154,25 +141,21 @@ cmd_load_buffer_callback(struct client * return; psize = EVBUFFER_LENGTH(c->stdin_data); - if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) { - free(data); + if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) goto out; - } + memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize); pdata[psize] = '\0'; evbuffer_drain(c->stdin_data, psize); limit = options_get_number(&global_options, "buffer-limit"); - if (*buffer == -1) - paste_add(pdata, psize, limit); - else if (paste_replace(*buffer, pdata, psize) != 0) { + if (paste_set(pdata, psize, limit, bufname, &cause) != 0) { /* No context so can't use server_client_msg_error. */ - evbuffer_add_printf(c->stderr_data, "no buffer %d\n", *buffer); + evbuffer_add_printf(c->stderr_data, "%s", cause); server_push_stderr(c); free(pdata); + free(cause); } - - free(data); out: cmdq_continue(c->cmdq); Index: cmd-paste-buffer.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-paste-buffer.c,v retrieving revision 1.24 diff -u -p -r1.24 cmd-paste-buffer.c --- cmd-paste-buffer.c 24 Apr 2014 09:14:43 -0000 1.24 +++ cmd-paste-buffer.c 24 Apr 2014 09:44:43 -0000 @@ -36,7 +36,7 @@ void cmd_paste_buffer_filter(struct wind const struct cmd_entry cmd_paste_buffer_entry = { "paste-buffer", "pasteb", "db:prs:t:", 0, 0, - "[-dpr] [-s separator] [-b buffer-index] " CMD_TARGET_PANE_USAGE, + "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, 0, NULL, cmd_paste_buffer_exec @@ -49,31 +49,22 @@ cmd_paste_buffer_exec(struct cmd *self, struct window_pane *wp; struct session *s; struct paste_buffer *pb; - const char *sepstr; - char *cause; - int buffer; + const char *sepstr, *bufname; int pflag; if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) return (CMD_RETURN_ERROR); - if (!args_has(args, 'b')) - buffer = -1; - else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - } + bufname = NULL; + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); - if (buffer == -1) + if (bufname == NULL) pb = paste_get_top(); else { - pb = paste_get_index(buffer); + pb = paste_get_name(bufname); if (pb == NULL) { - cmdq_error(cmdq, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } } @@ -92,10 +83,10 @@ cmd_paste_buffer_exec(struct cmd *self, /* Delete the buffer if -d. */ if (args_has(args, 'd')) { - if (buffer == -1) + if (bufname == NULL) paste_free_top(); else - paste_free_index(buffer); + paste_free_name(bufname); } return (CMD_RETURN_NORMAL); Index: cmd-save-buffer.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-save-buffer.c,v retrieving revision 1.24 diff -u -p -r1.24 cmd-save-buffer.c --- cmd-save-buffer.c 24 Apr 2014 09:14:43 -0000 1.24 +++ cmd-save-buffer.c 24 Apr 2014 09:44:43 -0000 @@ -59,10 +59,10 @@ cmd_save_buffer_exec(struct cmd *self, s struct client *c = cmdq->client; struct session *s; struct paste_buffer *pb; - const char *path; - char *cause, *start, *end, *msg; + const char *path, *bufname; + char *start, *end, *msg; size_t size, used, msglen; - int cwd, fd, buffer; + int cwd, fd; FILE *f; if (!args_has(args, 'b')) { @@ -71,16 +71,10 @@ cmd_save_buffer_exec(struct cmd *self, s return (CMD_RETURN_ERROR); } } else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - - pb = paste_get_index(buffer); + bufname = args_get(args, 'b'); + pb = paste_get_name(bufname); if (pb == NULL) { - cmdq_error(cmdq, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } } Index: cmd-set-buffer.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/cmd-set-buffer.c,v retrieving revision 1.17 diff -u -p -r1.17 cmd-set-buffer.c --- cmd-set-buffer.c 24 Apr 2014 09:14:43 -0000 1.17 +++ cmd-set-buffer.c 24 Apr 2014 09:44:43 -0000 @@ -31,8 +31,8 @@ enum cmd_retval cmd_set_buffer_exec(str const struct cmd_entry cmd_set_buffer_entry = { "set-buffer", "setb", - "ab:", 1, 1, - "[-a] " CMD_BUFFER_USAGE " data", + "ab:n:su", 0, 1, + "[-asu] [-n new_buffer_name] " CMD_BUFFER_USAGE " data", 0, NULL, cmd_set_buffer_exec @@ -45,36 +45,104 @@ cmd_set_buffer_exec(struct cmd *self, st struct paste_buffer *pb; u_int limit; char *pdata, *cause; + u_char sticky; + const char *bufname; size_t psize, newsize; - int buffer; + bufname = NULL; limit = options_get_number(&global_options, "buffer-limit"); - psize = 0; - pdata = NULL; - pb = NULL; - buffer = -1; + if (args_has(args, 'n')) { + if (args_has(args, 's') || args_has(args, 'u')) { + cmdq_error(cmdq, "specify only 1 of n, s, or u flags"); + return (CMD_RETURN_ERROR); + } - if ((newsize = strlen(args->argv[0])) == 0) - return (CMD_RETURN_NORMAL); + if (args->argc > 0) { + cmdq_error(cmdq, "don't provide data with n flag"); + return (CMD_RETURN_ERROR); + } - if (args_has(args, 'b')) { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); + + if (bufname == NULL) { + pb = paste_get_top(); + if (pb == NULL) { + cmdq_error(cmdq, "no buffer"); + return (CMD_RETURN_ERROR); + } + bufname = pb->name; + } + + if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) { + cmdq_error(cmdq, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } - pb = paste_get_index(buffer); + + return (CMD_RETURN_NORMAL); + } + + if (args_has(args, 's') && args_has(args, 'u')) { + cmdq_error(cmdq, "specify only 1 of s or u flags"); + return (CMD_RETURN_ERROR); + } + + if (args_has(args, 's') || args_has(args, 'u')) { + sticky = 0; + if (args_has(args, 's')) + sticky = 1; + + if (args->argc > 0) { + cmdq_error(cmdq, "don't provide data with s or u flags"); + return (CMD_RETURN_ERROR); + } + + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); + + if (bufname == NULL) + pb = paste_get_top(); + else + pb = paste_get_name(bufname); + if (pb == NULL) { - cmdq_error(cmdq, "no buffer %d", buffer); + if (bufname == NULL) + bufname = ""; + cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } + + if (sticky == 0) + paste_make_unsticky(pb->name, limit); + else + paste_make_sticky(pb->name); + + return (CMD_RETURN_NORMAL); + } + + if (args->argc != 1) { + cmdq_error(cmdq, "no data specified"); + return (CMD_RETURN_ERROR); + } + + psize = 0; + pdata = NULL; + + pb = NULL; + + if ((newsize = strlen(args->argv[0])) == 0) + return (CMD_RETURN_NORMAL); + + if (args_has(args, 'b')) { + bufname = args_get(args, 'b'); + pb = paste_get_name(bufname); } else if (args_has(args, 'a')) { pb = paste_get_top(); if (pb != NULL) - buffer = 0; + bufname = pb->name; } if (args_has(args, 'a') && pb != NULL) { @@ -87,10 +155,12 @@ cmd_set_buffer_exec(struct cmd *self, st memcpy(pdata + psize, args->argv[0], newsize); psize += newsize; - if (buffer == -1) - paste_add(pdata, psize, limit); - else - paste_replace(buffer, pdata, psize); + if (paste_set(pdata, psize, limit, bufname, &cause) != 0) { + cmdq_error(cmdq, "%s", cause); + free(pdata); + free(cause); + return (CMD_RETURN_ERROR); + } return (CMD_RETURN_NORMAL); } Index: format.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/format.c,v retrieving revision 1.44 diff -u -p -r1.44 format.c --- format.c 17 Apr 2014 15:37:55 -0000 1.44 +++ format.c 24 Apr 2014 09:44:43 -0000 @@ -608,6 +608,9 @@ format_paste_buffer(struct format_tree * char *s; format_add(ft, "buffer_size", "%zu", pb->size); + format_add(ft, "buffer_name", "%s", pb->name); + format_add(ft, "buffer_stickiness", "%c", + pb->sticky == 0 ? 'U' : 'S'); s = paste_make_sample(pb, utf8flag); format_add(ft, "buffer_sample", "%s", s); Index: key-bindings.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/key-bindings.c,v retrieving revision 1.40 diff -u -p -r1.40 key-bindings.c --- key-bindings.c 17 Apr 2014 07:55:43 -0000 1.40 +++ key-bindings.c 24 Apr 2014 09:44:43 -0000 @@ -130,6 +130,7 @@ key_bindings_init(void) { '?', 0, &cmd_list_keys_entry }, { 'D', 0, &cmd_choose_client_entry }, { 'L', 0, &cmd_switch_client_entry }, + { 'P', 0, &cmd_command_prompt_entry }, { '[', 0, &cmd_copy_mode_entry }, { '\'', 0, &cmd_command_prompt_entry }, { '\002', /* C-b */ 0, &cmd_send_prefix_entry }, Index: mode-key.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/mode-key.c,v retrieving revision 1.58 diff -u -p -r1.58 mode-key.c --- mode-key.c 31 Mar 2014 21:39:31 -0000 1.58 +++ mode-key.c 24 Apr 2014 09:44:43 -0000 @@ -51,6 +51,7 @@ const struct mode_key_cmdstr mode_key_cm { MODEKEYEDIT_DELETEWORD, "delete-word" }, { MODEKEYEDIT_ENDOFLINE, "end-of-line" }, { MODEKEYEDIT_ENTER, "enter" }, + { MODEKEYEDIT_ENTERAPPEND, "enter-append" }, { MODEKEYEDIT_HISTORYDOWN, "history-down" }, { MODEKEYEDIT_HISTORYUP, "history-up" }, { MODEKEYEDIT_NEXTSPACE, "next-space" }, @@ -141,6 +142,7 @@ const struct mode_key_cmdstr mode_key_cm { MODEKEYCOPY_SEARCHREVERSE, "search-reverse" }, { MODEKEYCOPY_SEARCHUP, "search-backward" }, { MODEKEYCOPY_SELECTLINE, "select-line" }, + { MODEKEYCOPY_STARTNAMEDBUFFER, "start-named-buffer" }, { MODEKEYCOPY_STARTNUMBERPREFIX, "start-number-prefix" }, { MODEKEYCOPY_STARTOFLINE, "start-of-line" }, { MODEKEYCOPY_STARTSELECTION, "begin-selection" }, @@ -160,6 +162,7 @@ const struct mode_key_entry mode_key_vi_ { '\033' /* Escape */, 0, MODEKEYEDIT_SWITCHMODE }, { '\n', 0, MODEKEYEDIT_ENTER }, { '\r', 0, MODEKEYEDIT_ENTER }, + { '\001' /* C-a */, 0, MODEKEYEDIT_ENTERAPPEND }, { KEYC_BSPACE, 0, MODEKEYEDIT_BACKSPACE }, { KEYC_DC, 0, MODEKEYEDIT_DELETE }, { KEYC_DOWN, 0, MODEKEYEDIT_HISTORYDOWN }, @@ -257,6 +260,7 @@ struct mode_key_tree mode_key_tree_vi_ch /* vi copy mode keys. */ const struct mode_key_entry mode_key_vi_copy[] = { { ' ', 0, MODEKEYCOPY_STARTSELECTION }, + { '"', 0, MODEKEYCOPY_STARTNAMEDBUFFER }, { '$', 0, MODEKEYCOPY_ENDOFLINE }, { ',', 0, MODEKEYCOPY_JUMPREVERSE }, { ';', 0, MODEKEYCOPY_JUMPAGAIN }, Index: paste.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/paste.c,v retrieving revision 1.18 diff -u -p -r1.18 paste.c --- paste.c 24 Apr 2014 09:14:43 -0000 1.18 +++ paste.c 24 Apr 2014 09:44:43 -0000 @@ -19,6 +19,7 @@ #include <sys/types.h> #include <sys/time.h> +#include <ctype.h> #include <stdlib.h> #include <string.h> #include <vis.h> @@ -30,124 +31,300 @@ * string! */ -ARRAY_DECL(, struct paste_buffer *) paste_buffers = ARRAY_INITIALIZER; +u_int paste_unsticky_idx; +u_int paste_num_unsticky; +RB_HEAD(paste_name_tree, paste_buffer) paste_by_name; +RB_HEAD(paste_time_tree, paste_buffer) paste_by_time; + +int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *); +RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); +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); -/* Return each item of the stack in turn. */ -struct paste_buffer * -paste_walk_stack(u_int *idx) +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); - (*idx)++; - return (pb); +int +paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b) +{ + return (a->crtime < b->crtime ? -1 : a->crtime > b->crtime); } -/* Get the top item on the stack. */ +/* Walk paste buffers by name. */ struct paste_buffer * -paste_get_top(void) +paste_walk(struct paste_buffer *pb) { - if (ARRAY_LENGTH(&paste_buffers) == 0) - return (NULL); - return (ARRAY_FIRST(&paste_buffers)); + if (pb == NULL) + return (RB_MIN(paste_name_tree, &paste_by_name)); + return (RB_NEXT(paste_name_tree, &paste_by_name, pb)); } -/* Get an item by its index. */ +/* Get the most recent unsticky buffer */ struct paste_buffer * -paste_get_index(u_int idx) +paste_get_top(void) { - if (idx >= ARRAY_LENGTH(&paste_buffers)) - return (NULL); - return (ARRAY_ITEM(&paste_buffers, idx)); + return (RB_MAX(paste_time_tree, &paste_by_time)); } -/* Free the top item on the stack. */ +/* Free the most recent unsticky buffer */ int paste_free_top(void) { struct paste_buffer *pb; - if (ARRAY_LENGTH(&paste_buffers) == 0) + pb = paste_get_top(); + if (pb == NULL) return (-1); - - pb = ARRAY_FIRST(&paste_buffers); - ARRAY_REMOVE(&paste_buffers, 0); - - free(pb->data); - free(pb); - - return (0); + return (paste_free_name(pb->name)); } -/* Free an item by index. */ +/* Free an item by name. */ int -paste_free_index(u_int idx) +paste_free_name(const char *name) { - struct paste_buffer *pb; + struct paste_buffer *pb, pbfind; + + if (name == NULL || *name == '\0') + return (-1); - if (idx >= ARRAY_LENGTH(&paste_buffers)) + pbfind.name = (char*)name; + pb = RB_FIND(paste_name_tree, &paste_by_name, &pbfind); + if (pb == NULL) return (-1); - pb = ARRAY_ITEM(&paste_buffers, idx); - ARRAY_REMOVE(&paste_buffers, idx); + RB_REMOVE(paste_name_tree, &paste_by_name, pb); + if (pb->sticky == 0) { + RB_REMOVE(paste_time_tree, &paste_by_time, pb); + paste_num_unsticky--; + } free(pb->data); + free(pb->name); free(pb); - return (0); } /* - * Add an item onto the top of the stack, freeing the bottom if at limit. Note + * Add an unsticky buffer, freeing the oldest unsticky item if at limit. Note * that the caller is responsible for allocating data. */ void paste_add(char *data, size_t size, u_int limit) { struct paste_buffer *pb; + u_int next_crtime; if (size == 0) return; - while (ARRAY_LENGTH(&paste_buffers) >= limit) { - pb = ARRAY_LAST(&paste_buffers); - free(pb->data); - free(pb); - ARRAY_TRUNC(&paste_buffers, 1); + while (paste_num_unsticky >= limit) { + pb = RB_MIN(paste_time_tree, &paste_by_time); + paste_free_name(pb->name); + } + + next_crtime = 0; + if (paste_num_unsticky > 0) { + pb = RB_MAX(paste_time_tree, &paste_by_time); + next_crtime = pb->crtime + 1; } pb = xmalloc(sizeof *pb); - ARRAY_INSERT(&paste_buffers, 0, pb); + pb->name = NULL; + do { + free(pb->name); + xasprintf(&pb->name, "buffer%04u", paste_unsticky_idx); + paste_unsticky_idx++; + } while (paste_get_name(pb->name) != NULL); pb->data = data; pb->size = size; + pb->sticky = 0; + pb->crtime = next_crtime; + + RB_INSERT(paste_name_tree, &paste_by_name, pb); + RB_INSERT(paste_time_tree, &paste_by_time, pb); + paste_num_unsticky++; } +/* Get an item by its name. */ +struct paste_buffer * +paste_get_name(const char *name) +{ + struct paste_buffer pbfind; + + if (name == NULL || *name == '\0') + return (NULL); + + pbfind.name = (char*)name; + return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); +} + +/* Rename a paste buffer. */ +int +paste_rename(const char *oldname, const char *newname, char **cause) +{ + const char *c; + struct paste_buffer *pb; + + *cause = NULL; + + if (oldname == NULL || *oldname == '\0') { + *cause = xstrdup("no buffer"); + return (-1); + } + + if (newname == NULL || *newname == '\0') { + *cause = xstrdup("new name is empty"); + return (-1); + } + + if ((pb = paste_get_name(oldname)) == NULL) { + xasprintf(cause, "no buffer %s", oldname); + return (-1); + } + + if (paste_get_name(newname) != NULL) { + *cause = xstrdup("buffer with new name already exists"); + return (-1); + } + + if (strlen(newname) > 16) { + *cause = xstrdup("buffer name too long"); + return (-1); + } + + for (c = newname; *c != '\0'; c++) { + if (!isprint(*c) || isspace(*c)) { + *cause = xstrdup("bad characters in buffer name"); + return (-1); + } + } + + RB_REMOVE(paste_name_tree, &paste_by_name, pb); + free(pb->name); + + pb->name = xstrdup(newname); + 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. */ int -paste_replace(u_int idx, char *data, size_t size) +paste_set(char *data, size_t size, u_int limit, const char *name, char **cause) { struct paste_buffer *pb; + const char *c; + + if (cause != NULL) + *cause = NULL; if (size == 0) { free(data); return (0); } - if (idx >= ARRAY_LENGTH(&paste_buffers)) + if (name == NULL) { + paste_add(data, size, limit); + return (0); + } + + + pb = paste_get_name(name); + + if (pb != NULL) { + free(pb->data); + pb->data = data; + pb->size = size; + return (0); + } + + + if (*name == '\0') { + if (cause != NULL) + *cause = xstrdup("empty buffer name"); return (-1); + } - pb = ARRAY_ITEM(&paste_buffers, idx); - free(pb->data); + if (strlen(name) > 16) { + if (cause != NULL) + *cause = xstrdup("buffer name too long"); + return (-1); + } + for (c = name; *c != '\0'; c++) { + if (!isprint(*c) || isspace(*c)) { + if (cause != NULL) + *cause = xstrdup("invalid buffer name"); + return (-1); + } + } + pb = xmalloc(sizeof *pb); pb->data = data; pb->size = size; + pb->name = xstrdup(name); + pb->sticky = 1; + + RB_INSERT(paste_name_tree, &paste_by_name, pb); return (0); +} + +/* Set paste buffer sticky. */ +void +paste_make_sticky(const char *name) +{ + struct paste_buffer *pb; + + if ((pb = paste_get_name(name)) == NULL) + return; + + if (pb->sticky == 1) + return; + + RB_REMOVE(paste_time_tree, &paste_by_time, pb); + paste_num_unsticky--; + + pb->sticky = 1; +} + +/* Set paste buffer unsticky. */ +void +paste_make_unsticky(const char *name, u_int limit) +{ + struct paste_buffer *pb, *tmppb; + u_int next_crtime; + + if ((pb = paste_get_name(name)) == NULL) + return; + + if (pb->sticky == 0) + return; + + while (paste_num_unsticky >= limit) { + tmppb = RB_MIN(paste_time_tree, &paste_by_time); + paste_free_name(tmppb->name); + } + + next_crtime = 0; + if (paste_num_unsticky > 0) { + tmppb = RB_MAX(paste_time_tree, &paste_by_time); + next_crtime = tmppb->crtime + 1; + } + + pb->sticky = 0; + pb->crtime = next_crtime; + + RB_INSERT(paste_time_tree, &paste_by_time, pb); + paste_num_unsticky++; } /* Convert start of buffer into a nice string. */ Index: tmux.h =================================================================== RCS file: /cvs/src/usr.bin/tmux/tmux.h,v retrieving revision 1.457 diff -u -p -r1.457 tmux.h --- tmux.h 24 Apr 2014 09:14:43 -0000 1.457 +++ tmux.h 24 Apr 2014 09:44:43 -0000 @@ -85,7 +85,7 @@ extern char **environ; /* Default template for choose-buffer. */ #define CHOOSE_BUFFER_TEMPLATE \ - "#{line}: #{buffer_size} bytes: #{buffer_sample}" + "#{buffer_name}: #{buffer_size} bytes: #{buffer_sample}" /* Default template for choose-client. */ #define CHOOSE_CLIENT_TEMPLATE \ @@ -118,7 +118,8 @@ extern char **environ; /* Default template for list-buffers. */ #define LIST_BUFFERS_TEMPLATE \ - "#{line}: #{buffer_size} bytes: \"#{buffer_sample}\"" + "#{buffer_name}: #{buffer_size} bytes: " \ + "\"#{buffer_sample}\"" /* Default template for list-clients. */ #define LIST_CLIENTS_TEMPLATE \ @@ -499,6 +500,7 @@ enum mode_key_cmd { MODEKEYEDIT_DELETEWORD, MODEKEYEDIT_ENDOFLINE, MODEKEYEDIT_ENTER, + MODEKEYEDIT_ENTERAPPEND, MODEKEYEDIT_HISTORYDOWN, MODEKEYEDIT_HISTORYUP, MODEKEYEDIT_NEXTSPACE, @@ -582,6 +584,7 @@ enum mode_key_cmd { MODEKEYCOPY_SEARCHREVERSE, MODEKEYCOPY_SEARCHUP, MODEKEYCOPY_SELECTLINE, + MODEKEYCOPY_STARTNAMEDBUFFER, MODEKEYCOPY_STARTNUMBERPREFIX, MODEKEYCOPY_STARTOFLINE, MODEKEYCOPY_STARTSELECTION, @@ -1035,7 +1038,13 @@ struct layout_cell { /* Paste buffer. */ struct paste_buffer { char *data; + char *name; + u_char sticky; size_t size; + u_int crtime; + + RB_ENTRY(paste_buffer) name_entry; + RB_ENTRY(paste_buffer) time_entry; }; /* Environment variable. */ @@ -1496,7 +1505,7 @@ RB_HEAD(format_tree, format_entry); #define CMD_SRCDST_WINDOW_USAGE "[-s src-window] [-t dst-window]" #define CMD_SRCDST_SESSION_USAGE "[-s src-session] [-t dst-session]" #define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]" -#define CMD_BUFFER_USAGE "[-b buffer-index]" +#define CMD_BUFFER_USAGE "[-b buffer-name]" /* tmux.c */ extern struct options global_options; @@ -1708,14 +1717,17 @@ void tty_keys_free(struct tty *); int tty_keys_next(struct tty *); /* paste.c */ -struct paste_buffer *paste_walk_stack(u_int *); +struct paste_buffer *paste_walk(struct paste_buffer *); struct paste_buffer *paste_get_top(void); -struct paste_buffer *paste_get_index(u_int); +struct paste_buffer *paste_get_name(const char *); int paste_free_top(void); -int paste_free_index(u_int); +int paste_free_name(const char *); void paste_add(char *, size_t, u_int); -int paste_replace(u_int, char *, size_t); +int paste_rename(const char *, const char *, char **); +int paste_set(char *, size_t, u_int, const char *, char **); char *paste_make_sample(struct paste_buffer *, int); +void paste_make_sticky(const char *); +void paste_make_unsticky(const char *, u_int); void paste_send_pane(struct paste_buffer *, struct window_pane *, const char *, int); Index: window-copy.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/window-copy.c,v retrieving revision 1.107 diff -u -p -r1.107 window-copy.c --- window-copy.c 24 Apr 2014 09:14:43 -0000 1.107 +++ window-copy.c 24 Apr 2014 09:44:43 -0000 @@ -54,11 +54,12 @@ void window_copy_update_cursor(struct wi void window_copy_start_selection(struct window_pane *); int window_copy_update_selection(struct window_pane *, int); void *window_copy_get_selection(struct window_pane *, size_t *); -void window_copy_copy_buffer(struct window_pane *, int, void *, size_t); +void window_copy_copy_buffer( + struct window_pane *, const char *, void *, size_t); void window_copy_copy_pipe( - struct window_pane *, struct session *, int, const char *); -void window_copy_copy_selection(struct window_pane *, int); -void window_copy_append_selection(struct window_pane *, int); + struct window_pane *, struct session *, const char *, const char *); +void window_copy_copy_selection(struct window_pane *, const char *); +void window_copy_append_selection(struct window_pane *, const char *); void window_copy_clear_selection(struct window_pane *); void window_copy_copy_line( struct window_pane *, char **, size_t *, u_int, u_int, u_int); @@ -94,6 +95,7 @@ const struct window_mode window_copy_mod enum window_copy_input_type { WINDOW_COPY_OFF, + WINDOW_COPY_NAMEDBUFFER, WINDOW_COPY_NUMERICPREFIX, WINDOW_COPY_SEARCHUP, WINDOW_COPY_SEARCHDOWN, @@ -417,7 +419,7 @@ window_copy_key(struct window_pane *wp, switch (cmd) { case MODEKEYCOPY_APPENDSELECTION: if (sess != NULL) { - window_copy_append_selection(wp, data->numprefix); + window_copy_append_selection(wp, NULL); window_pane_reset_mode(wp); return; } @@ -543,7 +545,7 @@ window_copy_key(struct window_pane *wp, if (sess != NULL && (cmd == MODEKEYCOPY_COPYLINE || cmd == MODEKEYCOPY_COPYENDOFLINE)) { - window_copy_copy_selection(wp, -1); + window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); return; } @@ -554,14 +556,14 @@ window_copy_key(struct window_pane *wp, break; case MODEKEYCOPY_COPYPIPE: if (sess != NULL) { - window_copy_copy_pipe(wp, sess, data->numprefix, arg); + window_copy_copy_pipe(wp, sess, NULL, arg); window_pane_reset_mode(wp); return; } break; case MODEKEYCOPY_COPYSELECTION: if (sess != NULL) { - window_copy_copy_selection(wp, data->numprefix); + window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); return; } @@ -676,6 +678,7 @@ window_copy_key(struct window_pane *wp, case WINDOW_COPY_JUMPBACK: case WINDOW_COPY_JUMPTOFORWARD: case WINDOW_COPY_JUMPTOBACK: + case WINDOW_COPY_NAMEDBUFFER: case WINDOW_COPY_NUMERICPREFIX: break; case WINDOW_COPY_SEARCHUP: @@ -711,6 +714,11 @@ window_copy_key(struct window_pane *wp, data->inputprompt = "Goto Line"; *data->inputstr = '\0'; goto input_on; + case MODEKEYCOPY_STARTNAMEDBUFFER: + data->inputtype = WINDOW_COPY_NAMEDBUFFER; + data->inputprompt = "Buffer"; + *data->inputstr = '\0'; + goto input_on; case MODEKEYCOPY_STARTNUMBERPREFIX: key &= KEYC_MASK_KEY; if (key >= '0' && key <= '9') { @@ -814,6 +822,11 @@ window_copy_key_input(struct window_pane data->searchtype = data->inputtype; data->searchstr = xstrdup(data->inputstr); break; + case WINDOW_COPY_NAMEDBUFFER: + window_copy_copy_selection(wp, data->inputstr); + *data->inputstr = '\0'; + window_pane_reset_mode(wp); + return (0); case WINDOW_COPY_GOTOLINE: window_copy_goto_line(wp, data->inputstr); *data->inputstr = '\0'; @@ -821,6 +834,17 @@ window_copy_key_input(struct window_pane } data->numprefix = -1; return (1); + case MODEKEYEDIT_ENTERAPPEND: + switch (data->inputtype) { + case WINDOW_COPY_NAMEDBUFFER: + window_copy_append_selection(wp, data->inputstr); + *data->inputstr = '\0'; + window_pane_reset_mode(wp); + return (0); + default: + break; + } + break; case MODEKEY_OTHER: if (key < 32 || key > 126) break; @@ -918,7 +942,7 @@ reset_mode: s->mode &= ~MODE_MOUSE_BUTTON; s->mode |= MODE_MOUSE_STANDARD; if (sess != NULL) { - window_copy_copy_selection(wp, -1); + window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); } } @@ -1452,10 +1476,11 @@ window_copy_get_selection(struct window_ } void -window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len) +window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf, + size_t len) { - u_int limit; - struct screen_write_ctx ctx; + u_int limit; + struct screen_write_ctx ctx; if (options_get_number(&global_options, "set-clipboard")) { screen_write_start(&ctx, wp, NULL); @@ -1463,16 +1488,16 @@ window_copy_copy_buffer(struct window_pa screen_write_stop(&ctx); } - if (idx == -1) { - limit = options_get_number(&global_options, "buffer-limit"); - paste_add(buf, len, limit); - } else if (paste_replace(idx, buf, len) != 0) + limit = options_get_number(&global_options, "buffer-limit"); + if (paste_set(buf, len, limit, bufname, NULL) != 0) free(buf); + + return; } void -window_copy_copy_pipe( - struct window_pane *wp, struct session *sess, int idx, const char *arg) +window_copy_copy_pipe(struct window_pane *wp, struct session *sess, + const char *bufname, const char *arg) { void *buf; size_t len; @@ -1486,11 +1511,11 @@ window_copy_copy_pipe( job = job_run(arg, sess, NULL, NULL, NULL); bufferevent_write(job->event, buf, len); - window_copy_copy_buffer(wp, idx, buf, len); + window_copy_copy_buffer(wp, bufname, buf, len); } void -window_copy_copy_selection(struct window_pane *wp, int idx) +window_copy_copy_selection(struct window_pane *wp, const char *bufname) { void* buf; size_t len; @@ -1499,17 +1524,17 @@ window_copy_copy_selection(struct window if (buf == NULL) return; - window_copy_copy_buffer(wp, idx, buf, len); + window_copy_copy_buffer(wp, bufname, buf, len); } void -window_copy_append_selection(struct window_pane *wp, int idx) +window_copy_append_selection(struct window_pane *wp, const char *bufname) { - char *buf; - struct paste_buffer *pb; - size_t len; - u_int limit; - struct screen_write_ctx ctx; + char *buf; + struct paste_buffer *pb; + size_t len; + u_int limit; + struct screen_write_ctx ctx; buf = window_copy_get_selection(wp, &len); if (buf == NULL) @@ -1521,24 +1546,20 @@ window_copy_append_selection(struct wind screen_write_stop(&ctx); } - if (idx == -1) - idx = 0; - - if (idx == 0 && paste_get_top() == NULL) { - limit = options_get_number(&global_options, "buffer-limit"); - paste_add(buf, len, limit); - return; - } - - pb = paste_get_index(idx); + limit = options_get_number(&global_options, "buffer-limit"); + if (bufname == NULL || *bufname == '\0') { + pb = paste_get_top(); + if (pb != NULL) + bufname = pb->name; + } else + pb = paste_get_name(bufname); if (pb != NULL) { buf = xrealloc(buf, 1, len + pb->size); memmove(buf + pb->size, buf, len); memcpy(buf, pb->data, pb->size); len += pb->size; } - - if (paste_replace(idx, buf, len) != 0) + if (paste_set(buf, len, limit, bufname, NULL) != 0) free(buf); } ------------------------------------------------------------------------------ Start Your Social Network Today - Download eXo Platform Build your Enterprise Intranet with eXo Platform Software Java Based Open Source Intranet - Social, Extensible, Cloud Ready Get Started Now And Turn Your Intranet Into A Collaboration Platform http://p.sf.net/sfu/ExoPlatform _______________________________________________ tmux-users mailing list tmux-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/tmux-users