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:
nicm 2014-05-13 07:34:35 +00:00
parent f4ffaf5a7f
commit 3dbacbb62b
13 changed files with 353 additions and 248 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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);
} }

View File

@ -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);

View File

@ -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);
} }

View File

@ -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);

View File

@ -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);
} }
} }

View File

@ -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);
} }

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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);

View File

@ -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,17 +1497,16 @@ 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);
if (buf == NULL) if (buf == NULL)
@ -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);
} }