diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 49614632..bbaf70cd 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -38,7 +38,7 @@ char *cmd_capture_pane_history(struct args *, struct cmd_q *, const struct cmd_entry cmd_capture_pane_entry = { "capture-pane", "capturep", "ab:CeE:JpPqS:t:", 0, 0, - "[-aCeJpPq] [-b buffer-index] [-E end-line] [-S start-line]" + "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] [-S start-line]" CMD_TARGET_PANE_USAGE, 0, NULL, @@ -165,8 +165,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c; struct window_pane *wp; char *buf, *cause; - int buffer; - u_int limit; + const char *bufname; size_t len; 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); server_push_stdout(c); } else { - limit = options_get_number(&global_options, "buffer-limit"); - if (!args_has(args, 'b')) { - paste_add(buf, len, limit); - return (CMD_RETURN_NORMAL); - } - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); + bufname = NULL; + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); + + if (paste_set(buf, len, bufname, &cause) != 0) { + cmdq_error(cmdq, "%s", cause); free(buf); free(cause); 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); diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index 18d28345..6b146fad 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -75,19 +75,20 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) action = xstrdup("paste-buffer -b '%%'"); idx = 0; - while ((pb = paste_walk_stack(&idx)) != NULL) { + pb = NULL; + while ((pb = paste_walk(pb)) != NULL) { cdata = window_choose_data_create(TREE_OTHER, c, c->session); - cdata->idx = idx - 1; + cdata->idx = idx; cdata->ft_template = xstrdup(template); - format_add(cdata->ft, "line", "%u", idx - 1); format_paste_buffer(cdata->ft, pb, utf8flag); - xasprintf(&action_data, "%u", idx - 1); + xasprintf(&action_data, "%s", pb->name); cdata->command = cmd_template_replace(action, action_data, 1); free(action_data); window_choose_add(wl->window->active, cdata); + idx++; } free(action); diff --git a/cmd-delete-buffer.c b/cmd-delete-buffer.c index e05fe090..9957de66 100644 --- a/cmd-delete-buffer.c +++ b/cmd-delete-buffer.c @@ -41,23 +41,16 @@ enum cmd_retval cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - char *cause; - int buffer; + const char *bufname; if (!args_has(args, 'b')) { paste_free_top(); return (CMD_RETURN_NORMAL); } + bufname = args_get(args, 'b'); - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - - if (paste_free_index(buffer) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); + if (paste_free_name(bufname) != 0) { + cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } diff --git a/cmd-find-window.c b/cmd-find-window.c index bef9afee..39a279b5 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -200,6 +200,8 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) window_choose_ready(wl->window->active, 0, cmd_find_window_callback); out: + for (i = 0; i < ARRAY_LENGTH(&find_list); i++) + free(ARRAY_ITEM(&find_list, i).list_ctx); ARRAY_FREE(&find_list); return (CMD_RETURN_NORMAL); } diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index f3987dda..c5a6e322 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -44,17 +44,15 @@ cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct paste_buffer *pb; struct format_tree *ft; - u_int idx; char *line; const char *template; if ((template = args_get(args, 'F')) == NULL) template = LIST_BUFFERS_TEMPLATE; - idx = 0; - while ((pb = paste_walk_stack(&idx)) != NULL) { + pb = NULL; + while ((pb = paste_walk(pb)) != NULL) { ft = format_create(); - format_add(ft, "line", "%u", idx - 1); format_paste_buffer(ft, pb, 0); line = format_expand(ft, template); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 16ff40d0..d2e1cc6b 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -50,30 +50,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; FILE *f; - const char *path; + const char *path, *bufname; char *pdata, *new_pdata, *cause; size_t psize; - u_int limit; - int ch, error, buffer, *buffer_ptr, cwd, fd; + int ch, error, cwd, fd; - if (!args_has(args, 'b')) - buffer = -1; - else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - } + bufname = NULL; + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); path = args->argv[0]; if (strcmp(path, "-") == 0) { - buffer_ptr = xmalloc(sizeof *buffer_ptr); - *buffer_ptr = buffer; - error = server_set_stdin_callback(c, cmd_load_buffer_callback, - buffer_ptr, &cause); + (void*)bufname, &cause); if (error != 0) { cmdq_error(cmdq, "%s: %s", path, cause); free(cause); @@ -117,14 +106,10 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) fclose(f); - limit = options_get_number(&global_options, "buffer-limit"); - if (buffer == -1) { - paste_add(pdata, psize, limit); - return (CMD_RETURN_NORMAL); - } - if (paste_replace(buffer, pdata, psize) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); + if (paste_set(pdata, psize, bufname, &cause) != 0) { + cmdq_error(cmdq, "%s", cause); free(pdata); + free(cause); return (CMD_RETURN_ERROR); } @@ -140,10 +125,9 @@ error: void cmd_load_buffer_callback(struct client *c, int closed, void *data) { - int *buffer = data; - char *pdata; - size_t psize; - u_int limit; + const char *bufname = data; + char *pdata, *cause; + size_t psize; if (!closed) return; @@ -154,26 +138,21 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) return; psize = EVBUFFER_LENGTH(c->stdin_data); - if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) { - free(data); + if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) goto out; - } + memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize); pdata[psize] = '\0'; evbuffer_drain(c->stdin_data, psize); - limit = options_get_number(&global_options, "buffer-limit"); - if (*buffer == -1) - paste_add(pdata, psize, limit); - else if (paste_replace(*buffer, pdata, psize) != 0) { + if (paste_set(pdata, psize, bufname, &cause) != 0) { /* No context so can't use server_client_msg_error. */ - evbuffer_add_printf(c->stderr_data, "no buffer %d\n", *buffer); + evbuffer_add_printf(c->stderr_data, "%s", cause); server_push_stderr(c); free(pdata); + free(cause); } - free(data); - out: cmdq_continue(c->cmdq); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 3071bd64..b36de70a 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -35,10 +35,10 @@ enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { "new-session", "new", - "Ac:dDF:n:Ps:t:x:y:", 0, 1, + "Ac:dDF:n:Ps:t:x:y:", 0, -1, "[-AdDP] [-c start-directory] [-F format] [-n window-name] " - "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] [-y height] " - "[command]", + "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " + "[-y height] [command]", CMD_STARTSERVER|CMD_CANTNEST, NULL, cmd_new_session_exec @@ -55,8 +55,9 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) struct termios tio, *tiop; const char *newname, *target, *update, *errstr, *template; const char *path; - char *cmd, *cause, *cp; + char **argv, *cmd, *cause, *cp; int detached, already_attached, idx, cwd, fd = -1; + int argc; u_int sx, sy; struct format_tree *ft; struct environ_entry *envent; @@ -183,12 +184,21 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) sy = 1; /* Figure out the command for the new window. */ - if (target != NULL) - cmd = NULL; - else if (args->argc != 0) - cmd = args->argv[0]; - else + argc = -1; + argv = NULL; + if (target == NULL && args->argc != 0) { + argc = args->argc; + argv = args->argv; + } else if (target == NULL) { cmd = options_get_string(&global_s_options, "default-command"); + if (cmd != NULL && *cmd != '\0') { + argc = 1; + argv = &cmd; + } else { + argc = 0; + argv = NULL; + } + } path = NULL; if (c != NULL && c->session == NULL) @@ -206,8 +216,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Create the new session. */ idx = -1 - options_get_number(&global_s_options, "base-index"); - s = session_create(newname, cmd, path, cwd, &env, tiop, idx, sx, sy, - &cause); + s = session_create(newname, argc, argv, path, cwd, &env, tiop, idx, sx, + sy, &cause); if (s == NULL) { cmdq_error(cmdq, "create session failed: %s", cause); free(cause); @@ -216,7 +226,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) environ_free(&env); /* Set the initial window name if one given. */ - if (cmd != NULL && args_has(args, 'n')) { + if (argc >= 0 && args_has(args, 'n')) { w = s->curw->window; window_set_name(w, args_get(args, 'n')); options_set_number(&w->options, "automatic-rename", 0); diff --git a/cmd-new-window.c b/cmd-new-window.c index 6e9fea50..cd0042ee 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -34,7 +34,7 @@ enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_window_entry = { "new-window", "neww", - "ac:dF:kn:Pt:", 0, 1, + "ac:dF:kn:Pt:", 0, -1, "[-adkP] [-c start-directory] [-F format] [-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", 0, @@ -50,8 +50,8 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl; struct client *c; const char *cmd, *path, *template; - char *cause, *cp; - int idx, last, detached, cwd, fd = -1; + char **argv, *cause, *cp; + int argc, idx, last, detached, cwd, fd = -1; struct format_tree *ft; struct environ_entry *envent; @@ -84,10 +84,19 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) } detached = args_has(args, 'd'); - if (args->argc == 0) + if (args->argc == 0) { cmd = options_get_string(&s->options, "default-command"); - else - cmd = args->argv[0]; + if (cmd != NULL && *cmd != '\0') { + argc = 1; + argv = (char**)&cmd; + } else { + argc = 0; + argv = NULL; + } + } else { + argc = args->argc; + argv = args->argv; + } path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) @@ -145,7 +154,8 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) if (idx == -1) idx = -1 - options_get_number(&s->options, "base-index"); - wl = session_new(s, args_get(args, 'n'), cmd, path, cwd, idx, &cause); + wl = session_new(s, args_get(args, 'n'), argc, argv, path, cwd, idx, + &cause); if (wl == NULL) { cmdq_error(cmdq, "create window failed: %s", cause); free(cause); diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 4bc2cfda..f6c9d0ad 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -35,7 +35,7 @@ void cmd_paste_buffer_filter(struct window_pane *, const struct cmd_entry cmd_paste_buffer_entry = { "paste-buffer", "pasteb", "db:prs:t:", 0, 0, - "[-dpr] [-s separator] [-b buffer-index] " CMD_TARGET_PANE_USAGE, + "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, 0, NULL, cmd_paste_buffer_exec @@ -48,31 +48,22 @@ 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; - char *cause; - int buffer; + const char *sepstr, *bufname; int pflag; if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) return (CMD_RETURN_ERROR); - if (!args_has(args, 'b')) - buffer = -1; - else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - } + bufname = NULL; + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); - if (buffer == -1) + if (bufname == NULL) pb = paste_get_top(); else { - pb = paste_get_index(buffer); + pb = paste_get_name(bufname); if (pb == NULL) { - cmdq_error(cmdq, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } } @@ -91,10 +82,10 @@ 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_top(); else - paste_free_index(buffer); + paste_free_name(bufname); } return (CMD_RETURN_NORMAL); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 9ac5b0b8..f951685c 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -32,7 +32,7 @@ enum cmd_retval cmd_respawn_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_pane_entry = { "respawn-pane", "respawnp", - "kt:", 0, 1, + "kt:", 0, -1, "[-k] " CMD_TARGET_PANE_USAGE " [command]", 0, NULL, @@ -48,7 +48,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp; struct session *s; struct environ env; - const char *cmd, *path; + const char *path; char *cause; u_int idx; struct environ_entry *envent; @@ -74,11 +74,6 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) screen_reinit(&wp->base); input_init(wp); - if (args->argc != 0) - cmd = args->argv[0]; - else - cmd = NULL; - path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) envent = environ_find(&cmdq->client->environ, "PATH"); @@ -87,8 +82,8 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, cmd, path, NULL, -1, &env, s->tio, - &cause) != 0) { + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env, + s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn pane failed: %s", cause); free(cause); environ_free(&env); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index d891eff3..307560bb 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -31,7 +31,7 @@ enum cmd_retval cmd_respawn_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_window_entry = { "respawn-window", "respawnw", - "kt:", 0, 1, + "kt:", 0, -1, "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", 0, NULL, @@ -47,7 +47,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp; struct session *s; struct environ env; - const char *cmd, *path; + const char *path; char *cause; struct environ_entry *envent; @@ -76,10 +76,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) window_destroy_panes(w); TAILQ_INSERT_HEAD(&w->panes, wp, entry); window_pane_resize(wp, w->sx, w->sy); - if (args->argc != 0) - cmd = args->argv[0]; - else - cmd = NULL; path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) @@ -89,8 +85,8 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, cmd, path, NULL, -1, &env, s->tio, - &cause) != 0) { + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env, + s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); environ_free(&env); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index b5ca62ee..8dd52c96 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -58,10 +58,10 @@ 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; - char *cause, *start, *end, *msg; + const char *path, *bufname; + char *start, *end, *msg; size_t size, used, msglen; - int cwd, fd, buffer; + int cwd, fd; FILE *f; if (!args_has(args, 'b')) { @@ -70,16 +70,10 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } } else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - - pb = paste_get_index(buffer); + bufname = args_get(args, 'b'); + pb = paste_get_name(bufname); if (pb == NULL) { - cmdq_error(cmdq, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } } diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index b0fc3fe4..82922279 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_buffer_entry = { "set-buffer", "setb", - "ab:", 1, 1, - "[-a] " CMD_BUFFER_USAGE " data", + "ab:n:", 0, 1, + "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", 0, NULL, 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 paste_buffer *pb; - u_int limit; char *pdata, *cause; + const char *bufname; 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; pdata = NULL; pb = NULL; - buffer = -1; if ((newsize = strlen(args->argv[0])) == 0) return (CMD_RETURN_NORMAL); 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(buffer); - if (pb == NULL) { - cmdq_error(cmdq, "no buffer %d", buffer); - return (CMD_RETURN_ERROR); - } + bufname = args_get(args, 'b'); + pb = paste_get_name(bufname); } else if (args_has(args, 'a')) { pb = paste_get_top(); if (pb != NULL) - buffer = 0; + bufname = pb->name; } if (args_has(args, 'a') && pb != NULL) { @@ -87,10 +108,12 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) memcpy(pdata + psize, args->argv[0], newsize); psize += newsize; - if (buffer == -1) - paste_add(pdata, psize, limit); - else - paste_replace(buffer, pdata, psize); + if (paste_set(pdata, psize, bufname, &cause) != 0) { + cmdq_error(cmdq, "%s", cause); + free(pdata); + free(cause); + return (CMD_RETURN_ERROR); + } return (CMD_RETURN_NORMAL); } diff --git a/cmd-split-window.c b/cmd-split-window.c index 9c4734be..ea047a3f 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -35,7 +35,7 @@ enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_split_window_entry = { "split-window", "splitw", - "c:dF:l:hp:Pt:v", 0, 1, + "c:dF:l:hp:Pt:v", 0, -1, "[-dhvP] [-c start-directory] [-F format] [-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", 0, @@ -61,9 +61,9 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp, *new_wp = NULL; struct environ env; const char *cmd, *path, *shell, *template; - char *cause, *new_cause, *cp; + char **argv, *cause, *new_cause, *cp; u_int hlimit; - int size, percentage, cwd, fd = -1; + int argc, size, percentage, cwd, fd = -1; enum layout_type type; struct layout_cell *lc; struct client *c; @@ -80,10 +80,19 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) environ_copy(&s->environ, &env); server_fill_environ(s, &env); - if (args->argc == 0) + if (args->argc == 0) { cmd = options_get_string(&s->options, "default-command"); - else - cmd = args->argv[0]; + if (cmd != NULL && *cmd != '\0') { + argc = 1; + argv = (char**)&cmd; + } else { + argc = 0; + argv = NULL; + } + } else { + argc = args->argc; + argv = args->argv; + } if (args_has(args, 'c')) { ft = format_create(); @@ -157,8 +166,8 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) if (envent != NULL) path = envent->value; - if (window_pane_spawn( - new_wp, cmd, path, shell, cwd, &env, s->tio, &cause) != 0) + if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, &env, + s->tio, &cause) != 0) goto error; layout_assign_pane(lc, new_wp); diff --git a/cmd.c b/cmd.c index f2d88c05..3a948c80 100644 --- a/cmd.c +++ b/cmd.c @@ -179,14 +179,14 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) } char ** -cmd_copy_argv(int argc, char *const *argv) +cmd_copy_argv(int argc, char **argv) { char **new_argv; int i; if (argc == 0) return (NULL); - new_argv = xcalloc(argc, sizeof *new_argv); + new_argv = xcalloc(argc + 1, sizeof *new_argv); for (i = 0; i < argc; i++) { if (argv[i] != NULL) new_argv[i] = xstrdup(argv[i]); @@ -206,6 +206,32 @@ cmd_free_argv(int argc, char **argv) free(argv); } +char * +cmd_stringify_argv(int argc, char **argv) +{ + char *buf; + int i; + size_t len; + + if (argc == 0) + return (xstrdup("")); + + len = 0; + buf = NULL; + + for (i = 0; i < argc; i++) { + len += strlen(argv[i]) + 1; + buf = xrealloc(buf, 1, len); + + if (i == 0) + *buf = '\0'; + else + strlcat(buf, " ", len); + strlcat(buf, argv[i], len); + } + return (buf); +} + struct cmd * cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) { diff --git a/format.c b/format.c index 6f988b9a..ecfda1be 100644 --- a/format.c +++ b/format.c @@ -368,7 +368,7 @@ format_get_command(struct window_pane *wp) cmd = osdep_get_name(wp->fd, wp->tty); if (cmd == NULL || *cmd == '\0') { free(cmd); - cmd = xstrdup(wp->cmd); + cmd = cmd_stringify_argv(wp->argc, wp->argv); if (cmd == NULL || *cmd == '\0') { free(cmd); cmd = xstrdup(wp->shell); @@ -559,10 +559,12 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) if (wp->tty != NULL) format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_pid", "%ld", (long) wp->pid); - if (wp->cmd != NULL) - format_add(ft, "pane_start_command", "%s", wp->cmd); if ((cwd = osdep_get_cwd(wp->fd)) != NULL) format_add(ft, "pane_current_path", "%s", cwd); + if ((cmd = cmd_stringify_argv(wp->argc, wp->argv)) != NULL) { + format_add(ft, "pane_start_command", "%s", cmd); + free(cmd); + } if ((cmd = format_get_command(wp)) != NULL) { format_add(ft, "pane_current_command", "%s", cmd); free(cmd); @@ -610,6 +612,7 @@ format_paste_buffer(struct format_tree *ft, struct paste_buffer *pb, char *s; format_add(ft, "buffer_size", "%zu", pb->size); + format_add(ft, "buffer_name", "%s", pb->name); s = paste_make_sample(pb, utf8flag); format_add(ft, "buffer_sample", "%s", s); diff --git a/input-keys.c b/input-keys.c index 11126985..7531f22d 100644 --- a/input-keys.c +++ b/input-keys.c @@ -204,6 +204,21 @@ input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) char buf[40]; size_t len; struct paste_buffer *pb; + u_int i; + + /* + * If the alternate screen is active and hasn't enabled the mouse, send + * up and down key presses for the mouse wheel. + */ + if (wp->saved_grid != NULL && !(wp->screen->mode & ALL_MOUSE_MODES)) { + for (i = 0; i < m->scroll; i++) { + if (m->wheel == MOUSE_WHEEL_UP) + input_key(wp, KEYC_UP); + else + input_key(wp, KEYC_DOWN); + } + return; + } if (wp->screen->mode & ALL_MOUSE_MODES) { /* diff --git a/layout.c b/layout.c index cfa34b9e..646d1bd9 100644 --- a/layout.c +++ b/layout.c @@ -53,7 +53,6 @@ layout_create_cell(struct layout_cell *lcparent) lc->yoff = UINT_MAX; lc->wp = NULL; - lc->lastwp = NULL; return (lc); } diff --git a/mode-key.c b/mode-key.c index 57be2d81..8bb83cb9 100644 --- a/mode-key.c +++ b/mode-key.c @@ -141,6 +141,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" }, @@ -257,6 +258,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/names.c b/names.c index 7c02961c..5c956259 100644 --- a/names.c +++ b/names.c @@ -68,9 +68,15 @@ window_name_callback(unused int fd, unused short events, void *data) char * default_window_name(struct window *w) { - if (w->active->cmd != NULL && *w->active->cmd != '\0') - return (parse_window_name(w->active->cmd)); - return (parse_window_name(w->active->shell)); + char *cmd, *s; + + cmd = cmd_stringify_argv(w->active->argc, w->active->argv); + if (cmd != NULL && *cmd != '\0') + s = parse_window_name(cmd); + else + s = parse_window_name(w->active->shell); + free(cmd); + return (s); } char * diff --git a/paste.c b/paste.c index 0f29b6b9..fdd4f5d6 100644 --- a/paste.c +++ b/paste.c @@ -25,127 +25,237 @@ #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! */ -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. */ -struct paste_buffer * -paste_walk_stack(u_int *idx) +int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *); +RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); +RB_GENERATE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); + +int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *); +RB_PROTOTYPE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); +RB_GENERATE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); + +int +paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b) { - struct paste_buffer *pb; - - pb = paste_get_index(*idx); - (*idx)++; - return (pb); + return (strcmp(a->name, b->name)); } -/* 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 * 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 (ARRAY_FIRST(&paste_buffers)); + return (pb); } -/* Get an item by its index. */ -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. */ +/* Free the most recent buffer */ int paste_free_top(void) { struct paste_buffer *pb; - if (ARRAY_LENGTH(&paste_buffers) == 0) + pb = paste_get_top(); + if (pb == NULL) return (-1); - - pb = ARRAY_FIRST(&paste_buffers); - ARRAY_REMOVE(&paste_buffers, 0); - - free(pb->data); - free(pb); - - return (0); + return (paste_free_name(pb->name)); } -/* Free an item by index. */ -int -paste_free_index(u_int idx) +/* Get a paste buffer by name. */ +struct paste_buffer * +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); - pb = ARRAY_ITEM(&paste_buffers, idx); - ARRAY_REMOVE(&paste_buffers, idx); + pbfind.name = (char*)name; + 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->name); free(pb); - 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. */ 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) return; - while (ARRAY_LENGTH(&paste_buffers) >= limit) { - pb = ARRAY_LAST(&paste_buffers); - free(pb->data); - free(pb); - ARRAY_TRUNC(&paste_buffers, 1); + limit = options_get_number(&global_options, "buffer-limit"); + RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) { + if (paste_num_automatic < limit) + break; + if (pb->automatic) + paste_free_name(pb->name); } 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->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. */ 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; + if (cause != NULL) + *cause = NULL; + if (size == 0) { free(data); 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); + } - pb = ARRAY_ITEM(&paste_buffers, idx); - free(pb->data); + pb = paste_get_name(name); + if (pb != NULL) + paste_free_name(name); + + pb = xmalloc(sizeof *pb); + + pb->name = xstrdup(name); pb->data = data; 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); } diff --git a/session.c b/session.c index ae6f35c9..2bcb1b98 100644 --- a/session.c +++ b/session.c @@ -84,11 +84,12 @@ session_find_by_id(u_int id) /* Create a new session. */ struct session * -session_create(const char *name, const char *cmd, const char *path, int cwd, - struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy, - char **cause) +session_create(const char *name, int argc, char **argv, const char *path, + int cwd, struct environ *env, struct termios *tio, int idx, u_int sx, + u_int sy, char **cause) { struct session *s; + struct winlink *wl; s = xmalloc(sizeof *s); s->references = 0; @@ -131,8 +132,9 @@ session_create(const char *name, const char *cmd, const char *path, int cwd, } RB_INSERT(sessions, &sessions, s); - if (cmd != NULL) { - if (session_new(s, NULL, cmd, path, cwd, idx, cause) == NULL) { + if (argc >= 0) { + wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause); + if (wl == NULL) { session_destroy(s); return (NULL); } @@ -226,7 +228,7 @@ session_previous_session(struct session *s) /* Create a new window on a session. */ struct winlink * -session_new(struct session *s, const char *name, const char *cmd, +session_new(struct session *s, const char *name, int argc, char **argv, const char *path, int cwd, int idx, char **cause) { struct window *w; @@ -250,8 +252,8 @@ session_new(struct session *s, const char *name, const char *cmd, shell = _PATH_BSHELL; hlimit = options_get_number(&s->options, "history-limit"); - w = window_create(name, cmd, path, shell, cwd, &env, s->tio, s->sx, - s->sy, hlimit, cause); + w = window_create(name, argc, argv, path, shell, cwd, &env, s->tio, + s->sx, s->sy, hlimit, cause); if (w == NULL) { winlink_remove(&s->windows, wl); environ_free(&env); diff --git a/style.c b/style.c index 99744086..2a049fb6 100644 --- a/style.c +++ b/style.c @@ -117,7 +117,7 @@ style_tostring(struct grid_cell *gc) *s = '\0'; - if (gc->fg != 8) { + if (gc->fg != 8 || gc->flags & GRID_FLAG_FG256) { if (gc->flags & GRID_FLAG_FG256) c = gc->fg | 0x100; else @@ -126,7 +126,7 @@ style_tostring(struct grid_cell *gc) comma = 1; } - if (gc->bg != 8) { + if (gc->bg != 8 || gc->flags & GRID_FLAG_BG256) { if (gc->flags & GRID_FLAG_BG256) c = gc->bg | 0x100; else @@ -221,13 +221,13 @@ style_apply_update(struct grid_cell *gc, struct options *oo, const char *name) struct grid_cell *gcp; gcp = options_get_style(oo, name); - if (gcp->fg != 8) { + if (gcp->fg != 8 || gcp->flags & GRID_FLAG_FG256) { if (gcp->flags & GRID_FLAG_FG256) colour_set_fg(gc, gcp->fg | 0x100); else colour_set_fg(gc, gcp->fg); } - if (gcp->bg != 8) { + if (gcp->bg != 8 || gcp->flags & GRID_FLAG_BG256) { if (gcp->flags & GRID_FLAG_BG256) colour_set_bg(gc, gcp->bg | 0x100); else diff --git a/tmux.1 b/tmux.1 index c05eacfd..30447edb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -482,12 +482,37 @@ It may be used alone to target a pane or the window containing it. arguments are .Xr sh 1 commands. -These must be passed as a single item, which typically means quoting them, for -example: +This may be a single argument passed to the shell, for example: .Bd -literal -offset indent new-window 'vi /etc/passwd' .Ed .Pp +Will run: +.Bd -literal -offset indent +/bin/sh -c 'vi /etc/passwd' +.Ed +.Pp +Additionally, the +.Ic new-window , +.Ic new-session , +.Ic split-window , +.Ic respawn-window +and +.Ic respawn-pane +commands allow +.Ar shell-command +to be given as multiple arguments and executed directly (without +.Ql sh -c ) . +This can avoid issues with shell quoting. +For example: +.Bd -literal -offset indent +$ tmux new-window vi /etc/passwd +.Ed +.Pp +Will run +.Xr vi 1 +directly without invoking the shell. +.Pp .Ar command .Op Ar arguments refers to a @@ -853,6 +878,7 @@ The following keys are supported as appropriate for the mode: .It Sy "Function" Ta Sy "vi" Ta Sy "emacs" .It Li "Append selection" Ta "A" Ta "" .It Li "Back to indentation" Ta "^" Ta "M-m" +.It Li "Copy to named buffer" Ta \&" Ta "" .It Li "Bottom of history" Ta "G" Ta "M-<" .It Li "Clear selection" Ta "Escape" Ta "C-g" .It Li "Copy selection" Ta "Enter" Ta "M-w" @@ -934,9 +960,6 @@ in emacs mode, and .Ql 10w in vi. .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: .Em vi-edit and @@ -1094,7 +1117,7 @@ but a different format may be specified with .Fl F . .It Xo Ic capture-pane .Op Fl aepPq -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Op Fl E Ar end-line .Op Fl S Ar start-line .Op Fl t Ar target-pane @@ -3371,19 +3394,40 @@ is given, otherwise the active pane for the session attached to .El .Sh BUFFERS .Nm -maintains a stack of +maintains a set of named .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 -option are kept; when a new buffer is added, the buffer at the bottom of the -stack is removed. +option is reached, the oldest automatically named buffer is deleted. +Explicitly named are not subject to +.Ic buffer-limit +and may be deleted with +.Ic delete-buffer +command. +.Pp Buffers may be added using .Ic copy-mode or the .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 command. +If a buffer command is used and no buffer is specified, the most +recently added automatically named buffer is assumed. .Pp A configurable history buffer is also maintained for each window. By default, up to 2000 lines are kept; this can be altered with the @@ -3404,7 +3448,7 @@ Put a window into buffer choice mode, where a buffer may be chosen interactively from a list. After a buffer is selected, .Ql %% -is replaced by the buffer index in +is replaced by the buffer name in .Ar template and the result executed as a command. If @@ -3419,11 +3463,11 @@ This command works only if at least one client is attached. .It Ic clear-history Op Fl t Ar target-pane .D1 (alias: Ic clearhist ) 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 ) -Delete the buffer at -.Ar buffer-index , -or the top buffer if not specified. +Delete the buffer named +.Ar buffer-name , +or the most recently added automatically named buffer if not specified. .It Xo Ic list-buffers .Op Fl F Ar format .Xc @@ -3435,7 +3479,7 @@ flag, see the .Sx FORMATS section. .It Xo Ic load-buffer -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Ar path .Xc .D1 (alias: Ic loadb ) @@ -3443,7 +3487,7 @@ Load the contents of the specified paste buffer from .Ar path . .It Xo Ic paste-buffer .Op Fl dpr -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Op Fl s Ar separator .Op Fl t Ar target-pane .Xc @@ -3452,7 +3496,7 @@ Insert the contents of a paste buffer into the specified pane. If not specified, paste into the current one. With .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 a separator, by default carriage return (CR). A custom separator may be specified using the @@ -3467,7 +3511,7 @@ is specified, paste bracket control codes are inserted around the buffer if the application has requested bracketed paste mode. .It Xo Ic save-buffer .Op Fl a -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Ar path .Xc .D1 (alias: Ic saveb ) @@ -3478,7 +3522,8 @@ The option appends to rather than overwriting the file. .It Xo Ic set-buffer .Op Fl a -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name +.Op Fl n Ar new-buffer-name .Ar data .Xc .D1 (alias: Ic setb ) @@ -3487,8 +3532,12 @@ Set the contents of the specified buffer to The .Fl a 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 -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Xc .D1 (alias: Ic showb ) Display the contents of the specified buffer. diff --git a/tmux.h b/tmux.h index fde94afc..1f80f41f 100644 --- a/tmux.h +++ b/tmux.h @@ -82,7 +82,7 @@ extern char **environ; /* Default template for choose-buffer. */ #define CHOOSE_BUFFER_TEMPLATE \ - "#{line}: #{buffer_size} bytes: #{buffer_sample}" + "#{buffer_name}: #{buffer_size} bytes: #{buffer_sample}" /* Default template for choose-client. */ #define CHOOSE_CLIENT_TEMPLATE \ @@ -115,7 +115,8 @@ extern char **environ; /* Default template for list-buffers. */ #define LIST_BUFFERS_TEMPLATE \ - "#{line}: #{buffer_size} bytes: \"#{buffer_sample}\"" + "#{buffer_name}: #{buffer_size} bytes: " \ + "\"#{buffer_sample}\"" /* Default template for list-clients. */ #define LIST_CLIENTS_TEMPLATE \ @@ -579,6 +580,7 @@ enum mode_key_cmd { MODEKEYCOPY_SEARCHREVERSE, MODEKEYCOPY_SEARCHUP, MODEKEYCOPY_SELECTLINE, + MODEKEYCOPY_STARTNAMEDBUFFER, MODEKEYCOPY_STARTNUMBERPREFIX, MODEKEYCOPY_STARTOFLINE, MODEKEYCOPY_STARTSELECTION, @@ -889,6 +891,7 @@ struct window_choose_mode_item { /* Child window structure. */ struct window_pane { u_int id; + u_int active_point; struct window *window; @@ -908,7 +911,8 @@ struct window_pane { #define PANE_RESIZE 0x8 #define PANE_FOCUSPUSH 0x10 - char *cmd; + int argc; + char **argv; char *shell; int cwd; @@ -945,6 +949,7 @@ struct window_pane { }; TAILQ_HEAD(window_panes, window_pane); RB_HEAD(window_pane_tree, window_pane); +ARRAY_DECL(window_pane_list, struct window_pane *); /* Window structure. */ struct window { @@ -1022,8 +1027,6 @@ struct layout_cell { u_int yoff; struct window_pane *wp; - struct window_pane *lastwp; - struct layout_cells cells; TAILQ_ENTRY(layout_cell) entry; @@ -1033,6 +1036,13 @@ struct layout_cell { struct paste_buffer { char *data; size_t size; + + char *name; + int automatic; + u_int order; + + RB_ENTRY(paste_buffer) name_entry; + RB_ENTRY(paste_buffer) time_entry; }; /* Environment variable. */ @@ -1127,6 +1137,9 @@ LIST_HEAD(tty_terms, tty_term); #define MOUSE_WHEEL_UP 0 #define MOUSE_WHEEL_DOWN 1 +/* Mouse wheel multipler. */ +#define MOUSE_WHEEL_SCALE 3 + /* Mouse event bits. */ #define MOUSE_EVENT_DOWN 0x1 #define MOUSE_EVENT_DRAG 0x2 @@ -1493,7 +1506,7 @@ RB_HEAD(format_tree, format_entry); #define CMD_SRCDST_WINDOW_USAGE "[-s src-window] [-t dst-window]" #define CMD_SRCDST_SESSION_USAGE "[-s src-session] [-t dst-session]" #define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]" -#define CMD_BUFFER_USAGE "[-b buffer-index]" +#define CMD_BUFFER_USAGE "[-b buffer-name]" /* tmux.c */ extern struct options global_options; @@ -1705,13 +1718,14 @@ void tty_keys_free(struct tty *); int tty_keys_next(struct tty *); /* paste.c */ -struct paste_buffer *paste_walk_stack(u_int *); +struct paste_buffer *paste_walk(struct paste_buffer *); struct paste_buffer *paste_get_top(void); -struct paste_buffer *paste_get_index(u_int); +struct paste_buffer *paste_get_name(const char *); int paste_free_top(void); -int paste_free_index(u_int); -void paste_add(char *, size_t, u_int); -int paste_replace(u_int, char *, size_t); +int paste_free_name(const char *); +void paste_add(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); void paste_send_pane(struct paste_buffer *, struct window_pane *, const char *, int); @@ -1732,8 +1746,9 @@ long long args_strtonum( /* cmd.c */ int cmd_pack_argv(int, char **, char *, size_t); int cmd_unpack_argv(char *, size_t, int, char ***); -char **cmd_copy_argv(int, char *const *); +char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); +char *cmd_stringify_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); size_t cmd_print(struct cmd *, char *, size_t); struct session *cmd_current_session(struct cmd_q *, int); @@ -2124,7 +2139,7 @@ void winlink_stack_remove(struct winlink_stack *, struct winlink *); int window_index(struct window *, u_int *); struct window *window_find_by_id(u_int); struct window *window_create1(u_int, u_int); -struct window *window_create(const char *, const char *, const char *, +struct window *window_create(const char *, int, char **, const char *, const char *, int, struct environ *, struct termios *, u_int, u_int, u_int, char **); void window_destroy(struct window *); @@ -2150,7 +2165,7 @@ struct window_pane *window_pane_find_by_id(u_int); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); void window_pane_timer_start(struct window_pane *); -int window_pane_spawn(struct window_pane *, const char *, +int window_pane_spawn(struct window_pane *, int, char **, const char *, const char *, int, struct environ *, struct termios *, char **); void window_pane_resize(struct window_pane *, u_int, u_int); @@ -2288,18 +2303,18 @@ RB_PROTOTYPE(sessions, session, entry, session_cmp); int session_alive(struct session *); struct session *session_find(const char *); struct session *session_find_by_id(u_int); -struct session *session_create(const char *, const char *, const char *, int, - struct environ *, struct termios *, int, u_int, u_int, - char **); +struct session *session_create(const char *, int, char **, const char *, + int, struct environ *, struct termios *, int, u_int, + u_int, char **); void session_destroy(struct session *); int session_check_name(const char *); void session_update_activity(struct session *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); -struct winlink *session_new(struct session *, const char *, const char *, +struct winlink *session_new(struct session *, const char *, int, char **, const char *, int, int, char **); -struct winlink *session_attach( - struct session *, struct window *, int, char **); +struct winlink *session_attach(struct session *, struct window *, int, + char **); int session_detach(struct session *, struct winlink *); struct winlink *session_has(struct session *, struct window *); int session_next(struct session *, int); diff --git a/tty-keys.c b/tty-keys.c index 42f2da40..37b89600 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -752,11 +752,11 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) if (b & MOUSE_MASK_SHIFT) m->scroll = 1; else - m->scroll = 3; + m->scroll = MOUSE_WHEEL_SCALE; if (b & MOUSE_MASK_META) - m->scroll *= 3; + m->scroll *= MOUSE_WHEEL_SCALE; if (b & MOUSE_MASK_CTRL) - m->scroll *= 3; + m->scroll *= MOUSE_WHEEL_SCALE; b &= MOUSE_MASK_BUTTONS; if (b == 0) diff --git a/window-choose.c b/window-choose.c index 86f37ed5..23e77b32 100644 --- a/window-choose.c +++ b/window-choose.c @@ -721,17 +721,22 @@ window_choose_mouse(struct window_pane *wp, struct session *sess, struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct window_choose_mode_item *item; - u_int idx; + u_int idx, i, n; if (m->event == MOUSE_EVENT_WHEEL) { /* - * Don't use m->scroll and just move line-by-line or it's - * annoying. + * Multiple line scrolling by default is annoying, so scale + * m->scroll back down. */ - if (m->wheel == MOUSE_WHEEL_UP) - window_choose_key(wp, sess, KEYC_UP); - else - window_choose_key(wp, sess, KEYC_DOWN); + n = m->scroll; + if (n >= MOUSE_WHEEL_SCALE) + n /= MOUSE_WHEEL_SCALE; + for (i = 0; i < n; i++) { + if (m->wheel == MOUSE_WHEEL_UP) + window_choose_key(wp, sess, KEYC_UP); + else + window_choose_key(wp, sess, KEYC_DOWN); + } return; } diff --git a/window-copy.c b/window-copy.c index 508001d3..ac29e6d9 100644 --- a/window-copy.c +++ b/window-copy.c @@ -54,14 +54,15 @@ void window_copy_update_cursor(struct window_pane *, u_int, u_int); void window_copy_start_selection(struct window_pane *); int window_copy_update_selection(struct window_pane *, int); void *window_copy_get_selection(struct window_pane *, size_t *); -void window_copy_copy_buffer(struct window_pane *, int, void *, size_t); -void window_copy_copy_pipe( - struct window_pane *, struct session *, int, const char *); -void window_copy_copy_selection(struct window_pane *, int); -void window_copy_append_selection(struct window_pane *, int); +void window_copy_copy_buffer(struct window_pane *, const char *, void *, + size_t); +void window_copy_copy_pipe(struct window_pane *, struct session *, + const char *, const char *); +void window_copy_copy_selection(struct window_pane *, const char *); +void window_copy_append_selection(struct window_pane *, const char *); void window_copy_clear_selection(struct window_pane *); -void window_copy_copy_line( - struct window_pane *, char **, size_t *, u_int, u_int, u_int); +void window_copy_copy_line(struct window_pane *, char **, size_t *, u_int, + u_int, u_int); int window_copy_in_set(struct window_pane *, u_int, u_int, const char *); u_int window_copy_find_length(struct window_pane *, u_int); void window_copy_cursor_start_of_line(struct window_pane *); @@ -94,6 +95,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, @@ -417,7 +419,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) switch (cmd) { case MODEKEYCOPY_APPENDSELECTION: if (sess != NULL) { - window_copy_append_selection(wp, data->numprefix); + window_copy_append_selection(wp, NULL); window_pane_reset_mode(wp); return; } @@ -543,7 +545,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) if (sess != NULL && (cmd == MODEKEYCOPY_COPYLINE || cmd == MODEKEYCOPY_COPYENDOFLINE)) { - window_copy_copy_selection(wp, -1); + window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); return; } @@ -554,14 +556,14 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) break; case MODEKEYCOPY_COPYPIPE: if (sess != NULL) { - window_copy_copy_pipe(wp, sess, data->numprefix, arg); + window_copy_copy_pipe(wp, sess, NULL, arg); window_pane_reset_mode(wp); return; } break; case MODEKEYCOPY_COPYSELECTION: if (sess != NULL) { - window_copy_copy_selection(wp, data->numprefix); + window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); return; } @@ -676,6 +678,7 @@ window_copy_key(struct window_pane *wp, 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 +714,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 +822,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, data->inputstr); + *data->inputstr = '\0'; + window_pane_reset_mode(wp); + return (0); case WINDOW_COPY_GOTOLINE: window_copy_goto_line(wp, data->inputstr); *data->inputstr = '\0'; @@ -918,7 +931,7 @@ reset_mode: s->mode &= ~MODE_MOUSE_BUTTON; s->mode |= MODE_MOUSE_STANDARD; if (sess != NULL) { - window_copy_copy_selection(wp, -1); + window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); } } @@ -1452,9 +1465,9 @@ 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) +window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf, + size_t len) { - u_int limit; struct screen_write_ctx ctx; if (options_get_number(&global_options, "set-clipboard")) { @@ -1463,16 +1476,13 @@ 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"); - paste_add(buf, len, limit); - } else if (paste_replace(idx, buf, len) != 0) + if (paste_set(buf, len, bufname, NULL) != 0) free(buf); } void -window_copy_copy_pipe( - struct window_pane *wp, struct session *sess, int idx, const char *arg) +window_copy_copy_pipe(struct window_pane *wp, struct session *sess, + const char *bufname, const char *arg) { void *buf; size_t len; @@ -1486,11 +1496,11 @@ window_copy_copy_pipe( job = job_run(arg, sess, NULL, NULL, NULL); bufferevent_write(job->event, buf, len); - window_copy_copy_buffer(wp, idx, buf, len); + window_copy_copy_buffer(wp, bufname, buf, len); } void -window_copy_copy_selection(struct window_pane *wp, int idx) +window_copy_copy_selection(struct window_pane *wp, const char *bufname) { void* buf; size_t len; @@ -1499,17 +1509,16 @@ window_copy_copy_selection(struct window_pane *wp, int idx) if (buf == NULL) return; - window_copy_copy_buffer(wp, idx, buf, len); + window_copy_copy_buffer(wp, bufname, buf, len); } void -window_copy_append_selection(struct window_pane *wp, int idx) +window_copy_append_selection(struct window_pane *wp, const char *bufname) { - char *buf; - struct paste_buffer *pb; - size_t len; - u_int limit; - struct screen_write_ctx ctx; + char *buf; + struct paste_buffer *pb; + size_t len; + struct screen_write_ctx ctx; buf = window_copy_get_selection(wp, &len); if (buf == NULL) @@ -1521,24 +1530,19 @@ window_copy_append_selection(struct window_pane *wp, int idx) screen_write_stop(&ctx); } - if (idx == -1) - idx = 0; - - if (idx == 0 && paste_get_top() == NULL) { - limit = options_get_number(&global_options, "buffer-limit"); - paste_add(buf, len, limit); - return; - } - - pb = paste_get_index(idx); + if (bufname == NULL || *bufname == '\0') { + pb = paste_get_top(); + if (pb != NULL) + bufname = pb->name; + } else + pb = paste_get_name(bufname); if (pb != NULL) { buf = xrealloc(buf, 1, len + pb->size); memmove(buf + pb->size, buf, len); memcpy(buf, pb->data, pb->size); len += pb->size; } - - if (paste_replace(idx, buf, len) != 0) + if (paste_set(buf, len, bufname, NULL) != 0) free(buf); } diff --git a/window.c b/window.c index ee851bde..d65bf503 100644 --- a/window.c +++ b/window.c @@ -55,15 +55,14 @@ struct windows windows; struct window_pane_tree all_window_panes; u_int next_window_pane_id; u_int next_window_id; - -struct window_pane *window_pane_active_set(struct window_pane *, - struct window_pane *); -void window_pane_active_lost(struct window_pane *, struct window_pane *); +u_int next_active_point; void window_pane_timer_callback(int, short, void *); void window_pane_read_callback(struct bufferevent *, void *); void window_pane_error_callback(struct bufferevent *, short, void *); +struct window_pane *window_pane_choose_best(struct window_pane_list *); + RB_GENERATE(winlinks, winlink, entry, winlink_cmp); int @@ -307,7 +306,7 @@ window_create1(u_int sx, u_int sy) } struct window * -window_create(const char *name, const char *cmd, const char *path, +window_create(const char *name, int argc, char **argv, const char *path, const char *shell, int cwd, struct environ *env, struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause) { @@ -318,7 +317,7 @@ window_create(const char *name, const char *cmd, const char *path, wp = window_add_pane(w, hlimit); layout_init(w, wp); - if (window_pane_spawn(wp, cmd, path, shell, cwd, env, tio, + if (window_pane_spawn(wp, argc, argv, path, shell, cwd, env, tio, cause) != 0) { window_destroy(w); return (NULL); @@ -386,64 +385,6 @@ window_resize(struct window *w, u_int sx, u_int sy) w->sy = sy; } -/* - * Restore previously active pane when changing from wp to nextwp. The intended - * pane is in nextwp and it returns the previously focused pane. - */ -struct window_pane * -window_pane_active_set(struct window_pane *wp, struct window_pane *nextwp) -{ - struct layout_cell *lc; - struct window_pane *lastwp; - - /* Target pane's parent must not be an ancestor of source pane. */ - for (lc = wp->layout_cell->parent; lc != NULL; lc = lc->parent) { - if (lc == nextwp->layout_cell->parent) - return (nextwp); - } - - /* - * Previously active pane, if any, must not be the same as the source - * pane. - */ - lc = nextwp->layout_cell->parent; - if (lc != NULL && lc->lastwp != NULL) { - lastwp = lc->lastwp; - if (lastwp != wp && window_pane_visible(lastwp)) - return (lastwp); - } - return (nextwp); -} - -/* Remember previously active pane when changing from wp to nextwp. */ -void -window_pane_active_lost(struct window_pane *wp, struct window_pane *nextwp) -{ - struct layout_cell *lc, *lc2, *lcparent; - - /* Get the parent cell. */ - lcparent = nextwp->layout_cell->parent; - if (lcparent == NULL) - return; - - /* Save the target pane in its parent. */ - lcparent->lastwp = nextwp; - - /* - * Save the source pane in all of its parents up to, but not including, - * the common ancestor of itself and the target panes. - */ - if (wp == NULL) - return; - for (lc = wp->layout_cell->parent; lc != NULL; lc = lc->parent) { - for (lc2 = lcparent; lc2 != NULL; lc2 = lc2->parent) { - if (lc == lc2) - return; - } - lc->lastwp = wp; - } -} - void window_set_active_pane(struct window *w, struct window_pane *wp) { @@ -451,7 +392,6 @@ window_set_active_pane(struct window *w, struct window_pane *wp) return; w->last = w->active; w->active = wp; - window_pane_active_lost(w->last, wp); while (!window_pane_visible(w->active)) { w->active = TAILQ_PREV(w->active, window_panes, entry); if (w->active == NULL) @@ -459,6 +399,7 @@ window_set_active_pane(struct window *w, struct window_pane *wp) if (w->active == wp) return; } + w->active->active_point = next_active_point++; } struct window_pane * @@ -736,7 +677,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); - wp->cmd = NULL; + wp->argc = 0; + wp->argv = NULL; wp->shell = NULL; wp->cwd = -1; @@ -770,16 +712,6 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) void window_pane_destroy(struct window_pane *wp) { - struct window_pane *wp2; - - /* Forget removed pane in all layout cells that remember it. */ - RB_FOREACH(wp2, window_pane_tree, &all_window_panes) { - if (wp2->layout_cell != NULL && - wp2->layout_cell->parent != NULL && - wp2->layout_cell->parent->lastwp == wp) - wp2->layout_cell->parent->lastwp = NULL; - } - window_pane_reset_mode(wp); if (event_initialized(&wp->changes_timer)) @@ -808,30 +740,32 @@ window_pane_destroy(struct window_pane *wp) close(wp->cwd); free(wp->shell); - free(wp->cmd); + cmd_free_argv(wp->argc, wp->argv); free(wp); } int -window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path, - const char *shell, int cwd, struct environ *env, struct termios *tio, - char **cause) +window_pane_spawn(struct window_pane *wp, int argc, char **argv, + const char *path, const char *shell, int cwd, struct environ *env, + struct termios *tio, char **cause) { struct winsize ws; - char *argv0, paneid[16]; - const char *ptr; + char *argv0, *cmd, **argvp, paneid[16]; + const char *ptr, *first; struct termios tio2; #ifdef HAVE_UTEMPTER char s[32]; #endif + int i; if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); } - if (cmd != NULL) { - free(wp->cmd); - wp->cmd = xstrdup(cmd); + if (argc > 0) { + cmd_free_argv(wp->argc, wp->argv); + wp->argc = argc; + wp->argv = cmd_copy_argv(argc, argv); } if (shell != NULL) { free(wp->shell); @@ -842,7 +776,10 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path, wp->cwd = dup(cwd); } - log_debug("spawn: %s -- %s", wp->shell, wp->cmd); + cmd = cmd_stringify_argv(wp->argc, wp->argv); + log_debug("spawn: %s -- %s", wp->shell, cmd); + for (i = 0; i < wp->argc; i++) + log_debug("spawn: argv[%d] = %s", i, wp->argv[i]); memset(&ws, 0, sizeof ws); ws.ws_col = screen_size_x(&wp->base); @@ -852,6 +789,7 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path, case -1: wp->fd = -1; xasprintf(cause, "%s: %s", cmd, strerror(errno)); + free(cmd); return (-1); case 0: if (fchdir(wp->cwd) != 0) @@ -883,22 +821,32 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path, setenv("SHELL", wp->shell, 1); ptr = strrchr(wp->shell, '/'); - if (*wp->cmd != '\0') { - /* Use the command. */ + /* + * If given one argument, assume it should be passed to sh -c; + * with more than one argument, use execvp(). If there is no + * arguments, create a login shell. + */ + if (wp->argc > 0) { + if (wp->argc != 1) { + /* Copy to ensure argv ends in NULL. */ + argvp = cmd_copy_argv(wp->argc, wp->argv); + execvp(argvp[0], argvp); + fatal("execvp failed"); + } + first = wp->argv[0]; + if (ptr != NULL && *(ptr + 1) != '\0') xasprintf(&argv0, "%s", ptr + 1); else xasprintf(&argv0, "%s", wp->shell); - execl(wp->shell, argv0, "-c", wp->cmd, (char *) NULL); + execl(wp->shell, argv0, "-c", first, (char *)NULL); fatal("execl failed"); } - - /* No command; fork a login shell. */ if (ptr != NULL && *(ptr + 1) != '\0') xasprintf(&argv0, "-%s", ptr + 1); else xasprintf(&argv0, "-%s", wp->shell); - execl(wp->shell, argv0, (char *) NULL); + execl(wp->shell, argv0, (char *)NULL); fatal("execl failed"); } @@ -909,10 +857,11 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *path, setblocking(wp->fd, 0); - wp->event = bufferevent_new(wp->fd, - window_pane_read_callback, NULL, window_pane_error_callback, wp); + wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, + window_pane_error_callback, wp); bufferevent_enable(wp->event, EV_READ|EV_WRITE); + free(cmd); return (0); } @@ -1201,114 +1150,198 @@ window_pane_search(struct window_pane *wp, const char *searchstr, return (msg); } -/* Find the pane directly above another. */ +/* Get MRU pane from a list. */ +struct window_pane * +window_pane_choose_best(struct window_pane_list *list) +{ + struct window_pane *next, *best; + u_int i; + + if (ARRAY_LENGTH(list) == 0) + return (NULL); + + best = ARRAY_FIRST(list); + for (i = 1; i < ARRAY_LENGTH(list); i++) { + next = ARRAY_ITEM(list, i); + if (next->active_point > best->active_point) + best = next; + } + return (best); +} + +/* + * Find the pane directly above another. We build a list of those adjacent to + * top edge and then choose the best. + */ struct window_pane * window_pane_find_up(struct window_pane *wp) { - struct window_pane *wp2; - u_int left, top; + struct window_pane *next, *best; + u_int edge, left, right, end; + struct window_pane_list list; + int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); + ARRAY_INIT(&list); + + edge = wp->yoff; + if (edge == 0) + edge = wp->window->sy + 1; - top = wp->yoff; - if (top == 0) - top = wp->window->sy + 1; left = wp->xoff; + right = wp->xoff + wp->sx; - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (!window_pane_visible(wp2)) + TAILQ_FOREACH(next, &wp->window->panes, entry) { + if (next == wp || !window_pane_visible(next)) continue; - if (wp2->yoff + wp2->sy + 1 != top) + if (next->yoff + next->sy + 1 != edge) continue; - if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) - return (window_pane_active_set(wp, wp2)); + end = next->xoff + next->sx - 1; + + found = 0; + if (next->xoff < left && end > right) + found = 1; + else if (next->xoff >= left && next->xoff <= right) + found = 1; + else if (end >= left && end <= right) + found = 1; + if (found) + ARRAY_ADD(&list, next); } - return (NULL); + + best = window_pane_choose_best(&list); + ARRAY_FREE(&list); + return (best); } /* Find the pane directly below another. */ struct window_pane * window_pane_find_down(struct window_pane *wp) { - struct window_pane *wp2; - u_int left, bottom; + struct window_pane *next, *best; + u_int edge, left, right, end; + struct window_pane_list list; + int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); + ARRAY_INIT(&list); + + edge = wp->yoff + wp->sy + 1; + if (edge >= wp->window->sy) + edge = 0; - bottom = wp->yoff + wp->sy + 1; - if (bottom >= wp->window->sy) - bottom = 0; left = wp->xoff; + right = wp->xoff + wp->sx; - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (!window_pane_visible(wp2)) + TAILQ_FOREACH(next, &wp->window->panes, entry) { + if (next == wp || !window_pane_visible(next)) continue; - if (wp2->yoff != bottom) + if (next->yoff != edge) continue; - if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) - return (window_pane_active_set(wp, wp2)); + end = next->xoff + next->sx - 1; + + found = 0; + if (next->xoff < left && end > right) + found = 1; + else if (next->xoff >= left && next->xoff <= right) + found = 1; + else if (end >= left && end <= right) + found = 1; + if (found) + ARRAY_ADD(&list, next); } - return (NULL); + + best = window_pane_choose_best(&list); + ARRAY_FREE(&list); + return (best); } -/* - * Find the pane directly to the left of another, adjacent to the left side and - * containing the top edge. - */ +/* Find the pane directly to the left of another. */ struct window_pane * window_pane_find_left(struct window_pane *wp) { - struct window_pane *wp2; - u_int left, top; + struct window_pane *next, *best; + u_int edge, top, bottom, end; + struct window_pane_list list; + int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); + ARRAY_INIT(&list); + + edge = wp->xoff; + if (edge == 0) + edge = wp->window->sx + 1; - left = wp->xoff; - if (left == 0) - left = wp->window->sx + 1; top = wp->yoff; + bottom = wp->yoff + wp->sy; - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (!window_pane_visible(wp2)) + TAILQ_FOREACH(next, &wp->window->panes, entry) { + if (next == wp || !window_pane_visible(next)) continue; - if (wp2->xoff + wp2->sx + 1 != left) + if (next->xoff + next->sx + 1 != edge) continue; - if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) - return (window_pane_active_set(wp, wp2)); + end = next->yoff + next->sy - 1; + + found = 0; + if (next->yoff < top && end > bottom) + found = 1; + else if (next->yoff >= top && next->yoff <= bottom) + found = 1; + else if (end >= top && end <= bottom) + found = 1; + if (found) + ARRAY_ADD(&list, next); } - return (NULL); + + best = window_pane_choose_best(&list); + ARRAY_FREE(&list); + return (best); } -/* - * Find the pane directly to the right of another, that is adjacent to the - * right edge and including the top edge. - */ +/* Find the pane directly to the right of another. */ struct window_pane * window_pane_find_right(struct window_pane *wp) { - struct window_pane *wp2; - u_int right, top; + struct window_pane *next, *best; + u_int edge, top, bottom, end; + struct window_pane_list list; + int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); + ARRAY_INIT(&list); + + edge = wp->xoff + wp->sx + 1; + if (edge >= wp->window->sx) + edge = 0; - right = wp->xoff + wp->sx + 1; - if (right >= wp->window->sx) - right = 0; top = wp->yoff; + bottom = wp->yoff + wp->sy; - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (!window_pane_visible(wp2)) + TAILQ_FOREACH(next, &wp->window->panes, entry) { + if (next == wp || !window_pane_visible(next)) continue; - if (wp2->xoff != right) + if (next->xoff != edge) continue; - if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) - return (window_pane_active_set(wp, wp2)); + end = next->yoff + next->sy - 1; + + found = 0; + if (next->yoff < top && end > bottom) + found = 1; + else if (next->yoff >= top && next->yoff <= bottom) + found = 1; + else if (end >= top && end <= bottom) + found = 1; + if (found) + ARRAY_ADD(&list, next); } - return (NULL); + + best = window_pane_choose_best(&list); + ARRAY_FREE(&list); + return (best); } /* Clear alert flags for a winlink */