I've attached a patch which implements named buffers.

Commands that take a buffer arg, like save-buffer and paste-buffer,
have been modified so that -b can take a number or a name.  So, you
can do "setb -b myBuf someData".

buffer-limit applies to both the paste stack and the named buffers.
So if buffer-limit is set to 20, you can have 20 items in the paste
stack, and 20 named buffer items.

Named buffers have to be explicitly deleted.  If you try to add a new
named buffer and you're at the buffer limit, it will fail to add a new
named buffer.  This is unlike the paste stack which removes the oldest
item.

When in copy mode, pressing the double-quote character allows you to
enter a buffer name.  Once you've made a selection, typing "myBuf and
then hitting Enter will save your selection to the buffer named myBuf.
 You can hit Ctrl-a instead of Enter to append.  Both of these key
bindings are only set by default in vi mode.

list-buffer and choose-buffer display the items in the paste stack
first, and then the named buffers, which are sorted alphabetically.

I added the ability to do prefix-P to more quickly enter a buffer name
(or number) for pasting.
diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c
index e157e3c..d801daa 100644
--- a/cmd-capture-pane.c
+++ b/cmd-capture-pane.c
@@ -164,7 +164,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq)
 	struct args		*args = self->args;
 	struct client		*c;
 	struct window_pane	*wp;
-	char			*buf, *cause;
+	char			*buf, *cause, *bufname;
 	int			 buffer;
 	u_int			 limit;
 	size_t			 len;
@@ -200,13 +200,17 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq)
 
 		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);
-		}
-
-		if (paste_replace(&global_buffers, buffer, buf, len) != 0) {
+			bufname = xstrdup(args_get(args, 'b'));
+			if (paste_set_named_buffer(&named_buffers,
+			    buf, len, limit, bufname, &cause) != 0) {
+				cmdq_error(cmdq, "%s", cause);
+				free(buf);
+				free(cause);
+				free(bufname);
+				return (CMD_RETURN_ERROR);
+			}
+		} else if (paste_replace(&global_buffers, buffer, buf, len) != 0) {
 			cmdq_error(cmdq, "no buffer %d", buffer);
 			free(buf);
 			return (CMD_RETURN_ERROR);
diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c
index d79f6fd..a96fae1 100644
--- a/cmd-choose-buffer.c
+++ b/cmd-choose-buffer.c
@@ -87,6 +87,22 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 
 		window_choose_add(wl->window->active, cdata);
 	}
+
+	idx = 0;
+	while ((pb = paste_walk_stack(&named_buffers, &idx)) != NULL) {
+		cdata = window_choose_data_create(TREE_OTHER, c, c->session);
+		cdata->idx = idx - 1;
+
+		cdata->ft_template = xstrdup(template);
+		format_add(cdata->ft, "line", "%s", pb->name);
+		format_paste_buffer(cdata->ft, pb);
+
+		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);
+	}
 	free(action);
 
 	window_choose_ready(wl->window->active, 0, NULL);
diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c
index 759d578..0acecc5 100644
--- a/cmd-command-prompt.c
+++ b/cmd-command-prompt.c
@@ -76,6 +76,9 @@ cmd_command_prompt_key_binding(struct cmd *self, int key)
 		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;
diff --git a/cmd-delete-buffer.c b/cmd-delete-buffer.c
index b8f55db..d4f97c1 100644
--- a/cmd-delete-buffer.c
+++ b/cmd-delete-buffer.c
@@ -43,6 +43,7 @@ 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(&global_buffers);
@@ -51,9 +52,14 @@ cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 
 	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 = args_get(args, 'b');
+		if (paste_free_name(&named_buffers, bufname) != 0) {
+			cmdq_error(cmdq, "no buffer %s", bufname);
+			return (CMD_RETURN_ERROR);
+		}
+
+		return (CMD_RETURN_NORMAL);
 	}
 
 	if (paste_free_index(&global_buffers, buffer) != 0) {
diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c
index 02a4183..98d013f 100644
--- a/cmd-list-buffers.c
+++ b/cmd-list-buffers.c
@@ -64,5 +64,18 @@ cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq)
 		format_free(ft);
 	}
 
+	idx = 0;
+	while ((pb = paste_walk_stack(&named_buffers, &idx)) != NULL) {
+		ft = format_create();
+		format_add(ft, "line", "%s", pb->name);
+		format_paste_buffer(ft, pb);
+
+		line = format_expand(ft, template);
+		cmdq_print(cmdq, "%s", line);
+		free(line);
+
+		format_free(ft);
+	}
+
 	return (CMD_RETURN_NORMAL);
 }
diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c
index 2cb01b9..9093fa1 100644
--- a/cmd-load-buffer.c
+++ b/cmd-load-buffer.c
@@ -51,29 +51,29 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 	struct session  *s;
 	FILE		*f;
 	const char	*path;
-	char		*pdata, *new_pdata, *cause;
+	char		*pdata, *new_pdata, *cause, *bufname;
 	size_t		 psize;
 	u_int		 limit;
-	int		 ch, error, buffer, *buffer_ptr, cwd, fd;
+	int		 ch, error, buffer, cwd, fd;
 
+	bufname = NULL;
 	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 = xstrdup(args_get(args, 'b'));
 		}
 	}
 
 	path = args->argv[0];
 	if (strcmp(path, "-") == 0) {
-		buffer_ptr = xmalloc(sizeof *buffer_ptr);
-		*buffer_ptr = buffer;
+		if (bufname == NULL && args_has(args, 'b'))
+			bufname = xstrdup(args_get(args, 'b'));
 
 		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,6 +118,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 	fclose(f);
 
 	limit = options_get_number(&global_options, "buffer-limit");
+
+	if (bufname != NULL) {
+		if(paste_set_named_buffer(&named_buffers,
+		    pdata, psize, limit, bufname, &cause) != 0) {
+			cmdq_error(cmdq, "%s", cause);
+			free(pdata);
+			free(cause);
+			free(bufname);
+			return (CMD_RETURN_ERROR);
+		}
+		return (CMD_RETURN_NORMAL);
+	}
+
 	if (buffer == -1) {
 		paste_add(&global_buffers, pdata, psize, limit);
 		return (CMD_RETURN_NORMAL);
@@ -140,8 +153,8 @@ error:
 void
 cmd_load_buffer_callback(struct client *c, int closed, void *data)
 {
-	int	*buffer = data;
-	char	*pdata;
+	int	buffer;
+	char	*pdata, *cause, *bufname, *bufarg = data;
 	size_t	 psize;
 	u_int	 limit;
 
@@ -163,11 +176,28 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data)
 	evbuffer_drain(c->stdin_data, psize);
 
 	limit = options_get_number(&global_options, "buffer-limit");
-	if (*buffer == -1)
+	bufname = NULL;
+	if (bufarg == NULL)
+		buffer = -1;
+	else {
+		buffer = strtonum(bufarg, 0, INT_MAX, &cause);
+		if (cause != NULL)
+			bufname = xstrdup(bufarg);
+	}
+
+	if (bufname != NULL) {
+		if (paste_set_named_buffer(&named_buffers,
+		    pdata, psize, limit, bufname, &cause) != 0) {
+			evbuffer_add_printf(c->stderr_data, "%s", cause);
+			server_push_stderr(c);
+			free(pdata);
+			free(cause);
+		}
+	} else if (buffer == -1)
 		paste_add(&global_buffers, pdata, psize, limit);
-	else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) {
+	else if (paste_replace(&global_buffers, buffer, pdata, psize) != 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, "no buffer %d\n", buffer);
 		server_push_stderr(c);
 		free(pdata);
 	}
diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c
index 5305b7e..14434d1 100644
--- a/cmd-paste-buffer.c
+++ b/cmd-paste-buffer.c
@@ -48,7 +48,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 	struct window_pane	*wp;
 	struct session		*s;
 	struct paste_buffer	*pb;
-	const char		*sepstr;
+	const char		*sepstr, *bufname;
 	char			*cause;
 	int			 buffer;
 	int			 pflag;
@@ -56,18 +56,24 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 	if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL)
 		return (CMD_RETURN_ERROR);
 
+	bufname = NULL;
 	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 = args_get(args, 'b');
 		}
 	}
 
-	if (buffer == -1)
+	if (bufname != NULL) {
+		pb = paste_get_name(&named_buffers, bufname);
+		if (pb == NULL) {
+			cmdq_error(cmdq, "no buffer %s", bufname);
+			return (CMD_RETURN_ERROR);
+		}
+	} else if (buffer == -1)
 		pb = paste_get_top(&global_buffers);
 	else {
 		pb = paste_get_index(&global_buffers, buffer);
@@ -91,7 +97,9 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 
 	/* Delete the buffer if -d. */
 	if (args_has(args, 'd')) {
-		if (buffer == -1)
+		if (bufname != NULL)
+			paste_free_name(&named_buffers, bufname);
+		else if (buffer == -1)
 			paste_free_top(&global_buffers);
 		else
 			paste_free_index(&global_buffers, buffer);
diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c
index 3788fc2..9933f60 100644
--- a/cmd-save-buffer.c
+++ b/cmd-save-buffer.c
@@ -58,7 +58,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 	struct client		*c = cmdq->client;
 	struct session          *s;
 	struct paste_buffer	*pb;
-	const char		*path;
+	const char		*path, *bufname;
 	char			*cause, *start, *end, *msg;
 	size_t			 size, used, msglen;
 	int			 cwd, fd, buffer;
@@ -72,15 +72,19 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 	} 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(&global_buffers, buffer);
-		if (pb == NULL) {
-			cmdq_error(cmdq, "no buffer %d", buffer);
-			return (CMD_RETURN_ERROR);
+			bufname = args_get(args, 'b');
+			pb = paste_get_name(&named_buffers, bufname);
+			if (pb == NULL) {
+				cmdq_error(cmdq, "no buffer %s", bufname);
+				return (CMD_RETURN_ERROR);
+			}
+		} else {
+			pb = paste_get_index(&global_buffers, buffer);
+			if (pb == NULL) {
+				cmdq_error(cmdq, "no buffer %d", buffer);
+				return (CMD_RETURN_ERROR);
+			}
 		}
 	}
 
diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c
index e7f9b95..63fc076 100644
--- a/cmd-set-buffer.c
+++ b/cmd-set-buffer.c
@@ -44,7 +44,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 	struct args		*args = self->args;
 	struct paste_buffer	*pb;
 	u_int			 limit;
-	char			*pdata, *cause;
+	char			*pdata, *cause, *bufname;
 	size_t			 psize, newsize;
 	int			 buffer;
 
@@ -55,6 +55,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 
 	pb = NULL;
 	buffer = -1;
+	bufname = NULL;
 
 	if ((newsize = strlen(args->argv[0])) == 0)
 		return (CMD_RETURN_NORMAL);
@@ -62,14 +63,15 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 	if (args_has(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);
-		}
-		pb = paste_get_index(&global_buffers, buffer);
-		if (pb == NULL) {
-			cmdq_error(cmdq, "no buffer %d", buffer);
-			return (CMD_RETURN_ERROR);
+			bufname = xstrdup(args_get(args, 'b'));
+			pb = paste_get_name(&named_buffers, bufname);
+		} else {
+			pb = paste_get_index(&global_buffers, buffer);
+			if (pb == NULL) {
+				cmdq_error(cmdq, "no buffer %d", buffer);
+				return (CMD_RETURN_ERROR);
+			}
 		}
 	} else if (args_has(args, 'a')) {
 		pb = paste_get_top(&global_buffers);
@@ -87,7 +89,16 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 	memcpy(pdata + psize, args->argv[0], newsize);
 	psize += newsize;
 
-	if (buffer == -1)
+	if (bufname != NULL) {
+		if (paste_set_named_buffer(&named_buffers,
+		    pdata, psize, limit, bufname, &cause) != 0) {
+			cmdq_error(cmdq, "%s", cause);
+			free(pdata);
+			free(cause);
+			free(bufname);
+			return (CMD_RETURN_ERROR);
+		}
+	} else if (buffer == -1)
 		paste_add(&global_buffers, pdata, psize, limit);
 	else
 		paste_replace(&global_buffers, buffer, pdata, psize);
diff --git a/key-bindings.c b/key-bindings.c
index 86048ea..93fbaa8 100644
--- a/key-bindings.c
+++ b/key-bindings.c
@@ -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 },
diff --git a/mode-key.c b/mode-key.c
index 57be2d8..b9ad8e4 100644
--- a/mode-key.c
+++ b/mode-key.c
@@ -51,6 +51,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_edit[] = {
 	{ 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_cmdstr_copy[] = {
 	{ 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_edit[] = {
 	{ '\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_choice;
 /* 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 },
diff --git a/paste.c b/paste.c
index 28f1230..f294bad 100644
--- a/paste.c
+++ b/paste.c
@@ -21,6 +21,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <ctype.h>
 
 #include "tmux.h"
 
@@ -29,6 +30,8 @@
  * string!
  */
 
+int compare_named_buffer_names(const void *, const void *);
+
 /* Return each item of the stack in turn. */
 struct paste_buffer *
 paste_walk_stack(struct paste_stack *ps, u_int *idx)
@@ -89,11 +92,31 @@ paste_free_index(struct paste_stack *ps, u_int idx)
 	ARRAY_REMOVE(ps, idx);
 
 	free(pb->data);
+	free(pb->name);
 	free(pb);
 
 	return (0);
 }
 
+/* Free an item by name. */
+int
+paste_free_name(struct paste_stack *ps, const char *name)
+{
+	struct paste_buffer	*pb;
+	u_int			 idx;
+
+	if (name == NULL || *name == '\0')
+		return (-1);
+
+	idx = 0;
+	while ((pb = paste_walk_stack(ps, &idx)) != NULL)
+		if (pb->name != NULL && strcmp(name, pb->name) == 0)
+			return (paste_free_index(ps, idx - 1));
+
+
+	return (-1);
+}
+
 /*
  * Add an item onto the top of the stack, freeing the bottom if at limit. Note
  * that the caller is responsible for allocating data.
@@ -109,6 +132,7 @@ paste_add(struct paste_stack *ps, char *data, size_t size, u_int limit)
 	while (ARRAY_LENGTH(ps) >= limit) {
 		pb = ARRAY_LAST(ps);
 		free(pb->data);
+		free(pb->name);
 		free(pb);
 		ARRAY_TRUNC(ps, 1);
 	}
@@ -118,6 +142,7 @@ paste_add(struct paste_stack *ps, char *data, size_t size, u_int limit)
 
 	pb->data = data;
 	pb->size = size;
+	pb->name = NULL;
 }
 
 
@@ -194,3 +219,105 @@ paste_send_pane(struct paste_buffer *pb, struct window_pane *wp,
 	if (bracket)
 		bufferevent_write(wp->event, "\033[201~", 6);
 }
+
+int
+compare_named_buffer_names(const void *a, const void *b)
+{
+	struct paste_buffer	*x, *y;
+
+	x = *(struct paste_buffer **)a;
+	y = *(struct paste_buffer **)b;
+
+	return (strcmp(x->name, y->name));
+}
+
+/* Get an item by its name. */
+struct paste_buffer *
+paste_get_name(struct paste_stack *ps, const char *name)
+{
+	struct paste_buffer	*pb;
+	u_int			 idx = 0;
+
+	if (name == NULL || *name == '\0')
+		return (NULL);
+
+	while ((pb = paste_walk_stack(ps, &idx)) != NULL)
+		if (pb->name != NULL && strcmp(name, pb->name) == 0)
+			break;
+
+	return (pb);
+}
+
+int
+paste_set_named_buffer(struct paste_stack *ps,  char *data, size_t size,
+    u_int limit, char *name, char **cause)
+{
+	struct paste_buffer	*pb, **psdata;
+	char			*c, *errstr;
+
+	if (cause != NULL)
+		*cause = NULL;
+
+	if (name == NULL || *name == '\0') {
+		if (cause != NULL)
+			*cause = xstrdup("empty buffer name");
+		return (-1);
+	}
+
+
+
+	if (size == 0) {
+		free(data);
+		free(name);
+		return (0);
+	}
+
+	pb = paste_get_name(ps, name);
+
+	if (pb != NULL) {
+		free(name);
+		free(pb->data);
+		pb->data = data;
+		pb->size = size;
+		return (0);
+	}
+
+	if (ARRAY_LENGTH(ps) >= limit) {
+		if (cause != NULL)
+			*cause = xstrdup("buffer-limit reached");
+		return (-1);
+	}
+
+	if (strlen(name) > 16) {
+		if (cause != NULL)
+			*cause = xstrdup("buffer name too long");
+		return (-1);
+	}
+
+	strtonum(name, 0, INT_MAX, &errstr);
+	if (errstr == NULL) {
+		if (cause != NULL)
+			*cause = xstrdup("buffer name can't be a number");
+		return (-1);
+	}
+
+	for (c = name; *c != '\0'; c++) {
+		if (!isprint(*c) || isspace(*c)) {
+			if (cause != NULL)
+				*cause = xstrdup("bad characters in buffer name");
+			return (-1);
+		}
+	}
+
+	pb = xmalloc(sizeof *pb);
+	ARRAY_INSERT(ps, 0, pb);
+
+	pb->data = data;
+	pb->size = size;
+	pb->name = name;
+
+	psdata = ARRAY_DATA(ps);
+	qsort(psdata, ARRAY_LENGTH(ps), sizeof pb, &compare_named_buffer_names);
+	return (0);
+}
+
diff --git a/server.c b/server.c
index f372de4..0bb11b4 100644
--- a/server.c
+++ b/server.c
@@ -51,6 +51,7 @@ struct event	 server_ev_accept;
 struct event	 server_ev_second;
 
 struct paste_stack global_buffers;
+struct paste_stack named_buffers;
 
 int		 server_create_socket(void);
 void		 server_loop(void);
@@ -147,6 +148,7 @@ server_start(int lockfd, char *lockfile)
 	RB_INIT(&dead_sessions);
 	TAILQ_INIT(&session_groups);
 	ARRAY_INIT(&global_buffers);
+	ARRAY_INIT(&named_buffers);
 	mode_key_init_trees();
 	key_bindings_init();
 	utf8_build();
diff --git a/tmux.h b/tmux.h
index 5aac390..e3980c5 100644
--- a/tmux.h
+++ b/tmux.h
@@ -496,6 +496,7 @@ enum mode_key_cmd {
 	MODEKEYEDIT_DELETEWORD,
 	MODEKEYEDIT_ENDOFLINE,
 	MODEKEYEDIT_ENTER,
+	MODEKEYEDIT_ENTERAPPEND,
 	MODEKEYEDIT_HISTORYDOWN,
 	MODEKEYEDIT_HISTORYUP,
 	MODEKEYEDIT_NEXTSPACE,
@@ -579,6 +580,7 @@ enum mode_key_cmd {
 	MODEKEYCOPY_SEARCHREVERSE,
 	MODEKEYCOPY_SEARCHUP,
 	MODEKEYCOPY_SELECTLINE,
+	MODEKEYCOPY_STARTNAMEDBUFFER,
 	MODEKEYCOPY_STARTNUMBERPREFIX,
 	MODEKEYCOPY_STARTOFLINE,
 	MODEKEYCOPY_STARTSELECTION,
@@ -1031,6 +1033,7 @@ struct layout_cell {
 /* Paste buffer. */
 struct paste_buffer {
 	char		*data;
+	char		*name;
 	size_t		 size;
 };
 ARRAY_DECL(paste_stack, struct paste_buffer *);
@@ -1707,9 +1710,13 @@ int	tty_keys_next(struct tty *);
 struct paste_buffer *paste_walk_stack(struct paste_stack *, u_int *);
 struct paste_buffer *paste_get_top(struct paste_stack *);
 struct paste_buffer *paste_get_index(struct paste_stack *, u_int);
+struct paste_buffer *paste_get_name(struct paste_stack *, const char *);
 int		 paste_free_top(struct paste_stack *);
 int		 paste_free_index(struct paste_stack *, u_int);
+int		 paste_free_name(struct paste_stack *, const char *);
 void		 paste_add(struct paste_stack *, char *, size_t, u_int);
+int		 paste_set_named_buffer(struct paste_stack *, char *, size_t,
+		     u_int, char *, char **);
 int		 paste_replace(struct paste_stack *, u_int, char *, size_t);
 char		*paste_print(struct paste_buffer *, size_t);
 void		 paste_send_pane(struct paste_buffer *, struct window_pane *,
@@ -1884,6 +1891,7 @@ const char *key_string_lookup_key(int);
 extern struct clients clients;
 extern struct clients dead_clients;
 extern struct paste_stack global_buffers;
+extern struct paste_stack named_buffers;
 int	 server_start(int, char *);
 void	 server_update_socket(void);
 void	 server_add_accept(int);
diff --git a/window-copy.c b/window-copy.c
index e3164f6..f7dac03 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -94,6 +94,7 @@ const struct window_mode window_copy_mode = {
 
 enum window_copy_input_type {
 	WINDOW_COPY_OFF,
+	WINDOW_COPY_NAMEDBUFFER,
 	WINDOW_COPY_NUMERICPREFIX,
 	WINDOW_COPY_SEARCHUP,
 	WINDOW_COPY_SEARCHDOWN,
@@ -676,6 +677,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key)
 		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 +713,11 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key)
 		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 +821,11 @@ window_copy_key_input(struct window_pane *wp, int key)
 			data->searchtype = data->inputtype;
 			data->searchstr = xstrdup(data->inputstr);
 			break;
+		case WINDOW_COPY_NAMEDBUFFER:
+			window_copy_copy_selection(wp, -1);
+			*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 +833,17 @@ window_copy_key_input(struct window_pane *wp, int key)
 		}
 		data->numprefix = -1;
 		return (1);
+	case MODEKEYEDIT_ENTERAPPEND:
+		switch (data->inputtype) {
+		case WINDOW_COPY_NAMEDBUFFER:
+			window_copy_append_selection(wp, -1);
+			*data->inputstr = '\0';
+			window_pane_reset_mode(wp);
+			return (0);
+		default:
+			break;
+		}
+		break;
 	case MODEKEY_OTHER:
 		if (key < 32 || key > 126)
 			break;
@@ -1451,8 +1474,10 @@ window_copy_get_selection(struct window_pane *wp, size_t *len)
 void
 window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len)
 {
-	u_int			limit;
-	struct screen_write_ctx	ctx;
+	u_int				 limit;
+	char				*bufname;
+	struct screen_write_ctx		 ctx;
+	struct window_copy_mode_data	*data = wp->modedata;
 
 	if (options_get_number(&global_options, "set-clipboard")) {
 		screen_write_start(&ctx, wp, NULL);
@@ -1460,10 +1485,21 @@ window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len)
 		screen_write_stop(&ctx);
 	}
 
-	if (idx == -1) {
-		limit = options_get_number(&global_options, "buffer-limit");
+	limit = options_get_number(&global_options, "buffer-limit");
+
+	if (data->inputstr != NULL && *data->inputstr != '\0') {
+		bufname = xstrdup(data->inputstr);
+		if (paste_set_named_buffer(&named_buffers,
+		    buf, len, limit, bufname, NULL) != 0) {
+			free(buf);
+			free(bufname);
+		}
+		return;
+	}
+
+	if (idx == -1)
 		paste_add(&global_buffers, buf, len, limit);
-	} else if (paste_replace(&global_buffers, idx, buf, len) != 0)
+	else if (paste_replace(&global_buffers, idx, buf, len) != 0)
 		free(buf);
 }
 
@@ -1502,11 +1538,12 @@ window_copy_copy_selection(struct window_pane *wp, int idx)
 void
 window_copy_append_selection(struct window_pane *wp, int idx)
 {
-	char			*buf;
-	struct paste_buffer	*pb;
-	size_t			 len;
-	u_int			 limit;
-	struct screen_write_ctx	 ctx;
+	char				*buf, *bufname;
+	struct paste_buffer		*pb;
+	size_t				 len;
+	u_int				 limit;
+	struct screen_write_ctx		 ctx;
+	struct window_copy_mode_data	*data = wp->modedata;
 
 	buf = window_copy_get_selection(wp, &len);
 	if (buf == NULL)
@@ -1521,13 +1558,21 @@ window_copy_append_selection(struct window_pane *wp, int idx)
 	if (idx == -1)
 		idx = 0;
 
-	if (idx == 0 && paste_get_top(&global_buffers) == NULL) {
-		limit = options_get_number(&global_options, "buffer-limit");
-		paste_add(&global_buffers, buf, len, limit);
-		return;
-	}
+	limit = options_get_number(&global_options, "buffer-limit");
+
+	bufname = NULL;
+	if (data->inputstr != NULL && *data->inputstr != '\0')
+		bufname = xstrdup(data->inputstr);
+
+	if (bufname == NULL) {
+		if (idx == 0 && paste_get_top(&global_buffers) == NULL) {
+			paste_add(&global_buffers, buf, len, limit);
+			return;
+		} else
+			pb = paste_get_index(&global_buffers, idx);
+	} else
+		pb = paste_get_name(&named_buffers, bufname);
 
-	pb = paste_get_index(&global_buffers, idx);
 	if (pb != NULL) {
 		buf = xrealloc(buf, 1, len + pb->size);
 		memmove(buf + pb->size, buf, len);
@@ -1535,8 +1580,17 @@ window_copy_append_selection(struct window_pane *wp, int idx)
 		len += pb->size;
 	}
 
-	if (paste_replace(&global_buffers, idx, buf, len) != 0)
-		free(buf);
+	if (bufname == NULL) {
+		if (paste_replace(&global_buffers, idx, buf, len) != 0)
+			free(buf);
+	} else {
+		if (paste_set_named_buffer(&named_buffers,
+		    buf, len, limit, bufname, NULL) != 0) {
+			free(buf);
+			free(bufname);
+		}
+	}
+
 }
 
 void
------------------------------------------------------------------------------
_______________________________________________
tmux-users mailing list
tmux-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tmux-users

Reply via email to