mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +00:00 
			
		
		
		
	Add support for named buffers. If you don't name a buffer, things work
much as before - buffers are automatically named "buffer0000",
"buffer0001" and so on and ordered as a stack. Buffers can be named
explicitly when creating ("loadb -b foo" etc) or renamed ("setb -b
buffer0000 -n foo"). If buffers are named explicitly, they are not
deleted when buffer-limit is reached. Diff from J Raynor.
			
			
This commit is contained in:
		| @@ -38,7 +38,7 @@ char		*cmd_capture_pane_history(struct args *, struct cmd_q *, | |||||||
| const struct cmd_entry cmd_capture_pane_entry = { | const struct cmd_entry cmd_capture_pane_entry = { | ||||||
| 	"capture-pane", "capturep", | 	"capture-pane", "capturep", | ||||||
| 	"ab:CeE:JpPqS:t:", 0, 0, | 	"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, | 	CMD_TARGET_PANE_USAGE, | ||||||
| 	0, | 	0, | ||||||
| 	NULL, | 	NULL, | ||||||
| @@ -165,8 +165,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) | |||||||
| 	struct client		*c; | 	struct client		*c; | ||||||
| 	struct window_pane	*wp; | 	struct window_pane	*wp; | ||||||
| 	char			*buf, *cause; | 	char			*buf, *cause; | ||||||
| 	int			 buffer; | 	const char		*bufname; | ||||||
| 	u_int			 limit; |  | ||||||
| 	size_t			 len; | 	size_t			 len; | ||||||
|  |  | ||||||
| 	if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) | 	if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) | ||||||
| @@ -192,25 +191,17 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) | |||||||
| 		    evbuffer_add(c->stdout_data, "\n", 1); | 		    evbuffer_add(c->stdout_data, "\n", 1); | ||||||
| 		server_push_stdout(c); | 		server_push_stdout(c); | ||||||
| 	} else { | 	} 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); | 		bufname = NULL; | ||||||
| 		if (cause != NULL) { | 		if (args_has(args, 'b')) | ||||||
| 			cmdq_error(cmdq, "buffer %s", cause); | 			bufname = args_get(args, 'b'); | ||||||
|  |  | ||||||
|  | 		if (paste_set(buf, len, bufname, &cause) != 0) { | ||||||
|  | 			cmdq_error(cmdq, "%s", cause); | ||||||
| 			free(buf); | 			free(buf); | ||||||
| 			free(cause); | 			free(cause); | ||||||
| 			return (CMD_RETURN_ERROR); | 			return (CMD_RETURN_ERROR); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (paste_replace(buffer, buf, len) != 0) { |  | ||||||
| 			cmdq_error(cmdq, "no buffer %d", buffer); |  | ||||||
| 			free(buf); |  | ||||||
| 			return (CMD_RETURN_ERROR); |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return (CMD_RETURN_NORMAL); | 	return (CMD_RETURN_NORMAL); | ||||||
|   | |||||||
| @@ -75,19 +75,20 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) | |||||||
| 		action = xstrdup("paste-buffer -b '%%'"); | 		action = xstrdup("paste-buffer -b '%%'"); | ||||||
|  |  | ||||||
| 	idx = 0; | 	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 = window_choose_data_create(TREE_OTHER, c, c->session); | ||||||
| 		cdata->idx = idx - 1; | 		cdata->idx = idx; | ||||||
|  |  | ||||||
| 		cdata->ft_template = xstrdup(template); | 		cdata->ft_template = xstrdup(template); | ||||||
| 		format_add(cdata->ft, "line", "%u", idx - 1); |  | ||||||
| 		format_paste_buffer(cdata->ft, pb, utf8flag); | 		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); | 		cdata->command = cmd_template_replace(action, action_data, 1); | ||||||
| 		free(action_data); | 		free(action_data); | ||||||
|  |  | ||||||
| 		window_choose_add(wl->window->active, cdata); | 		window_choose_add(wl->window->active, cdata); | ||||||
|  | 		idx++; | ||||||
| 	} | 	} | ||||||
| 	free(action); | 	free(action); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -41,23 +41,16 @@ enum cmd_retval | |||||||
| cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq) | cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq) | ||||||
| { | { | ||||||
| 	struct args	*args = self->args; | 	struct args	*args = self->args; | ||||||
| 	char		*cause; | 	const char 	*bufname; | ||||||
| 	int		 buffer; |  | ||||||
|  |  | ||||||
| 	if (!args_has(args, 'b')) { | 	if (!args_has(args, 'b')) { | ||||||
| 		paste_free_top(); | 		paste_free_top(); | ||||||
| 		return (CMD_RETURN_NORMAL); | 		return (CMD_RETURN_NORMAL); | ||||||
| 	} | 	} | ||||||
|  | 	bufname = args_get(args, 'b'); | ||||||
|  |  | ||||||
| 	buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); | 	if (paste_free_name(bufname) != 0) { | ||||||
| 	if (cause != NULL) { | 		cmdq_error(cmdq, "no buffer %s", bufname); | ||||||
| 		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); |  | ||||||
| 		return (CMD_RETURN_ERROR); | 		return (CMD_RETURN_ERROR); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -44,17 +44,15 @@ cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) | |||||||
| 	struct args		*args = self->args; | 	struct args		*args = self->args; | ||||||
| 	struct paste_buffer	*pb; | 	struct paste_buffer	*pb; | ||||||
| 	struct format_tree	*ft; | 	struct format_tree	*ft; | ||||||
| 	u_int			 idx; |  | ||||||
| 	char			*line; | 	char			*line; | ||||||
| 	const char		*template; | 	const char		*template; | ||||||
|  |  | ||||||
| 	if ((template = args_get(args, 'F')) == NULL) | 	if ((template = args_get(args, 'F')) == NULL) | ||||||
| 		template = LIST_BUFFERS_TEMPLATE; | 		template = LIST_BUFFERS_TEMPLATE; | ||||||
|  |  | ||||||
| 	idx = 0; | 	pb = NULL; | ||||||
| 	while ((pb = paste_walk_stack(&idx)) != NULL) { | 	while ((pb = paste_walk(pb)) != NULL) { | ||||||
| 		ft = format_create(); | 		ft = format_create(); | ||||||
| 		format_add(ft, "line", "%u", idx - 1); |  | ||||||
| 		format_paste_buffer(ft, pb, 0); | 		format_paste_buffer(ft, pb, 0); | ||||||
|  |  | ||||||
| 		line = format_expand(ft, template); | 		line = format_expand(ft, template); | ||||||
|   | |||||||
| @@ -50,30 +50,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) | |||||||
| 	struct client	*c = cmdq->client; | 	struct client	*c = cmdq->client; | ||||||
| 	struct session  *s; | 	struct session  *s; | ||||||
| 	FILE		*f; | 	FILE		*f; | ||||||
| 	const char	*path; | 	const char	*path, *bufname; | ||||||
| 	char		*pdata, *new_pdata, *cause; | 	char		*pdata, *new_pdata, *cause; | ||||||
| 	size_t		 psize; | 	size_t		 psize; | ||||||
| 	u_int		 limit; | 	int		 ch, error, cwd, fd; | ||||||
| 	int		 ch, error, buffer, *buffer_ptr, cwd, fd; |  | ||||||
|  |  | ||||||
| 	if (!args_has(args, 'b')) | 	bufname = NULL; | ||||||
| 		buffer = -1; | 	if (args_has(args, 'b')) | ||||||
| 	else { | 		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); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	path = args->argv[0]; | 	path = args->argv[0]; | ||||||
| 	if (strcmp(path, "-") == 0) { | 	if (strcmp(path, "-") == 0) { | ||||||
| 		buffer_ptr = xmalloc(sizeof *buffer_ptr); |  | ||||||
| 		*buffer_ptr = buffer; |  | ||||||
|  |  | ||||||
| 		error = server_set_stdin_callback(c, cmd_load_buffer_callback, | 		error = server_set_stdin_callback(c, cmd_load_buffer_callback, | ||||||
| 		    buffer_ptr, &cause); | 		    (void*)bufname, &cause); | ||||||
| 		if (error != 0) { | 		if (error != 0) { | ||||||
| 			cmdq_error(cmdq, "%s: %s", path, cause); | 			cmdq_error(cmdq, "%s: %s", path, cause); | ||||||
| 			free(cause); | 			free(cause); | ||||||
| @@ -117,14 +106,10 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) | |||||||
|  |  | ||||||
| 	fclose(f); | 	fclose(f); | ||||||
|  |  | ||||||
| 	limit = options_get_number(&global_options, "buffer-limit"); | 	if (paste_set(pdata, psize, bufname, &cause) != 0) { | ||||||
| 	if (buffer == -1) { | 		cmdq_error(cmdq, "%s", cause); | ||||||
| 		paste_add(pdata, psize, limit); |  | ||||||
| 		return (CMD_RETURN_NORMAL); |  | ||||||
| 	} |  | ||||||
| 	if (paste_replace(buffer, pdata, psize) != 0) { |  | ||||||
| 		cmdq_error(cmdq, "no buffer %d", buffer); |  | ||||||
| 		free(pdata); | 		free(pdata); | ||||||
|  | 		free(cause); | ||||||
| 		return (CMD_RETURN_ERROR); | 		return (CMD_RETURN_ERROR); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -140,10 +125,9 @@ error: | |||||||
| void | void | ||||||
| cmd_load_buffer_callback(struct client *c, int closed, void *data) | cmd_load_buffer_callback(struct client *c, int closed, void *data) | ||||||
| { | { | ||||||
| 	int	*buffer = data; | 	const char	*bufname = data; | ||||||
| 	char	*pdata; | 	char		*pdata, *cause; | ||||||
| 	size_t		 psize; | 	size_t		 psize; | ||||||
| 	u_int	 limit; |  | ||||||
|  |  | ||||||
| 	if (!closed) | 	if (!closed) | ||||||
| 		return; | 		return; | ||||||
| @@ -154,26 +138,21 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) | |||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	psize = EVBUFFER_LENGTH(c->stdin_data); | 	psize = EVBUFFER_LENGTH(c->stdin_data); | ||||||
| 	if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) { | 	if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) | ||||||
| 		free(data); |  | ||||||
| 		goto out; | 		goto out; | ||||||
| 	} |  | ||||||
| 	memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize); | 	memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize); | ||||||
| 	pdata[psize] = '\0'; | 	pdata[psize] = '\0'; | ||||||
| 	evbuffer_drain(c->stdin_data, psize); | 	evbuffer_drain(c->stdin_data, psize); | ||||||
|  |  | ||||||
| 	limit = options_get_number(&global_options, "buffer-limit"); | 	if (paste_set(pdata, psize, bufname, &cause) != 0) { | ||||||
| 	if (*buffer == -1) |  | ||||||
| 		paste_add(pdata, psize, limit); |  | ||||||
| 	else if (paste_replace(*buffer, pdata, psize) != 0) { |  | ||||||
| 		/* No context so can't use server_client_msg_error. */ | 		/* 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); | 		server_push_stderr(c); | ||||||
| 		free(pdata); | 		free(pdata); | ||||||
|  | 		free(cause); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	free(data); |  | ||||||
|  |  | ||||||
| out: | out: | ||||||
| 	cmdq_continue(c->cmdq); | 	cmdq_continue(c->cmdq); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -36,7 +36,7 @@ void	cmd_paste_buffer_filter(struct window_pane *, | |||||||
| const struct cmd_entry cmd_paste_buffer_entry = { | const struct cmd_entry cmd_paste_buffer_entry = { | ||||||
| 	"paste-buffer", "pasteb", | 	"paste-buffer", "pasteb", | ||||||
| 	"db:prs:t:", 0, 0, | 	"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, | 	0, | ||||||
| 	NULL, | 	NULL, | ||||||
| 	cmd_paste_buffer_exec | 	cmd_paste_buffer_exec | ||||||
| @@ -49,31 +49,22 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) | |||||||
| 	struct window_pane	*wp; | 	struct window_pane	*wp; | ||||||
| 	struct session		*s; | 	struct session		*s; | ||||||
| 	struct paste_buffer	*pb; | 	struct paste_buffer	*pb; | ||||||
| 	const char		*sepstr; | 	const char		*sepstr, *bufname; | ||||||
| 	char			*cause; |  | ||||||
| 	int			 buffer; |  | ||||||
| 	int			 pflag; | 	int			 pflag; | ||||||
|  |  | ||||||
| 	if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) | 	if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) | ||||||
| 		return (CMD_RETURN_ERROR); | 		return (CMD_RETURN_ERROR); | ||||||
|  |  | ||||||
| 	if (!args_has(args, 'b')) | 	bufname = NULL; | ||||||
| 		buffer = -1; | 	if (args_has(args, 'b')) | ||||||
| 	else { | 		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 (buffer == -1) | 	if (bufname == NULL) | ||||||
| 		pb = paste_get_top(); | 		pb = paste_get_top(); | ||||||
| 	else { | 	else { | ||||||
| 		pb = paste_get_index(buffer); | 		pb = paste_get_name(bufname); | ||||||
| 		if (pb == NULL) { | 		if (pb == NULL) { | ||||||
| 			cmdq_error(cmdq, "no buffer %d", buffer); | 			cmdq_error(cmdq, "no buffer %s", bufname); | ||||||
| 			return (CMD_RETURN_ERROR); | 			return (CMD_RETURN_ERROR); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -92,10 +83,10 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) | |||||||
|  |  | ||||||
| 	/* Delete the buffer if -d. */ | 	/* Delete the buffer if -d. */ | ||||||
| 	if (args_has(args, 'd')) { | 	if (args_has(args, 'd')) { | ||||||
| 		if (buffer == -1) | 		if (bufname == NULL) | ||||||
| 			paste_free_top(); | 			paste_free_top(); | ||||||
| 		else | 		else | ||||||
| 			paste_free_index(buffer); | 			paste_free_name(bufname); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return (CMD_RETURN_NORMAL); | 	return (CMD_RETURN_NORMAL); | ||||||
|   | |||||||
| @@ -59,10 +59,10 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) | |||||||
| 	struct client		*c = cmdq->client; | 	struct client		*c = cmdq->client; | ||||||
| 	struct session          *s; | 	struct session          *s; | ||||||
| 	struct paste_buffer	*pb; | 	struct paste_buffer	*pb; | ||||||
| 	const char		*path; | 	const char		*path, *bufname; | ||||||
| 	char			*cause, *start, *end, *msg; | 	char			*start, *end, *msg; | ||||||
| 	size_t			 size, used, msglen; | 	size_t			 size, used, msglen; | ||||||
| 	int			 cwd, fd, buffer; | 	int			 cwd, fd; | ||||||
| 	FILE			*f; | 	FILE			*f; | ||||||
|  |  | ||||||
| 	if (!args_has(args, 'b')) { | 	if (!args_has(args, 'b')) { | ||||||
| @@ -71,16 +71,10 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) | |||||||
| 			return (CMD_RETURN_ERROR); | 			return (CMD_RETURN_ERROR); | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); | 		bufname = args_get(args, 'b'); | ||||||
| 		if (cause != NULL) { | 		pb = paste_get_name(bufname); | ||||||
| 			cmdq_error(cmdq, "buffer %s", cause); |  | ||||||
| 			free(cause); |  | ||||||
| 			return (CMD_RETURN_ERROR); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		pb = paste_get_index(buffer); |  | ||||||
| 		if (pb == NULL) { | 		if (pb == NULL) { | ||||||
| 			cmdq_error(cmdq, "no buffer %d", buffer); | 			cmdq_error(cmdq, "no buffer %s", bufname); | ||||||
| 			return (CMD_RETURN_ERROR); | 			return (CMD_RETURN_ERROR); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -31,8 +31,8 @@ enum cmd_retval	 cmd_set_buffer_exec(struct cmd *, struct cmd_q *); | |||||||
|  |  | ||||||
| const struct cmd_entry cmd_set_buffer_entry = { | const struct cmd_entry cmd_set_buffer_entry = { | ||||||
| 	"set-buffer", "setb", | 	"set-buffer", "setb", | ||||||
| 	"ab:", 1, 1, | 	"ab:n:", 0, 1, | ||||||
| 	"[-a] " CMD_BUFFER_USAGE " data", | 	"[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", | ||||||
| 	0, | 	0, | ||||||
| 	NULL, | 	NULL, | ||||||
| 	cmd_set_buffer_exec | 	cmd_set_buffer_exec | ||||||
| @@ -43,38 +43,59 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) | |||||||
| { | { | ||||||
| 	struct args		*args = self->args; | 	struct args		*args = self->args; | ||||||
| 	struct paste_buffer	*pb; | 	struct paste_buffer	*pb; | ||||||
| 	u_int			 limit; |  | ||||||
| 	char			*pdata, *cause; | 	char			*pdata, *cause; | ||||||
|  | 	const char		*bufname; | ||||||
| 	size_t			 psize, newsize; | 	size_t			 psize, newsize; | ||||||
| 	int			 buffer; |  | ||||||
|  |  | ||||||
| 	limit = options_get_number(&global_options, "buffer-limit"); | 	bufname = NULL; | ||||||
|  |  | ||||||
|  | 	if (args_has(args, 'n')) { | ||||||
|  | 		if (args->argc > 0) { | ||||||
|  | 			cmdq_error(cmdq, "don't provide data with n flag"); | ||||||
|  | 			return (CMD_RETURN_ERROR); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		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); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return (CMD_RETURN_NORMAL); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (args->argc != 1) { | ||||||
|  | 		cmdq_error(cmdq, "no data specified"); | ||||||
|  | 		return (CMD_RETURN_ERROR); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	psize = 0; | 	psize = 0; | ||||||
| 	pdata = NULL; | 	pdata = NULL; | ||||||
|  |  | ||||||
| 	pb = NULL; | 	pb = NULL; | ||||||
| 	buffer = -1; |  | ||||||
|  |  | ||||||
| 	if ((newsize = strlen(args->argv[0])) == 0) | 	if ((newsize = strlen(args->argv[0])) == 0) | ||||||
| 		return (CMD_RETURN_NORMAL); | 		return (CMD_RETURN_NORMAL); | ||||||
|  |  | ||||||
| 	if (args_has(args, 'b')) { | 	if (args_has(args, 'b')) { | ||||||
| 		buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); | 		bufname = args_get(args, 'b'); | ||||||
| 		if (cause != NULL) { | 		pb = paste_get_name(bufname); | ||||||
| 			cmdq_error(cmdq, "buffer %s", cause); |  | ||||||
| 			free(cause); |  | ||||||
| 			return (CMD_RETURN_ERROR); |  | ||||||
| 		} |  | ||||||
| 		pb = paste_get_index(buffer); |  | ||||||
| 		if (pb == NULL) { |  | ||||||
| 			cmdq_error(cmdq, "no buffer %d", buffer); |  | ||||||
| 			return (CMD_RETURN_ERROR); |  | ||||||
| 		} |  | ||||||
| 	} else if (args_has(args, 'a')) { | 	} else if (args_has(args, 'a')) { | ||||||
| 		pb = paste_get_top(); | 		pb = paste_get_top(); | ||||||
| 		if (pb != NULL) | 		if (pb != NULL) | ||||||
| 			buffer = 0; | 			bufname = pb->name; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (args_has(args, 'a') && pb != NULL) { | 	if (args_has(args, 'a') && pb != NULL) { | ||||||
| @@ -87,10 +108,12 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) | |||||||
| 	memcpy(pdata + psize, args->argv[0], newsize); | 	memcpy(pdata + psize, args->argv[0], newsize); | ||||||
| 	psize += newsize; | 	psize += newsize; | ||||||
|  |  | ||||||
| 	if (buffer == -1) | 	if (paste_set(pdata, psize, bufname, &cause) != 0) { | ||||||
| 		paste_add(pdata, psize, limit); | 		cmdq_error(cmdq, "%s", cause); | ||||||
| 	else | 		free(pdata); | ||||||
| 		paste_replace(buffer, pdata, psize); | 		free(cause); | ||||||
|  | 		return (CMD_RETURN_ERROR); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return (CMD_RETURN_NORMAL); | 	return (CMD_RETURN_NORMAL); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								format.c
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								format.c
									
									
									
									
									
								
							| @@ -608,6 +608,7 @@ format_paste_buffer(struct format_tree *ft, struct paste_buffer *pb, | |||||||
| 	char	*s; | 	char	*s; | ||||||
|  |  | ||||||
| 	format_add(ft, "buffer_size", "%zu", pb->size); | 	format_add(ft, "buffer_size", "%zu", pb->size); | ||||||
|  | 	format_add(ft, "buffer_name", "%s", pb->name); | ||||||
|  |  | ||||||
| 	s = paste_make_sample(pb, utf8flag); | 	s = paste_make_sample(pb, utf8flag); | ||||||
| 	format_add(ft, "buffer_sample", "%s", s); | 	format_add(ft, "buffer_sample", "%s", s); | ||||||
|   | |||||||
							
								
								
									
										218
									
								
								paste.c
									
									
									
									
									
								
							
							
						
						
									
										218
									
								
								paste.c
									
									
									
									
									
								
							| @@ -26,127 +26,237 @@ | |||||||
| #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); |  | ||||||
| 	(*idx)++; |  | ||||||
| 	return (pb); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Get the top item on the stack. */ | int | ||||||
|  | paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b) | ||||||
|  | { | ||||||
|  | 	if (a->order > b->order) | ||||||
|  | 		return (-1); | ||||||
|  | 	if (a->order < b->order) | ||||||
|  | 		return (1); | ||||||
|  | 	return (0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Walk paste buffers by name. */ | ||||||
|  | struct paste_buffer * | ||||||
|  | paste_walk(struct paste_buffer *pb) | ||||||
|  | { | ||||||
|  | 	if (pb == NULL) | ||||||
|  | 		return (RB_MIN(paste_time_tree, &paste_by_time)); | ||||||
|  | 	return (RB_NEXT(paste_time_tree, &paste_by_time, pb)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Get the most recent automatic buffer */ | ||||||
| struct paste_buffer * | struct paste_buffer * | ||||||
| paste_get_top(void) | paste_get_top(void) | ||||||
| { | { | ||||||
| 	if (ARRAY_LENGTH(&paste_buffers) == 0) | 	struct paste_buffer	*pb; | ||||||
|  |  | ||||||
|  | 	pb = RB_MIN(paste_time_tree, &paste_by_time); | ||||||
|  | 	if (pb == NULL) | ||||||
| 		return (NULL); | 		return (NULL); | ||||||
| 	return (ARRAY_FIRST(&paste_buffers)); | 	return (pb); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Get an item by its index. */ | /* Free the most recent buffer */ | ||||||
| struct paste_buffer * |  | ||||||
| paste_get_index(u_int idx) |  | ||||||
| { |  | ||||||
| 	if (idx >= ARRAY_LENGTH(&paste_buffers)) |  | ||||||
| 		return (NULL); |  | ||||||
| 	return (ARRAY_ITEM(&paste_buffers, idx)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Free the top item on the stack. */ |  | ||||||
| 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); |  | ||||||
| 	ARRAY_REMOVE(&paste_buffers, 0); |  | ||||||
|  |  | ||||||
| 	free(pb->data); |  | ||||||
| 	free(pb); |  | ||||||
|  |  | ||||||
| 	return (0); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Free an item by index. */ | /* Get a paste buffer by name. */ | ||||||
| int | struct paste_buffer * | ||||||
| paste_free_index(u_int idx) | paste_get_name(const char *name) | ||||||
| { | { | ||||||
| 	struct paste_buffer	*pb; | 	struct paste_buffer	pbfind; | ||||||
|  |  | ||||||
| 	if (idx >= ARRAY_LENGTH(&paste_buffers)) | 	if (name == NULL || *name == '\0') | ||||||
|  | 		return (NULL); | ||||||
|  |  | ||||||
|  | 	pbfind.name = (char*)name; | ||||||
|  | 	return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Free a paste buffer by name. */ | ||||||
|  | int | ||||||
|  | paste_free_name(const char *name) | ||||||
|  | { | ||||||
|  | 	struct paste_buffer	*pb, pbfind; | ||||||
|  |  | ||||||
|  | 	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); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										63
									
								
								tmux.1
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								tmux.1
									
									
									
									
									
								
							| @@ -930,9 +930,6 @@ in emacs mode, and | |||||||
| .Ql 10w | .Ql 10w | ||||||
| in vi. | in vi. | ||||||
| .Pp | .Pp | ||||||
| When copying the selection, the repeat count indicates the buffer index to |  | ||||||
| replace, if used. |  | ||||||
| .Pp |  | ||||||
| Mode key bindings are defined in a set of named tables: | Mode key bindings are defined in a set of named tables: | ||||||
| .Em vi-edit | .Em vi-edit | ||||||
| and | and | ||||||
| @@ -1090,7 +1087,7 @@ but a different format may be specified with | |||||||
| .Fl F . | .Fl F . | ||||||
| .It Xo Ic capture-pane | .It Xo Ic capture-pane | ||||||
| .Op Fl aepPq | .Op Fl aepPq | ||||||
| .Op Fl b Ar buffer-index | .Op Fl b Ar buffer-name | ||||||
| .Op Fl E Ar end-line | .Op Fl E Ar end-line | ||||||
| .Op Fl S Ar start-line | .Op Fl S Ar start-line | ||||||
| .Op Fl t Ar target-pane | .Op Fl t Ar target-pane | ||||||
| @@ -3366,19 +3363,40 @@ is given, otherwise the active pane for the session attached to | |||||||
| .El | .El | ||||||
| .Sh BUFFERS | .Sh BUFFERS | ||||||
| .Nm | .Nm | ||||||
| maintains a stack of | maintains a set of named | ||||||
| .Em paste buffers . | .Em paste buffers . | ||||||
| Up to the value of the | Each buffer may be either explicitly or automatically named. | ||||||
|  | Explicitly named buffers are named when created with the | ||||||
|  | .Ic set-buffer | ||||||
|  | or | ||||||
|  | .Ic load-buffer | ||||||
|  | commands, or by renaming an automatically named buffer with | ||||||
|  | .Ic set-buffer | ||||||
|  | .Fl n . | ||||||
|  | Automatically named buffers are given a name such as | ||||||
|  | .Ql buffer0001 , | ||||||
|  | .Ql buffer0002 | ||||||
|  | and so on. | ||||||
|  | When the | ||||||
| .Ic buffer-limit | .Ic buffer-limit | ||||||
| option are kept; when a new buffer is added, the buffer at the bottom of the | option is reached, the oldest automatically named buffer is deleted. | ||||||
| stack is removed. | Explicitly named are not subject to | ||||||
|  | .Ic buffer-limit | ||||||
|  | and may be deleted with | ||||||
|  | .Ic delete-buffer | ||||||
|  | command. | ||||||
|  | .Pp | ||||||
| Buffers may be added using | Buffers may be added using | ||||||
| .Ic copy-mode | .Ic copy-mode | ||||||
| or the | or the | ||||||
| .Ic set-buffer | .Ic set-buffer | ||||||
| command, and pasted into a window using the | and | ||||||
|  | .Ic load-buffer | ||||||
|  | commands, and pasted into a window using the | ||||||
| .Ic paste-buffer | .Ic paste-buffer | ||||||
| command. | command. | ||||||
|  | If a buffer command is used and no buffer is specified, the most | ||||||
|  | recently added automatically named buffer is assumed. | ||||||
| .Pp | .Pp | ||||||
| A configurable history buffer is also maintained for each window. | A configurable history buffer is also maintained for each window. | ||||||
| By default, up to 2000 lines are kept; this can be altered with the | By default, up to 2000 lines are kept; this can be altered with the | ||||||
| @@ -3399,7 +3417,7 @@ Put a window into buffer choice mode, where a buffer may be chosen | |||||||
| interactively from a list. | interactively from a list. | ||||||
| After a buffer is selected, | After a buffer is selected, | ||||||
| .Ql %% | .Ql %% | ||||||
| is replaced by the buffer index in | is replaced by the buffer name in | ||||||
| .Ar template | .Ar template | ||||||
| and the result executed as a command. | and the result executed as a command. | ||||||
| If | If | ||||||
| @@ -3414,11 +3432,11 @@ This command works only if at least one client is attached. | |||||||
| .It Ic clear-history Op Fl t Ar target-pane | .It Ic clear-history Op Fl t Ar target-pane | ||||||
| .D1 (alias: Ic clearhist ) | .D1 (alias: Ic clearhist ) | ||||||
| Remove and free the history for the specified pane. | Remove and free the history for the specified pane. | ||||||
| .It Ic delete-buffer Op Fl b Ar buffer-index | .It Ic delete-buffer Op Fl b Ar buffer-name | ||||||
| .D1 (alias: Ic deleteb ) | .D1 (alias: Ic deleteb ) | ||||||
| Delete the buffer at | Delete the buffer named | ||||||
| .Ar buffer-index , | .Ar buffer-name , | ||||||
| or the top buffer if not specified. | or the most recently added automatically named buffer if not specified. | ||||||
| .It Xo Ic list-buffers | .It Xo Ic list-buffers | ||||||
| .Op Fl F Ar format | .Op Fl F Ar format | ||||||
| .Xc | .Xc | ||||||
| @@ -3430,7 +3448,7 @@ flag, see the | |||||||
| .Sx FORMATS | .Sx FORMATS | ||||||
| section. | section. | ||||||
| .It Xo Ic load-buffer | .It Xo Ic load-buffer | ||||||
| .Op Fl b Ar buffer-index | .Op Fl b Ar buffer-name | ||||||
| .Ar path | .Ar path | ||||||
| .Xc | .Xc | ||||||
| .D1 (alias: Ic loadb ) | .D1 (alias: Ic loadb ) | ||||||
| @@ -3438,7 +3456,7 @@ Load the contents of the specified paste buffer from | |||||||
| .Ar path . | .Ar path . | ||||||
| .It Xo Ic paste-buffer | .It Xo Ic paste-buffer | ||||||
| .Op Fl dpr | .Op Fl dpr | ||||||
| .Op Fl b Ar buffer-index | .Op Fl b Ar buffer-name | ||||||
| .Op Fl s Ar separator | .Op Fl s Ar separator | ||||||
| .Op Fl t Ar target-pane | .Op Fl t Ar target-pane | ||||||
| .Xc | .Xc | ||||||
| @@ -3447,7 +3465,7 @@ Insert the contents of a paste buffer into the specified pane. | |||||||
| If not specified, paste into the current one. | If not specified, paste into the current one. | ||||||
| With | With | ||||||
| .Fl d , | .Fl d , | ||||||
| also delete the paste buffer from the stack. | also delete the paste buffer. | ||||||
| When output, any linefeed (LF) characters in the paste buffer are replaced with | When output, any linefeed (LF) characters in the paste buffer are replaced with | ||||||
| a separator, by default carriage return (CR). | a separator, by default carriage return (CR). | ||||||
| A custom separator may be specified using the | A custom separator may be specified using the | ||||||
| @@ -3462,7 +3480,7 @@ is specified, paste bracket control codes are inserted around the | |||||||
| buffer if the application has requested bracketed paste mode. | buffer if the application has requested bracketed paste mode. | ||||||
| .It Xo Ic save-buffer | .It Xo Ic save-buffer | ||||||
| .Op Fl a | .Op Fl a | ||||||
| .Op Fl b Ar buffer-index | .Op Fl b Ar buffer-name | ||||||
| .Ar path | .Ar path | ||||||
| .Xc | .Xc | ||||||
| .D1 (alias: Ic saveb ) | .D1 (alias: Ic saveb ) | ||||||
| @@ -3473,7 +3491,8 @@ The | |||||||
| option appends to rather than overwriting the file. | option appends to rather than overwriting the file. | ||||||
| .It Xo Ic set-buffer | .It Xo Ic set-buffer | ||||||
| .Op Fl a | .Op Fl a | ||||||
| .Op Fl b Ar buffer-index | .Op Fl b Ar buffer-name | ||||||
|  | .Op Fl n Ar new-buffer-name | ||||||
| .Ar data | .Ar data | ||||||
| .Xc | .Xc | ||||||
| .D1 (alias: Ic setb ) | .D1 (alias: Ic setb ) | ||||||
| @@ -3482,8 +3501,12 @@ Set the contents of the specified buffer to | |||||||
| The | The | ||||||
| .Fl a | .Fl a | ||||||
| option appends to rather than overwriting the buffer. | option appends to rather than overwriting the buffer. | ||||||
|  | The | ||||||
|  | .Fl n | ||||||
|  | option renames the buffer to | ||||||
|  | .Ar new-buffer-name . | ||||||
| .It Xo Ic show-buffer | .It Xo Ic show-buffer | ||||||
| .Op Fl b Ar buffer-index | .Op Fl b Ar buffer-name | ||||||
| .Xc | .Xc | ||||||
| .D1 (alias: Ic showb ) | .D1 (alias: Ic showb ) | ||||||
| Display the contents of the specified buffer. | Display the contents of the specified buffer. | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								tmux.h
									
									
									
									
									
								
							| @@ -85,7 +85,7 @@ extern char   **environ; | |||||||
|  |  | ||||||
| /* Default template for choose-buffer. */ | /* Default template for choose-buffer. */ | ||||||
| #define CHOOSE_BUFFER_TEMPLATE					\ | #define CHOOSE_BUFFER_TEMPLATE					\ | ||||||
| 	"#{line}: #{buffer_size} bytes: #{buffer_sample}" | 	"#{buffer_name}: #{buffer_size} bytes: #{buffer_sample}" | ||||||
|  |  | ||||||
| /* Default template for choose-client. */ | /* Default template for choose-client. */ | ||||||
| #define CHOOSE_CLIENT_TEMPLATE					\ | #define CHOOSE_CLIENT_TEMPLATE					\ | ||||||
| @@ -118,7 +118,8 @@ extern char   **environ; | |||||||
|  |  | ||||||
| /* Default template for list-buffers. */ | /* Default template for list-buffers. */ | ||||||
| #define LIST_BUFFERS_TEMPLATE					\ | #define LIST_BUFFERS_TEMPLATE					\ | ||||||
| 	"#{line}: #{buffer_size} bytes: \"#{buffer_sample}\"" | 	"#{buffer_name}: #{buffer_size} bytes: "		\ | ||||||
|  | 	"\"#{buffer_sample}\"" | ||||||
|  |  | ||||||
| /* Default template for list-clients. */ | /* Default template for list-clients. */ | ||||||
| #define LIST_CLIENTS_TEMPLATE					\ | #define LIST_CLIENTS_TEMPLATE					\ | ||||||
| @@ -1036,6 +1037,13 @@ struct layout_cell { | |||||||
| struct paste_buffer { | struct paste_buffer { | ||||||
| 	char		*data; | 	char		*data; | ||||||
| 	size_t		 size; | 	size_t		 size; | ||||||
|  |  | ||||||
|  | 	char		*name; | ||||||
|  | 	int		 automatic; | ||||||
|  | 	u_int		 order; | ||||||
|  |  | ||||||
|  | 	RB_ENTRY(paste_buffer) name_entry; | ||||||
|  | 	RB_ENTRY(paste_buffer) time_entry; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Environment variable. */ | /* Environment variable. */ | ||||||
| @@ -1499,7 +1507,7 @@ RB_HEAD(format_tree, format_entry); | |||||||
| #define CMD_SRCDST_WINDOW_USAGE "[-s src-window] [-t dst-window]" | #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_SESSION_USAGE "[-s src-session] [-t dst-session]" | ||||||
| #define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]" | #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 */ | /* tmux.c */ | ||||||
| extern struct options global_options; | extern struct options global_options; | ||||||
| @@ -1711,13 +1719,14 @@ void	tty_keys_free(struct tty *); | |||||||
| int	tty_keys_next(struct tty *); | int	tty_keys_next(struct tty *); | ||||||
|  |  | ||||||
| /* paste.c */ | /* 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_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_top(void); | ||||||
| int		 paste_free_index(u_int); | int		 paste_free_name(const char *); | ||||||
| void		 paste_add(char *, size_t, u_int); | void		 paste_add(char *, size_t); | ||||||
| int		 paste_replace(u_int, char *, size_t); | int		 paste_rename(const char *, const char *, char **); | ||||||
|  | int		 paste_set(char *, size_t, const char *, char **); | ||||||
| char		*paste_make_sample(struct paste_buffer *, int); | char		*paste_make_sample(struct paste_buffer *, int); | ||||||
| void		 paste_send_pane(struct paste_buffer *, struct window_pane *, | void		 paste_send_pane(struct paste_buffer *, struct window_pane *, | ||||||
| 		     const char *, int); | 		     const char *, int); | ||||||
|   | |||||||
| @@ -54,11 +54,12 @@ void	window_copy_update_cursor(struct window_pane *, u_int, u_int); | |||||||
| void	window_copy_start_selection(struct window_pane *); | void	window_copy_start_selection(struct window_pane *); | ||||||
| int	window_copy_update_selection(struct window_pane *, int); | int	window_copy_update_selection(struct window_pane *, int); | ||||||
| void   *window_copy_get_selection(struct window_pane *, size_t *); | 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 *, | ||||||
| void	window_copy_copy_pipe( | 	    size_t); | ||||||
| 	    struct window_pane *, struct session *, int, const char *); | void	window_copy_copy_pipe(struct window_pane *, struct session *, | ||||||
| void	window_copy_copy_selection(struct window_pane *, int); | 	    const char *, const char *); | ||||||
| void	window_copy_append_selection(struct window_pane *, int); | 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_clear_selection(struct window_pane *); | ||||||
| void	window_copy_copy_line( | void	window_copy_copy_line( | ||||||
| 	    struct window_pane *, char **, size_t *, u_int, u_int, u_int); | 	    struct window_pane *, char **, size_t *, u_int, u_int, u_int); | ||||||
| @@ -417,7 +418,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) | |||||||
| 	switch (cmd) { | 	switch (cmd) { | ||||||
| 	case MODEKEYCOPY_APPENDSELECTION: | 	case MODEKEYCOPY_APPENDSELECTION: | ||||||
| 		if (sess != NULL) { | 		if (sess != NULL) { | ||||||
| 			window_copy_append_selection(wp, data->numprefix); | 			window_copy_append_selection(wp, NULL); | ||||||
| 			window_pane_reset_mode(wp); | 			window_pane_reset_mode(wp); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| @@ -543,7 +544,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) | |||||||
| 		if (sess != NULL && | 		if (sess != NULL && | ||||||
| 		    (cmd == MODEKEYCOPY_COPYLINE || | 		    (cmd == MODEKEYCOPY_COPYLINE || | ||||||
| 		    cmd == MODEKEYCOPY_COPYENDOFLINE)) { | 		    cmd == MODEKEYCOPY_COPYENDOFLINE)) { | ||||||
| 			window_copy_copy_selection(wp, -1); | 			window_copy_copy_selection(wp, NULL); | ||||||
| 			window_pane_reset_mode(wp); | 			window_pane_reset_mode(wp); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| @@ -554,14 +555,14 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) | |||||||
| 		break; | 		break; | ||||||
| 	case MODEKEYCOPY_COPYPIPE: | 	case MODEKEYCOPY_COPYPIPE: | ||||||
| 		if (sess != NULL) { | 		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); | 			window_pane_reset_mode(wp); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 		break; | 		break; | ||||||
| 	case MODEKEYCOPY_COPYSELECTION: | 	case MODEKEYCOPY_COPYSELECTION: | ||||||
| 		if (sess != NULL) { | 		if (sess != NULL) { | ||||||
| 			window_copy_copy_selection(wp, data->numprefix); | 			window_copy_copy_selection(wp, NULL); | ||||||
| 			window_pane_reset_mode(wp); | 			window_pane_reset_mode(wp); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| @@ -918,7 +919,7 @@ reset_mode: | |||||||
| 	s->mode &= ~MODE_MOUSE_BUTTON; | 	s->mode &= ~MODE_MOUSE_BUTTON; | ||||||
| 	s->mode |= MODE_MOUSE_STANDARD; | 	s->mode |= MODE_MOUSE_STANDARD; | ||||||
| 	if (sess != NULL) { | 	if (sess != NULL) { | ||||||
| 		window_copy_copy_selection(wp, -1); | 		window_copy_copy_selection(wp, NULL); | ||||||
| 		window_pane_reset_mode(wp); | 		window_pane_reset_mode(wp); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -1452,9 +1453,9 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) | |||||||
| } | } | ||||||
|  |  | ||||||
| void | 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; | 	struct screen_write_ctx	ctx; | ||||||
|  |  | ||||||
| 	if (options_get_number(&global_options, "set-clipboard")) { | 	if (options_get_number(&global_options, "set-clipboard")) { | ||||||
| @@ -1463,16 +1464,13 @@ window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len) | |||||||
| 		screen_write_stop(&ctx); | 		screen_write_stop(&ctx); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (idx == -1) { | 	if (paste_set(buf, len, bufname, NULL) != 0) | ||||||
| 		limit = options_get_number(&global_options, "buffer-limit"); |  | ||||||
| 		paste_add(buf, len, limit); |  | ||||||
| 	} else if (paste_replace(idx, buf, len) != 0) |  | ||||||
| 		free(buf); | 		free(buf); | ||||||
| } | } | ||||||
|  |  | ||||||
| void | void | ||||||
| window_copy_copy_pipe( | window_copy_copy_pipe(struct window_pane *wp, struct session *sess, | ||||||
|     struct window_pane *wp, struct session *sess, int idx, const char *arg) |     const char *bufname, const char *arg) | ||||||
| { | { | ||||||
| 	void		*buf; | 	void		*buf; | ||||||
| 	size_t		 len; | 	size_t		 len; | ||||||
| @@ -1486,11 +1484,11 @@ window_copy_copy_pipe( | |||||||
| 	job = job_run(arg, sess, NULL, NULL, NULL); | 	job = job_run(arg, sess, NULL, NULL, NULL); | ||||||
| 	bufferevent_write(job->event, buf, len); | 	bufferevent_write(job->event, buf, len); | ||||||
|  |  | ||||||
| 	window_copy_copy_buffer(wp, idx, buf, len); | 	window_copy_copy_buffer(wp, bufname, buf, len); | ||||||
| } | } | ||||||
|  |  | ||||||
| void | void | ||||||
| window_copy_copy_selection(struct window_pane *wp, int idx) | window_copy_copy_selection(struct window_pane *wp, const char *bufname) | ||||||
| { | { | ||||||
| 	void*	buf; | 	void*	buf; | ||||||
| 	size_t	len; | 	size_t	len; | ||||||
| @@ -1499,16 +1497,15 @@ window_copy_copy_selection(struct window_pane *wp, int idx) | |||||||
| 	if (buf == NULL) | 	if (buf == NULL) | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	window_copy_copy_buffer(wp, idx, buf, len); | 	window_copy_copy_buffer(wp, bufname, buf, len); | ||||||
| } | } | ||||||
|  |  | ||||||
| void | void | ||||||
| window_copy_append_selection(struct window_pane *wp, int idx) | window_copy_append_selection(struct window_pane *wp, const char *bufname) | ||||||
| { | { | ||||||
| 	char				*buf; | 	char				*buf; | ||||||
| 	struct paste_buffer		*pb; | 	struct paste_buffer		*pb; | ||||||
| 	size_t				 len; | 	size_t				 len; | ||||||
| 	u_int			 limit; |  | ||||||
| 	struct screen_write_ctx		 ctx; | 	struct screen_write_ctx		 ctx; | ||||||
|  |  | ||||||
| 	buf = window_copy_get_selection(wp, &len); | 	buf = window_copy_get_selection(wp, &len); | ||||||
| @@ -1521,24 +1518,19 @@ window_copy_append_selection(struct window_pane *wp, int idx) | |||||||
| 		screen_write_stop(&ctx); | 		screen_write_stop(&ctx); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (idx == -1) | 	if (bufname == NULL || *bufname == '\0') { | ||||||
| 		idx = 0; | 		pb = paste_get_top(); | ||||||
|  | 		if (pb != NULL) | ||||||
| 	if (idx == 0 && paste_get_top() == NULL) { | 			bufname = pb->name; | ||||||
| 		limit = options_get_number(&global_options, "buffer-limit"); | 	} else | ||||||
| 		paste_add(buf, len, limit); | 		pb = paste_get_name(bufname); | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	pb = paste_get_index(idx); |  | ||||||
| 	if (pb != NULL) { | 	if (pb != NULL) { | ||||||
| 		buf = xrealloc(buf, 1, len + pb->size); | 		buf = xrealloc(buf, 1, len + pb->size); | ||||||
| 		memmove(buf + pb->size, buf, len); | 		memmove(buf + pb->size, buf, len); | ||||||
| 		memcpy(buf, pb->data, pb->size); | 		memcpy(buf, pb->data, pb->size); | ||||||
| 		len += pb->size; | 		len += pb->size; | ||||||
| 	} | 	} | ||||||
|  | 	if (paste_set(buf, len, bufname, NULL) != 0) | ||||||
| 	if (paste_replace(idx, buf, len) != 0) |  | ||||||
| 		free(buf); | 		free(buf); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 nicm
					nicm