diff options
-rw-r--r-- | CHANGES | 16 | ||||
-rw-r--r-- | COPYING | 7 | ||||
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | TODO | 4 | ||||
-rw-r--r-- | cfg.c | 33 | ||||
-rw-r--r-- | cmd-attach-session.c | 2 | ||||
-rw-r--r-- | cmd-join-pane.c | 10 | ||||
-rw-r--r-- | cmd-kill-session.c | 4 | ||||
-rw-r--r-- | cmd-new-session.c | 105 | ||||
-rw-r--r-- | cmd-new-window.c | 109 | ||||
-rw-r--r-- | cmd-queue.c | 2 | ||||
-rw-r--r-- | cmd-respawn-pane.c | 60 | ||||
-rw-r--r-- | cmd-respawn-window.c | 72 | ||||
-rw-r--r-- | cmd-rotate-window.c | 4 | ||||
-rw-r--r-- | cmd-select-pane.c | 4 | ||||
-rw-r--r-- | cmd-set-option.c | 2 | ||||
-rw-r--r-- | cmd-show-options.c | 132 | ||||
-rw-r--r-- | cmd-split-window.c | 139 | ||||
-rw-r--r-- | cmd-swap-pane.c | 10 | ||||
-rw-r--r-- | cmd-switch-client.c | 4 | ||||
-rw-r--r-- | cmd.c | 19 | ||||
-rw-r--r-- | compat.h | 1 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | environ.c | 10 | ||||
-rw-r--r-- | format.c | 17 | ||||
-rw-r--r-- | input.c | 2 | ||||
-rw-r--r-- | layout.c | 20 | ||||
-rw-r--r-- | options-table.c | 15 | ||||
-rw-r--r-- | options.c | 209 | ||||
-rw-r--r-- | osdep-darwin.c | 3 | ||||
-rw-r--r-- | paste.c | 9 | ||||
-rw-r--r-- | screen-write.c | 2 | ||||
-rw-r--r-- | server-client.c | 1 | ||||
-rw-r--r-- | server-fn.c | 4 | ||||
-rw-r--r-- | server.c | 2 | ||||
-rw-r--r-- | session.c | 68 | ||||
-rw-r--r-- | spawn.c | 432 | ||||
-rw-r--r-- | status.c | 16 | ||||
-rw-r--r-- | style.c | 2 | ||||
-rw-r--r-- | tmux.1 | 51 | ||||
-rw-r--r-- | tmux.h | 79 | ||||
-rw-r--r-- | tty-keys.c | 11 | ||||
-rw-r--r-- | tty-term.c | 7 | ||||
-rw-r--r-- | tty.c | 8 | ||||
-rw-r--r-- | window-buffer.c | 16 | ||||
-rw-r--r-- | window-copy.c | 1550 | ||||
-rw-r--r-- | window-tree.c | 2 | ||||
-rw-r--r-- | window.c | 227 |
48 files changed, 2215 insertions, 1290 deletions
@@ -1,3 +1,19 @@ +CHANGES FROM 2.9 to X.X + +* Add an argument to copy commands to set the prefix for the buffer name, this + (for example) allows buffers for different sessions to be named separately. + +* Update session activity on focus event. + +* Pass target from source-file into the config file parser so formats in %if + and %endif have access to more useful variables. + +* Add the ability to infer an option type (server, session, window) from its + name to show-options (it was already present in set-option). + +* Allow the prefix used for new buffer names to be given as an argument to the + copy commands in copy mode. + CHANGES FROM 2.8 to 2.9 * Attempt to preserve horizontal cursor position as well as vertical with @@ -1,10 +1,7 @@ THIS IS FOR INFORMATION ONLY, CODE IS UNDER THE LICENCE AT THE TOP OF ITS FILE. -The README, CHANGES, FAQ and TODO files are licensed under the ISC -license. Files under examples/ remain copyright their authors unless otherwise -stated in the file but permission has been received to distribute them with -tmux. All other files have a license and copyright notice at their start, -typically: +The README, CHANGES, FAQ and TODO files are licensed under the ISC license. All +other files have a license and copyright notice at their start, typically: Copyright (c) <author> diff --git a/Makefile.am b/Makefile.am index 16c842c8..942a9e28 100644 --- a/Makefile.am +++ b/Makefile.am @@ -160,6 +160,7 @@ dist_tmux_SOURCES = \ server-fn.c \ server.c \ session.c \ + spawn.c \ status.c \ style.c \ tmux.c \ @@ -52,7 +52,6 @@ need to be suspended * allow the prefix for automatic buffers to be specified as part of the key binding to allow session buffers or similar (GitHub issue 1501) - * copy-pipe should be synchronous (GitHub issue 1517) - layout stuff * way to tag a layout as a number/name @@ -149,8 +148,6 @@ TODO soonish maybe: - Store hooks as options, issue 1619. -- Support buffer prefixes, issue 1501. -- copy-pipe should be synchronous, issue 1517. - -E flag to pass environment to new-*, issue 1498. - Copy mode searching is slow when there is a big history, issue 1545. - Grid "block" stuff, issue 1269. Can be used potentially for compression of @@ -159,3 +156,4 @@ TODO soonish maybe: collected LRU. - Command aliases should be able to override builtin commands in order to add default flags (some mechanism needed to avoid recursion). GitHub issue 1630. +- Merge alternate and normal screen modes: https://gitlab.freedesktop.org/terminal-wg/specifications/issues/5#note_112220 @@ -101,7 +101,8 @@ start_cfg(void) cmdq_append(c, cfg_item); } - load_cfg(TMUX_CONF, NULL, NULL, 1); + if (cfg_file == NULL) + load_cfg(TMUX_CONF, NULL, NULL, 1); if (cfg_file == NULL && (home = find_home()) != NULL) { xasprintf(&cfg_file, "%s/.tmux.conf", home); @@ -114,7 +115,8 @@ start_cfg(void) } static int -cfg_check_condition(const char *path, size_t line, const char *p, int *skip) +cfg_check_cond(const char *path, size_t line, const char *p, int *skip, + struct client *c, struct cmd_find_state *fs) { struct format_tree *ft; char *s; @@ -129,6 +131,10 @@ cfg_check_condition(const char *path, size_t line, const char *p, int *skip) } ft = format_create(NULL, NULL, FORMAT_NONE, FORMAT_NOJOBS); + if (fs != NULL) + format_defaults(ft, c, fs->s, fs->wl, fs->wp); + else + format_defaults(ft, c, NULL, NULL, NULL); s = format_expand(ft, p); result = format_true(s); free(s); @@ -140,7 +146,7 @@ cfg_check_condition(const char *path, size_t line, const char *p, int *skip) static void cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds, - const char *p) + const char *p, struct client *c, struct cmd_find_state *fs) { struct cfg_cond *cond; struct cfg_cond *parent = TAILQ_FIRST(conds); @@ -152,7 +158,7 @@ cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds, cond = xcalloc(1, sizeof *cond); cond->line = line; if (parent == NULL || parent->met) - cond->met = cfg_check_condition(path, line, p, &cond->skip); + cond->met = cfg_check_cond(path, line, p, &cond->skip, c, fs); else cond->skip = 1; cond->saw_else = 0; @@ -161,7 +167,7 @@ cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds, static void cfg_handle_elif(const char *path, size_t line, struct cfg_conds *conds, - const char *p) + const char *p, struct client *c, struct cmd_find_state *fs) { struct cfg_cond *cond = TAILQ_FIRST(conds); @@ -172,7 +178,7 @@ cfg_handle_elif(const char *path, size_t line, struct cfg_conds *conds, if (cond == NULL || cond->saw_else) cfg_add_cause("%s:%zu: unexpected %%elif", path, line); else if (!cond->skip) - cond->met = cfg_check_condition(path, line, p, &cond->skip); + cond->met = cfg_check_cond(path, line, p, &cond->skip, c, fs); else cond->met = 0; } @@ -213,16 +219,16 @@ cfg_handle_endif(const char *path, size_t line, struct cfg_conds *conds) static void cfg_handle_directive(const char *p, const char *path, size_t line, - struct cfg_conds *conds) + struct cfg_conds *conds, struct client *c, struct cmd_find_state *fs) { int n = 0; while (p[n] != '\0' && !isspace((u_char)p[n])) n++; if (strncmp(p, "%if", n) == 0) - cfg_handle_if(path, line, conds, p + n); + cfg_handle_if(path, line, conds, p + n, c, fs); else if (strncmp(p, "%elif", n) == 0) - cfg_handle_elif(path, line, conds, p + n); + cfg_handle_elif(path, line, conds, p + n, c, fs); else if (strcmp(p, "%else") == 0) cfg_handle_else(path, line, conds); else if (strcmp(p, "%endif") == 0) @@ -243,6 +249,13 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet) struct cmdq_item *new_item; struct cfg_cond *cond, *cond1; struct cfg_conds conds; + struct cmd_find_state *fs = NULL; + struct client *fc = NULL; + + if (item != NULL) { + fs = &item->target; + fc = cmd_find_client(item, NULL, 1); + } TAILQ_INIT(&conds); @@ -269,7 +282,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet) *q-- = '\0'; if (*p == '%') { - cfg_handle_directive(p, path, line, &conds); + cfg_handle_directive(p, path, line, &conds, fc, fs); continue; } cond = TAILQ_FIRST(&conds); diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 73ff530d..bcd6d895 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -87,7 +87,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (wl != NULL) { if (wp != NULL) - window_set_active_pane(wp->window, wp); + window_set_active_pane(wp->window, wp, 1); session_set_current(s, wl); if (wp != NULL) cmd_find_from_winlink_pane(current, wl, wp, 0); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 947b2499..3d798e09 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -71,7 +71,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) int size, percentage, dst_idx; enum layout_type type; struct layout_cell *lc; - int not_same_window; + int not_same_window, flags; if (self->entry == &cmd_join_pane_entry) not_same_window = 1; @@ -123,7 +123,11 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) else size = (dst_wp->sx * percentage) / 100; } - lc = layout_split_pane(dst_wp, type, size, args_has(args, 'b'), 0); + if (args_has(args, 'b')) + flags = SPAWN_BEFORE; + else + flags = 0; + lc = layout_split_pane(dst_wp, type, size, flags); if (lc == NULL) { cmdq_error(item, "create pane failed: pane too small"); return (CMD_RETURN_ERROR); @@ -144,7 +148,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) server_redraw_window(dst_w); if (!args_has(args, 'd')) { - window_set_active_pane(dst_w, src_wp); + window_set_active_pane(dst_w, src_wp, 1); session_select(dst_s, dst_idx); cmd_find_from_session(current, dst_s, 0); server_redraw_session(dst_s); diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 00ea7789..dcef8097 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -61,12 +61,12 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item) RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) { if (sloop != s) { server_destroy_session(sloop); - session_destroy(sloop, __func__); + session_destroy(sloop, 1, __func__); } } } else { server_destroy_session(s); - session_destroy(s, __func__); + session_destroy(s, 1, __func__); } return (CMD_RETURN_NORMAL); } diff --git a/cmd-new-session.c b/cmd-new-session.c index e1be350b..f0a353d8 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -69,20 +69,17 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) struct args *args = self->args; struct client *c = item->client; struct session *s, *as, *groupwith; - struct window *w; struct environ *env; struct options *oo; struct termios tio, *tiop; struct session_group *sg; - const char *errstr, *template, *group, *prefix; - const char *path, *cmd, *tmp, *value; - char **argv, *cause, *cp, *newname, *cwd = NULL; - int detached, already_attached, idx, argc; - int is_control = 0; - u_int sx, sy, dsx = 80, dsy = 24; - struct environ_entry *envent; - struct cmd_find_state fs; + const char *errstr, *template, *group, *prefix, *tmp; + char *cause, *cwd = NULL, *cp, *newname = NULL; + int detached, already_attached, is_control = 0; + u_int sx, sy, dsx, dsy; + struct spawn_context sc; enum cmd_retval retval; + struct cmd_find_state fs; if (self->entry == &cmd_has_session_entry) { /* @@ -97,13 +94,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } - newname = NULL; if (args_has(args, 's')) { newname = format_single(item, args_get(args, 's'), c, NULL, NULL, NULL); if (!session_check_name(newname)) { cmdq_error(item, "bad session name: %s", newname); - goto error; + goto fail; } if ((as = session_find(newname)) != NULL) { if (args_has(args, 'A')) { @@ -114,7 +110,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) return (retval); } cmdq_error(item, "duplicate session: %s", newname); - goto error; + goto fail; } } @@ -125,7 +121,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (groupwith == NULL) { if (!session_check_name(group)) { cmdq_error(item, "bad group name: %s", group); - goto error; + goto fail; } sg = session_group_find(group); } else @@ -173,7 +169,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (server_client_check_nested(item->client)) { cmdq_error(item, "sessions should be nested with care, " "unset $TMUX to force"); - return (CMD_RETURN_ERROR); + goto fail; } if (tcgetattr(c->tty.fd, &tio) != 0) fatal("tcgetattr failed"); @@ -186,7 +182,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (server_client_open(c, &cause) != 0) { cmdq_error(item, "open terminal failed: %s", cause); free(cause); - goto error; + goto fail; } } @@ -200,7 +196,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) dsx = strtonum(tmp, 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "width %s", errstr); - goto error; + goto fail; } } } @@ -213,7 +209,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) dsy = strtonum(tmp, 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "height %s", errstr); - goto error; + goto fail; } } } @@ -225,8 +221,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (sy > 0 && options_get_number(global_s_options, "status")) sy--; } else { - value = options_get_string(global_s_options, "default-size"); - if (sscanf(value, "%ux%u", &sx, &sy) != 2) { + tmp = options_get_string(global_s_options, "default-size"); + if (sscanf(tmp, "%ux%u", &sx, &sy) != 2) { sx = 80; sy = 24; } @@ -240,59 +236,34 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) if (sy == 0) sy = 1; - /* Figure out the command for the new window. */ - argc = -1; - argv = NULL; - if (!args_has(args, 't') && args->argc != 0) { - argc = args->argc; - argv = args->argv; - } else if (sg == NULL && groupwith == NULL) { - cmd = options_get_string(global_s_options, "default-command"); - if (cmd != NULL && *cmd != '\0') { - argc = 1; - argv = (char **)&cmd; - } else { - argc = 0; - argv = NULL; - } - } - - path = NULL; - if (c != NULL && c->session == NULL) - envent = environ_find(c->environ, "PATH"); - else - envent = environ_find(global_environ, "PATH"); - if (envent != NULL) - path = envent->value; - - /* Construct the environment. */ + /* Create the new session. */ + oo = options_create(global_s_options); + if (args_has(args, 'x') || args_has(args, 'y')) + options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy); env = environ_create(); if (c != NULL && !args_has(args, 'E')) environ_update(global_s_options, c->environ, env); + s = session_create(prefix, newname, cwd, env, oo, tiop); - /* Set up the options. */ - oo = options_create(global_s_options); - if (args_has(args, 'x') || args_has(args, 'y')) - options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy); + /* Spawn the initial window. */ + memset(&sc, 0, sizeof sc); + sc.item = item; + sc.s = s; - /* Create the new session. */ - idx = -1 - options_get_number(global_s_options, "base-index"); - s = session_create(prefix, newname, argc, argv, path, cwd, env, oo, - tiop, idx, &cause); - environ_free(env); - if (s == NULL) { - cmdq_error(item, "create session failed: %s", cause); - free(cause); - goto error; - } + sc.name = args_get(args, 'n'); + sc.argc = args->argc; + sc.argv = args->argv; - /* Set the initial window name if one given. */ - if (argc >= 0 && (tmp = args_get(args, 'n')) != NULL) { - cp = format_single(item, tmp, c, s, NULL, NULL); - w = s->curw->window; - window_set_name(w, cp); - options_set_number(w->options, "automatic-rename", 0); - free(cp); + sc.idx = -1; + sc.cwd = args_get(args, 'c'); + + sc.flags = 0; + + if (spawn_window(&sc, &cause) == NULL) { + session_destroy(s, 0, __func__); + cmdq_error(item, "create window failed: %s", cause); + free(cause); + goto fail; } /* @@ -364,7 +335,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) free(newname); return (CMD_RETURN_NORMAL); -error: +fail: free(cwd); free(newname); return (CMD_RETURN_ERROR); diff --git a/cmd-new-window.c b/cmd-new-window.c index 5baeff65..e3bacff8 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -53,87 +53,45 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_find_state *current = &item->shared->current; + struct spawn_context sc; + struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; - struct client *c = cmd_find_client(item, NULL, 1); int idx = item->target.idx; - const char *cmd, *path, *template, *tmp; - char **argv, *cause, *cp, *cwd, *name; - int argc, detached; - struct environ_entry *envent; + struct winlink *new_wl; + char *cause = NULL, *cp; + const char *template; struct cmd_find_state fs; - if (args_has(args, 'a') && wl != NULL) { - if ((idx = winlink_shuffle_up(s, wl)) == -1) { - cmdq_error(item, "no free window indexes"); - return (CMD_RETURN_ERROR); - } - } - detached = args_has(args, 'd'); - - if (args->argc == 0) { - cmd = options_get_string(s->options, "default-command"); - 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, 'a') && (idx = winlink_shuffle_up(s, wl)) == -1) { + cmdq_error(item, "couldn't get a window index"); + return (CMD_RETURN_ERROR); } - path = NULL; - if (item->client != NULL && item->client->session == NULL) - envent = environ_find(item->client->environ, "PATH"); - else - envent = environ_find(s->environ, "PATH"); - if (envent != NULL) - path = envent->value; - - if ((tmp = args_get(args, 'c')) != NULL) - cwd = format_single(item, tmp, c, s, NULL, NULL); - else - cwd = xstrdup(server_client_get_cwd(item->client, s)); - - if ((tmp = args_get(args, 'n')) != NULL) - name = format_single(item, tmp, c, s, NULL, NULL); - else - name = NULL; - - if (idx != -1) - wl = winlink_find_by_index(&s->windows, idx); - if (wl != NULL && args_has(args, 'k')) { - /* - * Can't use session_detach as it will destroy session if this - * makes it empty. - */ - notify_session_window("window-unlinked", s, wl->window); - wl->flags &= ~WINLINK_ALERTFLAGS; - winlink_stack_remove(&s->lastw, wl); - winlink_remove(&s->windows, wl); - - /* Force select/redraw if current. */ - if (wl == s->curw) { - detached = 0; - s->curw = NULL; - } - } + memset(&sc, 0, sizeof sc); + sc.item = item; + sc.s = s; - if (idx == -1) - idx = -1 - options_get_number(s->options, "base-index"); - wl = session_new(s, name, argc, argv, path, cwd, idx, - &cause); - if (wl == NULL) { + sc.name = args_get(args, 'n'); + sc.argc = args->argc; + sc.argv = args->argv; + + sc.idx = idx; + sc.cwd = args_get(args, 'c'); + + sc.flags = 0; + if (args_has(args, 'd')) + sc.flags |= SPAWN_DETACHED; + if (args_has(args, 'k')) + sc.flags |= SPAWN_KILL; + + if ((new_wl = spawn_window(&sc, &cause)) == NULL) { cmdq_error(item, "create window failed: %s", cause); free(cause); - goto error; + return (CMD_RETURN_ERROR); } - if (!detached) { - session_select(s, wl->idx); - cmd_find_from_winlink(current, wl, 0); + if (!args_has(args, 'd') || new_wl == s->curw) { + cmd_find_from_winlink(current, new_wl, 0); server_redraw_session_group(s); } else server_status_session_group(s); @@ -141,20 +99,13 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; - cp = format_single(item, template, c, s, wl, NULL); + cp = format_single(item, template, c, s, new_wl, NULL); cmdq_print(item, "%s", cp); free(cp); } - cmd_find_from_winlink(&fs, wl, 0); + cmd_find_from_winlink(&fs, new_wl, 0); hooks_insert(s->hooks, item, &fs, "after-new-window"); - free(name); - free(cwd); return (CMD_RETURN_NORMAL); - -error: - free(name); - free(cwd); - return (CMD_RETURN_ERROR); } diff --git a/cmd-queue.c b/cmd-queue.c index 97b3c1c9..03fd5f10 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -111,7 +111,7 @@ cmdq_remove(struct cmdq_item *item) if (item->client != NULL) server_client_unref(item->client); - if (item->type == CMDQ_COMMAND) + if (item->cmdlist != NULL) cmd_list_free(item->cmdlist); TAILQ_REMOVE(item->queue, item, entry); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index eb4a7e09..44234315 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -20,7 +20,7 @@ #include <sys/types.h> #include <stdlib.h> -#include <unistd.h> +#include <string.h> #include "tmux.h" @@ -36,7 +36,7 @@ const struct cmd_entry cmd_respawn_pane_entry = { .args = { "c:kt:", 0, -1 }, .usage = "[-c start-directory] [-k] " CMD_TARGET_PANE_USAGE - " [command]", + " [command]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -48,53 +48,39 @@ static enum cmd_retval cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; + struct spawn_context sc; + struct session *s = item->target.s; struct winlink *wl = item->target.wl; - struct window *w = wl->window; struct window_pane *wp = item->target.wp; - struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct environ *env; - const char *path = NULL, *cp; - char *cause, *cwd = NULL; - u_int idx; - struct environ_entry *envent; - - if (!args_has(self->args, 'k') && wp->fd != -1) { - if (window_pane_index(wp, &idx) != 0) - fatalx("index not found"); - cmdq_error(item, "pane still active: %s:%d.%u", - s->name, wl->idx, idx); - return (CMD_RETURN_ERROR); - } + char *cause = NULL; + + memset(&sc, 0, sizeof sc); + sc.item = item; + sc.s = s; + sc.wl = wl; + + sc.wp0 = wp; + sc.lc = NULL; - window_pane_reset_mode_all(wp); - screen_reinit(&wp->base); - input_init(wp); + sc.name = NULL; + sc.argc = args->argc; + sc.argv = args->argv; - if (item->client != NULL && item->client->session == NULL) - envent = environ_find(item->client->environ, "PATH"); - else - envent = environ_find(s->environ, "PATH"); - if (envent != NULL) - path = envent->value; + sc.idx = -1; + sc.cwd = args_get(args, 'c'); - if ((cp = args_get(args, 'c')) != NULL) - cwd = format_single(item, cp, c, s, NULL, NULL); + sc.flags = SPAWN_RESPAWN; + if (args_has(args, 'k')) + sc.flags |= SPAWN_KILL; - env = environ_for_session(s, 0); - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, cwd, env, - s->tio, &cause) != 0) { + if (spawn_pane(&sc, &cause) == NULL) { cmdq_error(item, "respawn pane failed: %s", cause); free(cause); - environ_free(env); - free(cwd); return (CMD_RETURN_ERROR); } - environ_free(env); - free(cwd); wp->flags |= PANE_REDRAW; - server_status_window(w); + server_status_window(wp->window); return (CMD_RETURN_NORMAL); } diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 68791990..2ccb2fde 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -19,7 +19,7 @@ #include <sys/types.h> #include <stdlib.h> -#include <unistd.h> +#include <string.h> #include "tmux.h" @@ -48,64 +48,34 @@ static enum cmd_retval cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; + struct spawn_context sc; struct session *s = item->target.s; struct winlink *wl = item->target.wl; - struct window *w = wl->window; - struct window_pane *wp; - struct client *c = cmd_find_client(item, NULL, 1); - struct environ *env; - const char *path = NULL, *cp; - char *cause, *cwd = NULL; - struct environ_entry *envent; - - if (!args_has(self->args, 'k')) { - TAILQ_FOREACH(wp, &w->panes, entry) { - if (wp->fd == -1) - continue; - cmdq_error(item, "window still active: %s:%d", s->name, - wl->idx); - return (CMD_RETURN_ERROR); - } - } + char *cause = NULL; + + memset(&sc, 0, sizeof sc); + sc.item = item; + sc.s = s; + sc.wl = wl; + + sc.name = NULL; + sc.argc = args->argc; + sc.argv = args->argv; - wp = TAILQ_FIRST(&w->panes); - TAILQ_REMOVE(&w->panes, wp, entry); - layout_free(w); - window_destroy_panes(w); - TAILQ_INSERT_HEAD(&w->panes, wp, entry); - window_pane_resize(wp, w->sx, w->sy); - - if (item->client != NULL && item->client->session == NULL) - envent = environ_find(item->client->environ, "PATH"); - else - envent = environ_find(s->environ, "PATH"); - if (envent != NULL) - path = envent->value; - - if ((cp = args_get(args, 'c')) != NULL) - cwd = format_single(item, cp, c, s, NULL, NULL); - - env = environ_for_session(s, 0); - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, cwd, env, - s->tio, &cause) != 0) { + sc.idx = -1; + sc.cwd = args_get(args, 'c'); + + sc.flags = SPAWN_RESPAWN; + if (args_has(args, 'k')) + sc.flags |= SPAWN_KILL; + + if (spawn_window(&sc, &cause) == NULL) { cmdq_error(item, "respawn window failed: %s", cause); free(cause); - environ_free(env); - free(cwd); - server_destroy_pane(wp, 0); return (CMD_RETURN_ERROR); } - environ_free(env); - free(cwd); - - layout_init(w, wp); - window_pane_reset_mode_all(wp); - screen_reinit(&wp->base); - input_init(wp); - window_set_active_pane(w, wp); - recalculate_sizes(); - server_redraw_window(w); + server_redraw_window(wl->window); return (CMD_RETURN_NORMAL); } diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 5a900a13..6dc0f2a8 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -77,7 +77,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL) wp = TAILQ_LAST(&w->panes, window_panes); - window_set_active_pane(w, wp); + window_set_active_pane(w, wp, 1); cmd_find_from_winlink_pane(current, wl, wp, 0); server_redraw_window(w); } else { @@ -105,7 +105,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) if ((wp = TAILQ_NEXT(w->active, entry)) == NULL) wp = TAILQ_FIRST(&w->panes); - window_set_active_pane(w, wp); + window_set_active_pane(w, wp, 1); cmd_find_from_winlink_pane(current, wl, wp, 0); server_redraw_window(w); } diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 2873737f..af0f033b 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -112,7 +112,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) else { server_unzoom_window(w); window_redraw_active_switch(w, lastwp); - if (window_set_active_pane(w, lastwp)) { + if (window_set_active_pane(w, lastwp, 1)) { cmd_find_from_winlink(current, wl, 0); cmd_select_pane_redraw(w); } @@ -194,7 +194,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); server_unzoom_window(wp->window); window_redraw_active_switch(w, wp); - if (window_set_active_pane(w, wp)) { + if (window_set_active_pane(w, wp, 1)) { cmd_find_from_winlink_pane(current, wl, wp, 0); hooks_insert(s->hooks, item, current, "after-select-pane"); cmd_select_pane_redraw(w); diff --git a/cmd-set-option.c b/cmd-set-option.c index 96428d73..edeb8385 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -366,8 +366,6 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, return (-1); } return (0); - case OPTIONS_TABLE_ARRAY: - break; } return (-1); } diff --git a/cmd-show-options.c b/cmd-show-options.c index 5d480664..59d9f9f5 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -29,8 +29,8 @@ static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *); -static enum cmd_retval cmd_show_options_one(struct cmd *, struct cmdq_item *, - struct options *); +static void cmd_show_options_print(struct cmd *, struct cmdq_item *, + struct options_entry *, int); static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *, struct options *); @@ -65,23 +65,95 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_find_state *fs = &item->target; + struct client *c = cmd_find_client(item, NULL, 1); + struct session *s = item->target.s; + struct winlink *wl = item->target.wl; struct options *oo; enum options_table_scope scope; - char *cause; - int window; + char *argument, *name = NULL, *cause; + const char *target; + int window, idx, ambiguous; + struct options_entry *o; window = (self->entry == &cmd_show_window_options_entry); - scope = options_scope_from_flags(args, window, fs, &oo, &cause); + if (args->argc == 0) { + scope = options_scope_from_flags(args, window, fs, &oo, &cause); + return (cmd_show_options_all(self, item, oo)); + } + argument = format_single(item, args->argv[0], c, s, wl, NULL); + + name = options_match(argument, &idx, &ambiguous); + if (name == NULL) { + if (args_has(args, 'q')) + goto fail; + if (ambiguous) + cmdq_error(item, "ambiguous option: %s", argument); + else + cmdq_error(item, "invalid option: %s", argument); + goto fail; + } + if (*name == '@') + scope = options_scope_from_flags(args, window, fs, &oo, &cause); + else { + if (options_get_only(global_options, name) != NULL) + scope = OPTIONS_TABLE_SERVER; + else if (options_get_only(global_s_options, name) != NULL) + scope = OPTIONS_TABLE_SESSION; + else if (options_get_only(global_w_options, name) != NULL) + scope = OPTIONS_TABLE_WINDOW; + else { + scope = OPTIONS_TABLE_NONE; + xasprintf(&cause, "unknown option: %s", argument); + } + if (scope == OPTIONS_TABLE_SERVER) + oo = global_options; + else if (scope == OPTIONS_TABLE_SESSION) { + if (args_has(self->args, 'g')) + oo = global_s_options; + else if (s == NULL) { + target = args_get(args, 't'); + if (target != NULL) { + cmdq_error(item, "no such session: %s", + target); + } else + cmdq_error(item, "no current session"); + goto fail; + } else + oo = s->options; + } else if (scope == OPTIONS_TABLE_WINDOW) { + if (args_has(self->args, 'g')) + oo = global_w_options; + else if (wl == NULL) { + target = args_get(args, 't'); + if (target != NULL) { + cmdq_error(item, "no such window: %s", + target); + } else + cmdq_error(item, "no current window"); + goto fail; + } else + oo = wl->window->options; + } + } if (scope == OPTIONS_TABLE_NONE) { + if (args_has(args, 'q')) + goto fail; cmdq_error(item, "%s", cause); free(cause); - return (CMD_RETURN_ERROR); + goto fail; } + o = options_get_only(oo, name); + if (o != NULL) + cmd_show_options_print(self, item, o, idx); - if (args->argc == 0) - return (cmd_show_options_all(self, item, oo)); - else - return (cmd_show_options_one(self, item, oo)); + free(name); + free(argument); + return (CMD_RETURN_NORMAL); + +fail: + free(name); + free(argument); + return (CMD_RETURN_ERROR); } static void @@ -123,55 +195,15 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, } static enum cmd_retval -cmd_show_options_one(struct cmd *self, struct cmdq_item *item, - struct options *oo) -{ - struct args *args = self->args; - struct client *c = cmd_find_client(item, NULL, 1); - struct session *s = item->target.s; - struct winlink *wl = item->target.wl; - struct options_entry *o; - int idx, ambiguous; - char *name; - - name = format_single(item, args->argv[0], c, s, wl, NULL); - o = options_match_get(oo, name, &idx, 1, &ambiguous); - if (o == NULL) { - if (args_has(args, 'q')) { - free(name); - return (CMD_RETURN_NORMAL); - } - if (ambiguous) { - cmdq_error(item, "ambiguous option: %s", name); - free(name); - return (CMD_RETURN_ERROR); - } - if (*name != '@' && - options_match_get(oo, name, &idx, 0, &ambiguous) != NULL) { - free(name); - return (CMD_RETURN_NORMAL); - } - cmdq_error(item, "unknown option: %s", name); - free(name); - return (CMD_RETURN_ERROR); - } - cmd_show_options_print(self, item, o, idx); - free(name); - return (CMD_RETURN_NORMAL); -} - -static enum cmd_retval cmd_show_options_all(struct cmd *self, struct cmdq_item *item, struct options *oo) { struct options_entry *o; - const struct options_table_entry *oe; struct options_array_item *a; u_int idx; o = options_first(oo); while (o != NULL) { - oe = options_table_entry(o); if (!options_isarray(o)) cmd_show_options_print(self, item, o, -1); else { diff --git a/cmd-split-window.c b/cmd-split-window.c index f5dde751..e64e9247 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -52,111 +52,87 @@ const struct cmd_entry cmd_split_window_entry = { static enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) { - struct cmd_find_state *current = &item->shared->current; struct args *args = self->args; + struct cmd_find_state *current = &item->shared->current; + struct spawn_context sc; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; - struct window *w = wl->window; - struct window_pane *wp = item->target.wp, *new_wp = NULL; - struct environ *env; - const char *cmd, *path, *shell, *template, *tmp; - char **argv, *cause, *new_cause, *cp, *cwd; - u_int hlimit; - int argc, size, percentage, before; + struct window_pane *wp = item->target.wp, *new_wp; enum layout_type type; struct layout_cell *lc; - struct environ_entry *envent; - struct cmd_find_state fs; - - server_unzoom_window(w); - - if (args->argc == 0) { - cmd = options_get_string(s->options, "default-command"); - if (cmd != NULL && *cmd != '\0') { - argc = 1; - argv = (char **)&cmd; - } else { - argc = 0; - argv = NULL; - } - } else { - argc = args->argc; - argv = args->argv; - } - - if ((tmp = args_get(args, 'c')) != NULL) - cwd = format_single(item, tmp, c, s, NULL, NULL); - else - cwd = xstrdup(server_client_get_cwd(item->client, s)); + struct cmd_find_state fs; + int size, percentage, flags; + const char *template; + char *cause, *cp; - type = LAYOUT_TOPBOTTOM; if (args_has(args, 'h')) type = LAYOUT_LEFTRIGHT; - before = args_has(args, 'b'); - - size = -1; + else + type = LAYOUT_TOPBOTTOM; if (args_has(args, 'l')) { size = args_strtonum(args, 'l', 0, INT_MAX, &cause); if (cause != NULL) { - xasprintf(&new_cause, "size %s", cause); + cmdq_error(item, "create pane failed: -l %s", cause); free(cause); - cause = new_cause; - goto error; + return (CMD_RETURN_ERROR); } } else if (args_has(args, 'p')) { percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause); if (cause != NULL) { - xasprintf(&new_cause, "percentage %s", cause); + cmdq_error(item, "create pane failed: -p %s", cause); free(cause); - cause = new_cause; - goto error; + return (CMD_RETURN_ERROR); } if (type == LAYOUT_TOPBOTTOM) size = (wp->sy * percentage) / 100; else size = (wp->sx * percentage) / 100; - } - hlimit = options_get_number(s->options, "history-limit"); + } else + size = -1; + + server_unzoom_window(wp->window); - shell = options_get_string(s->options, "default-shell"); - if (*shell == '\0' || areshell(shell)) - shell = _PATH_BSHELL; + flags = 0; + if (args_has(args, 'b')) + flags |= SPAWN_BEFORE; + if (args_has(args, 'f')) + flags |= SPAWN_FULLSIZE; - lc = layout_split_pane(wp, type, size, before, args_has(args, 'f')); + lc = layout_split_pane(wp, type, size, flags); if (lc == NULL) { - cause = xstrdup("pane too small"); - goto error; + cmdq_error(item, "no space for new pane"); + return (CMD_RETURN_ERROR); } - new_wp = window_add_pane(w, wp, before, args_has(args, 'f'), hlimit); - layout_make_leaf(lc, new_wp); - path = NULL; - if (item->client != NULL && item->client->session == NULL) - envent = environ_find(item->client->environ, "PATH"); - else - envent = environ_find(s->environ, "PATH"); - if (envent != NULL) - path = envent->value; - - env = environ_for_session(s, 0); - if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, env, - s->tio, &cause) != 0) { - environ_free(env); - goto error; - } - environ_free(env); + memset(&sc, 0, sizeof sc); + sc.item = item; + sc.s = s; + sc.wl = wl; - layout_fix_panes(w); - server_redraw_window(w); + sc.wp0 = wp; + sc.lc = lc; - if (!args_has(args, 'd')) { - window_set_active_pane(w, new_wp); - session_select(s, wl->idx); - cmd_find_from_session(current, s, 0); - server_redraw_session(s); - } else - server_status_session(s); + sc.name = NULL; + sc.argc = args->argc; + sc.argv = args->argv; + + sc.idx = -1; + sc.cwd = args_get(args, 'c'); + + sc.flags = flags; + if (args_has(args, 'd')) + sc.flags |= SPAWN_DETACHED; + + if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { + cmdq_error(item, "create pane failed: %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + if (!args_has(args, 'd')) + cmd_find_from_winlink_pane(current, wl, new_wp, 0); + server_redraw_window(wp->window); + server_status_session(s); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) @@ -165,22 +141,9 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) cmdq_print(item, "%s", cp); free(cp); } - notify_window("window-layout-changed", w); cmd_find_from_winlink_pane(&fs, wl, new_wp, 0); hooks_insert(s->hooks, item, &fs, "after-split-window"); - free(cwd); return (CMD_RETURN_NORMAL); - -error: - if (new_wp != NULL) { - layout_close_pane(new_wp); - window_remove_pane(w, new_wp); - } - cmdq_error(item, "create pane failed: %s", cause); - free(cause); - - free(cwd); - return (CMD_RETURN_ERROR); } diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 1de272c4..2ad05561 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -101,17 +101,17 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(self->args, 'd')) { if (src_w != dst_w) { - window_set_active_pane(src_w, dst_wp); - window_set_active_pane(dst_w, src_wp); + window_set_active_pane(src_w, dst_wp, 1); + window_set_active_pane(dst_w, src_wp, 1); } else { tmp_wp = dst_wp; - window_set_active_pane(src_w, tmp_wp); + window_set_active_pane(src_w, tmp_wp, 1); } } else { if (src_w->active == src_wp) - window_set_active_pane(src_w, dst_wp); + window_set_active_pane(src_w, dst_wp, 1); if (dst_w->active == dst_wp) - window_set_active_pane(dst_w, src_wp); + window_set_active_pane(dst_w, src_wp, 1); } if (src_w != dst_w) { if (src_w->last == src_wp) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 3e19346e..8f51d0fe 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -61,7 +61,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL) return (CMD_RETURN_ERROR); - if (tflag != NULL && tflag[strcspn(tflag, ":.")] != '\0') { + if (tflag != NULL && tflag[strcspn(tflag, ":.%")] != '\0') { type = CMD_FIND_PANE; flags = 0; } else { @@ -116,7 +116,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) server_unzoom_window(wl->window); if (wp != NULL) { window_redraw_active_switch(wp->window, wp); - window_set_active_pane(wp->window, wp); + window_set_active_pane(wp->window, wp, 1); } session_set_current(s, wl); cmd_find_from_session(&item->shared->current, s, 0); @@ -320,10 +320,11 @@ cmd_try_alias(int *argc, char ***argv) { struct options_entry *o; struct options_array_item *a; + union options_value *ov; int old_argc = *argc, new_argc, i; char **old_argv = *argv, **new_argv; size_t wanted; - const char *s, *cp = NULL; + const char *cp = NULL; o = options_get_only(global_options, "command-alias"); if (o == NULL) @@ -332,14 +333,16 @@ cmd_try_alias(int *argc, char ***argv) a = options_array_first(o); while (a != NULL) { - s = options_array_item_value(a); - if (s != NULL) { - cp = strchr(s, '='); - if (cp != NULL && - (size_t)(cp - s) == wanted && - strncmp(old_argv[0], s, wanted) == 0) - break; + ov = options_array_item_value(a); + if (ov == NULL) { + a = options_array_next(a); + continue; } + cp = strchr(ov->string, '='); + if (cp != NULL && + (size_t)(cp - ov->string) == wanted && + strncmp(old_argv[0], ov->string, wanted) == 0) + break; a = options_array_next(a); } if (a == NULL) @@ -67,6 +67,7 @@ void warnx(const char *, ...); #define _PATH_DEVNULL "/dev/null" #define _PATH_TTY "/dev/tty" #define _PATH_DEV "/dev/" +#define _PATH_DEFPATH "/usr/bin:/bin" #endif #ifndef __OpenBSD__ diff --git a/configure.ac b/configure.ac index 89e62066..1e7578ee 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 2.9) +AC_INIT([tmux], next-3.0) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) @@ -177,20 +177,20 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst) struct environ_entry *envent; struct options_entry *o; struct options_array_item *a; - const char *value; + union options_value *ov; o = options_get(oo, "update-environment"); if (o == NULL) return; a = options_array_first(o); while (a != NULL) { - value = options_array_item_value(a); - if (value == NULL) { + ov = options_array_item_value(a); + if (ov == NULL) { a = options_array_next(a); continue; } - if ((envent = environ_find(src, value)) == NULL) - environ_clear(dst, value); + if ((envent = environ_find(src, ov->string)) == NULL) + environ_clear(dst, ov->string); else environ_set(dst, envent->name, "%s", envent->value); a = options_array_next(a); @@ -1822,6 +1822,23 @@ void format_defaults(struct format_tree *ft, struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp) { + if (c != NULL) + log_debug("%s: c=%s", __func__, c->name); + else + log_debug("%s: s=none", __func__); + if (s != NULL) + log_debug("%s: s=$%u", __func__, s->id); + else + log_debug("%s: s=none", __func__); + if (wl != NULL) + log_debug("%s: wl=%u w=@%u", __func__, wl->idx, wl->window->id); + else + log_debug("%s: wl=none", __func__); + if (wp != NULL) + log_debug("%s: wp=%%%u", __func__, wp->id); + else + log_debug("%s: wp=none", __func__); + if (c != NULL && s != NULL && c->session != s) log_debug("%s: session does not match", __func__); @@ -2432,7 +2432,7 @@ input_osc_52(struct input_ctx *ictx, const char *p) screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); - paste_add(out, outlen); + paste_add(NULL, out, outlen); } /* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */ @@ -414,9 +414,9 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc, lcother = TAILQ_NEXT(lc, entry); else lcother = TAILQ_PREV(lc, layout_cells, entry); - if (lcparent->type == LAYOUT_LEFTRIGHT) + if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT) layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1); - else + else if (lcother != NULL) layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1); /* Remove this from the parent's list. */ @@ -831,12 +831,12 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc) */ struct layout_cell * layout_split_pane(struct window_pane *wp, enum layout_type type, int size, - int insert_before, int full_size) + int flags) { struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; u_int sx, sy, xoff, yoff, size1, size2, minimum; u_int new_size, saved_size, resize_first = 0; - int status; + int full_size = (flags & SPAWN_FULLSIZE), status; /* * If full_size is specified, add a new cell at the top of the window @@ -881,7 +881,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, saved_size = sy; if (size < 0) size2 = ((saved_size + 1) / 2) - 1; - else if (insert_before) + else if (flags & SPAWN_BEFORE) size2 = saved_size - size - 1; else size2 = size; @@ -892,7 +892,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, size1 = saved_size - 1 - size2; /* Which size are we using? */ - if (insert_before) + if (flags & SPAWN_BEFORE) new_size = size2; else new_size = size1; @@ -908,7 +908,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, */ lcparent = lc->parent; lcnew = layout_create_cell(lcparent); - if (insert_before) + if (flags & SPAWN_BEFORE) TAILQ_INSERT_BEFORE(lc, lcnew, entry); else TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry); @@ -937,7 +937,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, layout_set_size(lcnew, size, sy, 0, 0); else if (lc->type == LAYOUT_TOPBOTTOM) layout_set_size(lcnew, sx, size, 0, 0); - if (insert_before) + if (flags & SPAWN_BEFORE) TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry); else TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); @@ -961,12 +961,12 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, /* Create the new child cell. */ lcnew = layout_create_cell(lcparent); - if (insert_before) + if (flags & SPAWN_BEFORE) TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry); else TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry); } - if (insert_before) { + if (flags & SPAWN_BEFORE) { lc1 = lcnew; lc2 = lc; } else { diff --git a/options-table.c b/options-table.c index 40639aff..a8beea06 100644 --- a/options-table.c +++ b/options-table.c @@ -140,8 +140,9 @@ const struct options_table_entry options_table[] = { }, { .name = "command-alias", - .type = OPTIONS_TABLE_ARRAY, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, + .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "split-pane=split-window," "splitp=split-window," "server-info=show-messages -JT," @@ -205,8 +206,9 @@ const struct options_table_entry options_table[] = { }, { .name = "terminal-overrides", - .type = OPTIONS_TABLE_ARRAY, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, + .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT", @@ -214,8 +216,9 @@ const struct options_table_entry options_table[] = { }, { .name = "user-keys", - .type = OPTIONS_TABLE_ARRAY, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, + .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "", .separator = "," }, @@ -420,8 +423,9 @@ const struct options_table_entry options_table[] = { }, { .name = "status-format", - .type = OPTIONS_TABLE_ARRAY, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, + .flags = OPTIONS_TABLE_IS_ARRAY, .default_arr = options_table_status_format_default, }, @@ -503,8 +507,9 @@ const struct options_table_entry options_table[] = { }, { .name = "update-environment", - .type = OPTIONS_TABLE_ARRAY, + .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, + .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "DISPLAY KRB5CCNAME SSH_ASKPASS SSH_AUTH_SOCK " "SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY" }, @@ -32,10 +32,9 @@ struct options_array_item { u_int index; - char *value; + union options_value value; RB_ENTRY(options_array_item) entry; }; -RB_HEAD(options_array, options_array_item); static int options_array_cmp(struct options_array_item *a1, struct options_array_item *a2) { @@ -48,19 +47,13 @@ options_array_cmp(struct options_array_item *a1, struct options_array_item *a2) RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp); struct options_entry { - struct options *owner; + struct options *owner; - const char *name; - const struct options_table_entry *tableentry; + const char *name; + const struct options_table_entry *tableentry; + union options_value value; - union { - char *string; - long long number; - struct style style; - struct options_array array; - }; - - RB_ENTRY(options_entry) entry; + RB_ENTRY(options_entry) entry; }; struct options { @@ -83,9 +76,10 @@ static struct options_entry *options_add(struct options *, const char *); #define OPTIONS_IS_STYLE(o) \ ((o)->tableentry != NULL && \ (o)->tableentry->type == OPTIONS_TABLE_STYLE) -#define OPTIONS_IS_ARRAY(o) \ + +#define OPTIONS_IS_ARRAY(o) \ ((o)->tableentry != NULL && \ - (o)->tableentry->type == OPTIONS_TABLE_ARRAY) + ((o)->tableentry->flags & OPTIONS_TABLE_IS_ARRAY)) static int options_cmp(struct options_entry *, struct options_entry *); RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp); @@ -109,6 +103,56 @@ options_parent_table_entry(struct options *oo, const char *s) return (o->tableentry); } +static void +options_value_free(struct options_entry *o, union options_value *ov) +{ + if (OPTIONS_IS_STRING(o)) + free(ov->string); +} + +static const char * +options_value_tostring(struct options_entry *o, union options_value *ov, + int numeric) +{ + static char s[1024]; + const char *tmp; + + if (OPTIONS_IS_STYLE(o)) + return (style_tostring(&ov->style)); + if (OPTIONS_IS_NUMBER(o)) { + tmp = NULL; + switch (o->tableentry->type) { + case OPTIONS_TABLE_NUMBER: + xsnprintf(s, sizeof s, "%lld", ov->number); + break; + case OPTIONS_TABLE_KEY: + tmp = key_string_lookup_key(ov->number); + break; + case OPTIONS_TABLE_COLOUR: + tmp = colour_tostring(ov->number); + break; + case OPTIONS_TABLE_FLAG: + if (numeric) + xsnprintf(s, sizeof s, "%lld", ov->number); + else + tmp = (ov->number ? "on" : "off"); + break; + case OPTIONS_TABLE_CHOICE: + tmp = o->tableentry->choices[ov->number]; + break; + case OPTIONS_TABLE_STRING: + case OPTIONS_TABLE_STYLE: + break; + } + if (tmp != NULL) + xsnprintf(s, sizeof s, "%s", tmp); + return (s); + } + if (OPTIONS_IS_STRING(o)) + return (ov->string); + return (""); +} + struct options * options_create(struct options *parent) { @@ -174,8 +218,11 @@ options_empty(struct options *oo, const struct options_table_entry *oe) o = options_add(oo, oe->name); o->tableentry = oe; - if (oe->type == OPTIONS_TABLE_ARRAY) - RB_INIT(&o->array); + if (oe->flags & OPTIONS_TABLE_IS_ARRAY) { + if (oe->type != OPTIONS_TABLE_STRING) + fatalx("arrays can only be strings"); + RB_INIT(&o->value.array); + } return (o); } @@ -183,23 +230,34 @@ options_empty(struct options *oo, const struct options_table_entry *oe) struct options_entry * options_default(struct options *oo, const struct options_table_entry *oe) { - struct options_entry *o; - u_int i; + struct options_entry *o; + union options_value *ov; + u_int i; o = options_empty(oo, oe); - if (oe->type == OPTIONS_TABLE_ARRAY) { + ov = &o->value; + + if (oe->flags & OPTIONS_TABLE_IS_ARRAY) { if (oe->default_arr != NULL) { for (i = 0; oe->default_arr[i] != NULL; i++) options_array_set(o, i, oe->default_arr[i], 0); } else options_array_assign(o, oe->default_str); - } else if (oe->type == OPTIONS_TABLE_STRING) - o->string = xstrdup(oe->default_str); - else if (oe->type == OPTIONS_TABLE_STYLE) { - style_set(&o->style, &grid_default_cell); - style_parse(&o->style, &grid_default_cell, oe->default_str); - } else - o->number = oe->default_num; + return (o); + } + + switch (oe->type) { + case OPTIONS_TABLE_STRING: + ov->string = xstrdup(oe->default_str); + break; + case OPTIONS_TABLE_STYLE: + style_set(&ov->style, &grid_default_cell); + style_parse(&ov->style, &grid_default_cell, oe->default_str); + break; + default: + ov->number = oe->default_num; + break; + } return (o); } @@ -225,11 +283,10 @@ options_remove(struct options_entry *o) { struct options *oo = o->owner; - if (OPTIONS_IS_STRING(o)) - free(o->string); - else if (OPTIONS_IS_ARRAY(o)) + if (OPTIONS_IS_ARRAY(o)) options_array_clear(o); - + else + options_value_free(o, &o->value); RB_REMOVE(options_tree, &oo->tree, o); free(o); } @@ -252,14 +309,14 @@ options_array_item(struct options_entry *o, u_int idx) struct options_array_item a; a.index = idx; - return (RB_FIND(options_array, &o->array, &a)); + return (RB_FIND(options_array, &o->value.array, &a)); } static void options_array_free(struct options_entry *o, struct options_array_item *a) { - free(a->value); - RB_REMOVE(options_array, &o->array, a); + options_value_free(o, &a->value); + RB_REMOVE(options_array, &o->value.array, a); free(a); } @@ -271,11 +328,11 @@ options_array_clear(struct options_entry *o) if (!OPTIONS_IS_ARRAY(o)) return; - RB_FOREACH_SAFE(a, options_array, &o->array, a1) + RB_FOREACH_SAFE(a, options_array, &o->value.array, a1) options_array_free(o, a); } -const char * +union options_value * options_array_get(struct options_entry *o, u_int idx) { struct options_array_item *a; @@ -285,7 +342,7 @@ options_array_get(struct options_entry *o, u_int idx) a = options_array_item(o, idx); if (a == NULL) return (NULL); - return (a->value); + return (&a->value); } int @@ -308,15 +365,15 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, if (a == NULL) { a = xcalloc(1, sizeof *a); a->index = idx; - a->value = xstrdup(value); - RB_INSERT(options_array, &o->array, a); + a->value.string = xstrdup(value); + RB_INSERT(options_array, &o->value.array, a); } else { - free(a->value); + options_value_free(o, &a->value); if (a != NULL && append) - xasprintf(&new, "%s%s", a->value, value); + xasprintf(&new, "%s%s", a->value.string, value); else new = xstrdup(value); - a->value = new; + a->value.string = new; } return (0); @@ -353,13 +410,13 @@ options_array_first(struct options_entry *o) { if (!OPTIONS_IS_ARRAY(o)) return (NULL); - return (RB_MIN(options_array, &o->array)); + return (RB_MIN(options_array, &o->value.array)); } struct options_array_item * options_array_next(struct options_array_item *a) { - return (RB_NEXT(options_array, &o->array, a)); + return (RB_NEXT(options_array, &o->value.array, a)); } u_int @@ -368,10 +425,10 @@ options_array_item_index(struct options_array_item *a) return (a->index); } -const char * +union options_value * options_array_item_value(struct options_array_item *a) { - return (a->value); + return (&a->value); } int @@ -383,14 +440,12 @@ options_isarray(struct options_entry *o) int options_isstring(struct options_entry *o) { - return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o)); + return (OPTIONS_IS_STRING(o)); } const char * options_tostring(struct options_entry *o, int idx, int numeric) { - static char s[1024]; - const char *tmp; struct options_array_item *a; if (OPTIONS_IS_ARRAY(o)) { @@ -399,43 +454,9 @@ options_tostring(struct options_entry *o, int idx, int numeric) a = options_array_item(o, idx); if (a == NULL) return (""); - return (a->value); - } - if (OPTIONS_IS_STYLE(o)) - return (style_tostring(&o->style)); - if (OPTIONS_IS_NUMBER(o)) { - tmp = NULL; - switch (o->tableentry->type) { - case OPTIONS_TABLE_NUMBER: - xsnprintf(s, sizeof s, "%lld", o->number); - break; - case OPTIONS_TABLE_KEY: - tmp = key_string_lookup_key(o->number); - break; - case OPTIONS_TABLE_COLOUR: - tmp = colour_tostring(o->number); - break; - case OPTIONS_TABLE_FLAG: - if (numeric) - xsnprintf(s, sizeof s, "%lld", o->number); - else - tmp = (o->number ? "on" : "off"); - break; - case OPTIONS_TABLE_CHOICE: - tmp = o->tableentry->choices[o->number]; - break; - case OPTIONS_TABLE_STRING: - case OPTIONS_TABLE_STYLE: - case OPTIONS_TABLE_ARRAY: - break; - } - if (tmp != NULL) - xsnprintf(s, sizeof s, "%s", tmp); - return (s); + return (options_value_tostring(o, &a->value, numeric)); } - if (OPTIONS_IS_STRING(o)) - return (o->string); - return (NULL); + return (options_value_tostring(o, &o->value, numeric)); } char * @@ -549,7 +570,7 @@ options_get_string(struct options *oo, const char *name) fatalx("missing option %s", name); if (!OPTIONS_IS_STRING(o)) fatalx("option %s is not a string", name); - return (o->string); + return (o->value.string); } long long @@ -562,7 +583,7 @@ options_get_number(struct options *oo, const char *name) fatalx("missing option %s", name); if (!OPTIONS_IS_NUMBER(o)) fatalx("option %s is not a number", name); - return (o->number); + return (o->value.number); } struct style * @@ -575,7 +596,7 @@ options_get_style(struct options *oo, const char *name) fatalx("missing option %s", name); if (!OPTIONS_IS_STYLE(o)) fatalx("option %s is not a style", name); - return (&o->style); + return (&o->value.style); } struct options_entry * @@ -592,7 +613,7 @@ options_set_string(struct options *oo, const char *name, int append, o = options_get_only(oo, name); if (o != NULL && append && OPTIONS_IS_STRING(o)) { - xasprintf(&value, "%s%s", o->string, s); + xasprintf(&value, "%s%s", o->value.string, s); free(s); } else value = s; @@ -606,8 +627,8 @@ options_set_string(struct options *oo, const char *name, int append, if (!OPTIONS_IS_STRING(o)) fatalx("option %s is not a string", name); - free(o->string); - o->string = value; + free(o->value.string); + o->value.string = value; return (o); } @@ -628,7 +649,7 @@ options_set_number(struct options *oo, const char *name, long long value) if (!OPTIONS_IS_NUMBER(o)) fatalx("option %s is not a number", name); - o->number = value; + o->value.number = value; return (o); } @@ -644,7 +665,7 @@ options_set_style(struct options *oo, const char *name, int append, o = options_get_only(oo, name); if (o != NULL && append && OPTIONS_IS_STYLE(o)) - style_copy(&sy, &o->style); + style_copy(&sy, &o->value.style); else style_set(&sy, &grid_default_cell); if (style_parse(&sy, &grid_default_cell, value) == -1) @@ -657,7 +678,7 @@ options_set_style(struct options *oo, const char *name, int append, if (!OPTIONS_IS_STYLE(o)) fatalx("option %s is not a style", name); - style_copy(&o->style, &sy); + style_copy(&o->value.style, &sy); return (o); } diff --git a/osdep-darwin.c b/osdep-darwin.c index 5d69cdda..d4a88028 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -30,7 +30,9 @@ char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); +#ifndef __unused #define __unused __attribute__ ((__unused__)) +#endif char * osdep_get_name(int fd, __unused char *tty) @@ -47,6 +49,7 @@ osdep_get_name(int fd, __unused char *tty) &bsdinfo, sizeof bsdinfo); if (ret == sizeof bsdinfo && *bsdinfo.pbsi_comm != '\0') return (strdup(bsdinfo.pbsi_comm)); + return (NULL); #else int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 }; size_t size; @@ -157,11 +157,14 @@ paste_free(struct paste_buffer *pb) * that the caller is responsible for allocating data. */ void -paste_add(char *data, size_t size) +paste_add(const char *prefix, char *data, size_t size) { struct paste_buffer *pb, *pb1; u_int limit; + if (prefix == NULL) + prefix = "buffer"; + if (size == 0) { free(data); return; @@ -180,7 +183,7 @@ paste_add(char *data, size_t size) pb->name = NULL; do { free(pb->name); - xasprintf(&pb->name, "buffer%04u", paste_next_index); + xasprintf(&pb->name, "%s%u", prefix, paste_next_index); paste_next_index++; } while (paste_get_name(pb->name) != NULL); @@ -262,7 +265,7 @@ paste_set(char *data, size_t size, const char *name, char **cause) return (0); } if (name == NULL) { - paste_add(data, size); + paste_add(NULL, data, size); return (0); } diff --git a/screen-write.c b/screen-write.c index 566d4d14..237b6359 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1139,7 +1139,7 @@ screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) u_int i; size_t size; - for (i = y ; i < y + n; i++) { + for (i = y; i < y + n; i++) { if (TAILQ_EMPTY(&ctx->list[i].items)) continue; size = 0; diff --git a/server-client.c b/server-client.c index aea090c7..bf7da577 100644 --- a/server-client.c +++ b/server-client.c @@ -1379,6 +1379,7 @@ focused: if (wp->base.mode & MODE_FOCUSON) bufferevent_write(wp->event, "\033[I", 3); notify_pane("pane-focus-in", wp); + session_update_activity(c->session, NULL); } wp->flags |= PANE_FOCUSED; } diff --git a/server-fn.c b/server-fn.c index 90db7d51..8051fe4f 100644 --- a/server-fn.c +++ b/server-fn.c @@ -369,7 +369,7 @@ server_destroy_session_group(struct session *s) else { TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { server_destroy_session(s); - session_destroy(s, __func__); + session_destroy(s, 1, __func__); } } } @@ -436,7 +436,7 @@ server_check_unattached(void) if (s->attached != 0) continue; if (options_get_number (s->options, "destroy-unattached")) - session_destroy(s, __func__); + session_destroy(s, 1, __func__); } } @@ -296,7 +296,7 @@ server_send_exit(void) } RB_FOREACH_SAFE(s, sessions, &sessions, s1) - session_destroy(s, __func__); + session_destroy(s, 1, __func__); } /* Update socket execute permissions based on whether sessions are attached. */ @@ -111,12 +111,10 @@ session_find_by_id(u_int id) /* Create a new session. */ struct session * -session_create(const char *prefix, const char *name, int argc, char **argv, - const char *path, const char *cwd, struct environ *env, struct options *oo, - struct termios *tio, int idx, char **cause) +session_create(const char *prefix, const char *name, const char *cwd, + struct environ *env, struct options *oo, struct termios *tio) { struct session *s; - struct winlink *wl; s = xcalloc(1, sizeof *s); s->references = 1; @@ -128,10 +126,7 @@ session_create(const char *prefix, const char *name, int argc, char **argv, TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); - s->environ = environ_create(); - if (env != NULL) - environ_copy(env, s->environ); - + s->environ = env; s->options = oo; s->hooks = hooks_create(global_hooks); @@ -165,17 +160,6 @@ session_create(const char *prefix, const char *name, int argc, char **argv, fatal("gettimeofday failed"); session_update_activity(s, &s->creation_time); - if (argc >= 0) { - wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause); - if (wl == NULL) { - session_destroy(s, __func__); - return (NULL); - } - session_select(s, RB_ROOT(&s->windows)->idx); - } - - log_debug("session %s created", s->name); - return (s); } @@ -219,7 +203,7 @@ session_free(__unused int fd, __unused short events, void *arg) /* Destroy a session. */ void -session_destroy(struct session *s, const char *from) +session_destroy(struct session *s, int notify, const char *from) { struct winlink *wl; @@ -227,7 +211,8 @@ session_destroy(struct session *s, const char *from) s->curw = NULL; RB_REMOVE(sessions, &sessions, s); - notify_session("session-closed", s); + if (notify) + notify_session("session-closed", s); free(s->tio); @@ -337,45 +322,6 @@ session_previous_session(struct session *s) return (s2); } -/* Create a new window on a session. */ -struct winlink * -session_new(struct session *s, const char *name, int argc, char **argv, - const char *path, const char *cwd, int idx, char **cause) -{ - struct window *w; - struct winlink *wl; - struct environ *env; - const char *shell; - u_int hlimit, sx, sy; - - if ((wl = winlink_add(&s->windows, idx)) == NULL) { - xasprintf(cause, "index in use: %d", idx); - return (NULL); - } - wl->session = s; - - shell = options_get_string(s->options, "default-shell"); - if (*shell == '\0' || areshell(shell)) - shell = _PATH_BSHELL; - - default_window_size(s, NULL, &sx, &sy, -1); - hlimit = options_get_number(s->options, "history-limit"); - env = environ_for_session(s, 0); - w = window_create_spawn(name, argc, argv, path, shell, cwd, env, s->tio, - sx, sy, hlimit, cause); - if (w == NULL) { - winlink_remove(&s->windows, wl); - environ_free(env); - return (NULL); - } - winlink_set_window(wl, w); - environ_free(env); - notify_session_window("window-linked", s, w); - - session_group_synchronize_from(s); - return (wl); -} - /* Attach a window to a session. */ struct winlink * session_attach(struct session *s, struct window *w, int idx, char **cause) @@ -411,7 +357,7 @@ session_detach(struct session *s, struct winlink *wl) session_group_synchronize_from(s); if (RB_EMPTY(&s->windows)) { - session_destroy(s, __func__); + session_destroy(s, 1, __func__); return (1); } return (0); diff --git a/spawn.c b/spawn.c new file mode 100644 index 00000000..1da3c857 --- /dev/null +++ b/spawn.c @@ -0,0 +1,432 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "tmux.h" + +/* + * Set up the environment and create a new window and pane or a new pane. + * + * We need to set up the following items: + * + * - history limit, comes from the session; + * + * - base index, comes from the session; + * + * - current working directory, may be specified - if it isn't it comes from + * either the client or the session; + * + * - PATH variable, comes from the client if any, otherwise from the session + * environment; + * + * - shell, comes from default-shell; + * + * - termios, comes from the session; + * + * - remaining environment, comes from the session. + */ + +static void +spawn_log(const char *from, struct spawn_context *sc) +{ + struct session *s = sc->s; + struct winlink *wl = sc->wl; + struct window_pane *wp0 = sc->wp0; + char tmp[128]; + const char *name; + + log_debug("%s: %s, flags=%#x", from, sc->item->name, sc->flags); + + if (wl != NULL && wp0 != NULL) + xsnprintf(tmp, sizeof tmp, "wl=%d wp0=%%%u", wl->idx, wp0->id); + else if (wl != NULL) + xsnprintf(tmp, sizeof tmp, "wl=%d wp0=none", wl->idx); + else if (wp0 != NULL) + xsnprintf(tmp, sizeof tmp, "wl=none wp0=%%%u", wp0->id); + else + xsnprintf(tmp, sizeof tmp, "wl=none wp0=none"); + log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx); + + name = sc->name; + if (name == NULL) + name = "none"; + log_debug("%s: name=%s", from, name); +} + +struct winlink * +spawn_window(struct spawn_context *sc, char **cause) +{ + struct session *s = sc->s; + struct window *w; + struct window_pane *wp; + struct winlink *wl; + int idx = sc->idx; + u_int sx, sy; + + spawn_log(__func__, sc); + + /* + * If the window already exists, we are respawning, so destroy all the + * panes except one. + */ + if (sc->flags & SPAWN_RESPAWN) { + w = sc->wl->window; + if (~sc->flags & SPAWN_KILL) { + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->fd != -1) + break; + } + if (wp != NULL) { + xasprintf(cause, "window %s:%d still active", + s->name, sc->wl->idx); + return (NULL); + } + } + + sc->wp0 = TAILQ_FIRST(&w->panes); + TAILQ_REMOVE(&w->panes, sc->wp0, entry); + + layout_free(w); + window_destroy_panes(w); + + TAILQ_INSERT_HEAD(&w->panes, sc->wp0, entry); + window_pane_resize(sc->wp0, w->sx, w->sy); + + layout_init(w, sc->wp0); + window_set_active_pane(w, sc->wp0, 0); + } + + /* + * Otherwise we have no window so we will need to create one. First + * check if the given index already exists and destroy it if so. + */ + if ((~sc->flags & SPAWN_RESPAWN) && idx != -1) { + wl = winlink_find_by_index(&s->windows, idx); + if (wl != NULL && (~sc->flags & SPAWN_KILL)) { + xasprintf(cause, "index %d in use", idx); + return (NULL); + } + if (wl != NULL) { + /* + * Can't use session_detach as it will destroy session + * if this makes it empty. + */ + wl->flags &= ~WINLINK_ALERTFLAGS; + notify_session_window("window-unlinked", s, wl->window); + winlink_stack_remove(&s->lastw, wl); + winlink_remove(&s->windows, wl); + + if (s->curw == wl) { + s->curw = NULL; + sc->flags &= ~SPAWN_DETACHED; + } + } + } + + /* Then create a window if needed. */ + if (~sc->flags & SPAWN_RESPAWN) { + if (idx == -1) + idx = -1 - options_get_number(s->options, "base-index"); + if ((sc->wl = winlink_add(&s->windows, idx)) == NULL) { + xasprintf(cause, "couldn't add window %d", idx); + return (NULL); + } + default_window_size(s, NULL, &sx, &sy, -1); + if ((w = window_create(sx, sy)) == NULL) { + winlink_remove(&s->windows, sc->wl); + xasprintf(cause, "couldn't create window %d", idx); + return (NULL); + } + sc->wl->session = s; + winlink_set_window(sc->wl, w); + } else + w = NULL; + sc->flags |= SPAWN_NONOTIFY; + + /* Spawn the pane. */ + wp = spawn_pane(sc, cause); + if (wp == NULL) { + if (~sc->flags & SPAWN_RESPAWN) { + window_destroy(w); + winlink_remove(&s->windows, sc->wl); + } + return (NULL); + } + + /* Set the name of the new window. */ + if (~sc->flags & SPAWN_RESPAWN) { + if (sc->name != NULL) { + w->name = xstrdup(sc->name); + options_set_number(w->options, "automatic-rename", 0); + } else + w->name = xstrdup(default_window_name(w)); + } + + /* Switch to the new window if required. */ + if (~sc->flags & SPAWN_DETACHED) + session_select(s, sc->wl->idx); + + /* Fire notification if new window. */ + if (~sc->flags & SPAWN_RESPAWN) + notify_session_window("window-linked", s, w); + + session_group_synchronize_from(s); + return (sc->wl); +} + +struct window_pane * +spawn_pane(struct spawn_context *sc, char **cause) +{ + struct cmdq_item *item = sc->item; + struct client *c = item->client; + struct session *s = sc->s; + struct window *w = sc->wl->window; + struct window_pane *new_wp; + struct environ *child; + struct environ_entry *ee; + char **argv, *cp, **argvp, *argv0, *cwd; + const char *cmd, *tmp; + int argc; + u_int idx; + struct termios now; + u_int hlimit; + struct winsize ws; + sigset_t set, oldset; + + spawn_log(__func__, sc); + + /* + * If we are respawning then get rid of the old process. Otherwise + * either create a new cell or assign to the one we are given. + */ + hlimit = options_get_number(s->options, "history-limit"); + if (sc->flags & SPAWN_RESPAWN) { + if (sc->wp0->fd != -1 && (~sc->flags & SPAWN_KILL)) { + window_pane_index(sc->wp0, &idx); + xasprintf(cause, "pane %s:%d.%u still active", + s->name, sc->wl->idx, idx); + return (NULL); + } + if (sc->wp0->fd != -1) { + bufferevent_free(sc->wp0->event); + close(sc->wp0->fd); + } + window_pane_reset_mode_all(sc->wp0); + screen_reinit(&sc->wp0->base); + input_init(sc->wp0); + new_wp = sc->wp0; + new_wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN); + } else if (sc->lc == NULL) { + new_wp = window_add_pane(w, NULL, hlimit, sc->flags); + layout_init(w, new_wp); + } else { + new_wp = window_add_pane(w, sc->wp0, hlimit, sc->flags); + layout_assign_pane(sc->lc, new_wp); + } + + /* + * Now we have a pane with nothing running in it ready for the new + * process. Work out the command and arguments. + */ + if (sc->argc == 0) { + cmd = options_get_string(s->options, "default-command"); + if (cmd != NULL && *cmd != '\0') { + argc = 1; + argv = (char **)&cmd; + } else { + argc = 0; + argv = NULL; + } + } else { + argc = sc->argc; + argv = sc->argv; + } + + /* + * Replace the stored arguments if there are new ones. If not, the + * existing ones will be used (they will only exist for respawn). + */ + if (argc > 0) { + cmd_free_argv(new_wp->argc, new_wp->argv); + new_wp->argc = argc; + new_wp->argv = cmd_copy_argv(argc, argv); + } + + /* + * Work out the current working directory. If respawning, use + * the pane's stored one unless specified. + */ + if (sc->cwd != NULL) + cwd = format_single(item, sc->cwd, c, s, NULL, NULL); + else if (~sc->flags & SPAWN_RESPAWN) + cwd = xstrdup(server_client_get_cwd(c, s)); + else + cwd = NULL; + if (cwd != NULL) { + free(new_wp->cwd); + new_wp->cwd = cwd; + } + + /* Create an environment for this pane. */ + child = environ_for_session(s, 0); + environ_set(child, "TMUX_PANE", "%%%u", new_wp->id); + + /* + * Then the PATH environment variable. The session one is replaced from + * the client if there is one because otherwise running "tmux new + * myprogram" wouldn't work if myprogram isn't in the session's path. + */ + if (c != NULL && c->session == NULL) { /* only unattached clients */ + ee = environ_find(c->environ, "PATH"); + if (ee != NULL) + environ_set(child, "PATH", "%s", ee->value); + } + if (environ_find(child, "PATH") == NULL) + environ_set(child, "%s", _PATH_DEFPATH); + + /* Then the shell. If respawning, use the old one. */ + if (~sc->flags & SPAWN_RESPAWN) { + tmp = options_get_string(s->options, "default-shell"); + if (*tmp == '\0' || areshell(tmp)) + tmp = _PATH_BSHELL; + free(new_wp->shell); + new_wp->shell = xstrdup(tmp); + } + environ_set(child, "SHELL", "%s", new_wp->shell); + + /* Log the arguments we are going to use. */ + log_debug("%s: shell=%s", __func__, new_wp->shell); + if (new_wp->argc != 0) { + cp = cmd_stringify_argv(new_wp->argc, new_wp->argv); + log_debug("%s: cmd=%s", __func__, cp); + free(cp); + } + if (cwd != NULL) + log_debug("%s: cwd=%s", __func__, cwd); + cmd_log_argv(new_wp->argc, new_wp->argv, __func__); + environ_log(child, "%s: environment ", __func__); + + /* Initialize the window size. */ + memset(&ws, 0, sizeof ws); + ws.ws_col = screen_size_x(&new_wp->base); + ws.ws_row = screen_size_y(&new_wp->base); + + /* Block signals until fork has completed. */ + sigfillset(&set); + sigprocmask(SIG_BLOCK, &set, &oldset); + + /* Fork the new process. */ + new_wp->pid = fdforkpty(ptm_fd, &new_wp->fd, new_wp->tty, NULL, &ws); + if (new_wp->pid == -1) { + xasprintf(cause, "fork failed: %s", strerror(errno)); + new_wp->fd = -1; + if (~sc->flags & SPAWN_RESPAWN) { + layout_close_pane(new_wp); + window_remove_pane(w, new_wp); + } + sigprocmask(SIG_SETMASK, &oldset, NULL); + return (NULL); + } + + /* In the parent process, everything is done now. */ + if (new_wp->pid != 0) { + new_wp->pipe_off = 0; + new_wp->flags &= ~PANE_EXITED; + + sigprocmask(SIG_SETMASK, &oldset, NULL); + window_pane_set_event(new_wp); + + if (sc->flags & SPAWN_RESPAWN) + return (new_wp); + if ((~sc->flags & SPAWN_DETACHED) || w->active == NULL) { + if (sc->flags & SPAWN_NONOTIFY) + window_set_active_pane(w, new_wp, 0); + else + window_set_active_pane(w, new_wp, 1); + } + if (~sc->flags & SPAWN_NONOTIFY) + notify_window("window-layout-changed", w); + return (new_wp); + } + + /* + * Child process. Change to the working directory or home if that + * fails. + */ + if (chdir(new_wp->cwd) != 0) { + if ((tmp = find_home()) == NULL || chdir(tmp) != 0) + chdir("/"); + } + + /* + * Update terminal escape characters from the session if available and + * force VERASE to tmux's \177. + */ + if (tcgetattr(STDIN_FILENO, &now) != 0) + _exit(1); + if (s->tio != NULL) + memcpy(now.c_cc, s->tio->c_cc, sizeof now.c_cc); + now.c_cc[VERASE] = '\177'; + if (tcsetattr(STDIN_FILENO, TCSANOW, &now) != 0) + _exit(1); + + /* Clean up file descriptors and signals and update the environment. */ + closefrom(STDERR_FILENO + 1); + proc_clear_signals(server_proc, 1); + sigprocmask(SIG_SETMASK, &oldset, NULL); + log_close(); + environ_push(child); + + /* + * If given multiple arguments, use execvp(). Copy the arguments to + * ensure they end in a NULL. + */ + if (new_wp->argc != 0 && new_wp->argc != 1) { + argvp = cmd_copy_argv(new_wp->argc, new_wp->argv); + execvp(argvp[0], argvp); + _exit(1); + } + + /* + * If one argument, pass it to $SHELL -c. Otherwise create a login + * shell. + */ + cp = strrchr(new_wp->shell, '/'); + if (new_wp->argc == 1) { + tmp = new_wp->argv[0]; + if (cp != NULL && cp[1] != '\0') + xasprintf(&argv0, "%s", cp + 1); + else + xasprintf(&argv0, "%s", new_wp->shell); + execl(new_wp->shell, argv0, "-c", tmp, (char *)NULL); + _exit(1); + } + if (cp != NULL && cp[1] != '\0') + xasprintf(&argv0, "-%s", cp + 1); + else + xasprintf(&argv0, "-%s", new_wp->shell); + execl(new_wp->shell, argv0, (char *)NULL); + _exit(1); +} @@ -323,8 +323,8 @@ status_redraw(struct client *c) u_int lines, i, width = c->tty.sx; int flags, force = 0, changed = 0; struct options_entry *o; + union options_value *ov; struct format_tree *ft; - const char *fmt; char *expanded; log_debug("%s enter", __func__); @@ -370,14 +370,14 @@ status_redraw(struct client *c) for (i = 0; i < lines; i++) { screen_write_cursormove(&ctx, 0, i, 0); - fmt = options_array_get(o, i); - if (fmt == NULL) { + ov = options_array_get(o, i); + if (ov == NULL) { screen_write_clearline(&ctx, gc.bg); continue; } sle = &sl->entries[i]; - expanded = format_expand_time(ft, fmt); + expanded = format_expand_time(ft, ov->string); if (!force && sle->expanded != NULL && strcmp(expanded, sle->expanded) == 0) { @@ -1293,6 +1293,7 @@ status_prompt_complete_list(u_int *size, const char *s) size_t slen = strlen(s), valuelen; struct options_entry *o; struct options_array_item *a; + union options_value *ov; const char *layouts[] = { "even-horizontal", "even-vertical", "main-horizontal", "main-vertical", "tiled", NULL @@ -1321,10 +1322,13 @@ status_prompt_complete_list(u_int *size, const char *s) if (o != NULL) { a = options_array_first(o); while (a != NULL) { - value = options_array_item_value(a);; - if (value == NULL || (cp = strchr(value, '=')) == NULL) + ov = options_array_item_value(a); + if (ov == NULL) goto next; + value = ov->string; + if ((cp = strchr(value, '=')) == NULL) + goto next; valuelen = cp - value; if (slen > valuelen || strncmp(value, s, slen) != 0) goto next; @@ -172,7 +172,7 @@ style_tostring(struct style *sy) { struct grid_cell *gc = &sy->gc; int off = 0; - const char *comma = "", *tmp; + const char *comma = "", *tmp = ""; static char s[256]; char b[16]; @@ -1096,6 +1096,14 @@ Switch the current session for client .Ar target-client to .Ar target-session . +As a special case, +.Fl t +may refer to a pane (a target that contains +.Ql : , +.Ql . +or +.Ql % ) +in which case the session, window and pane are all changed. If .Fl l , .Fl n @@ -1175,12 +1183,14 @@ The following commands are supported in copy mode: .It Li "bottom-line" Ta "L" Ta "" .It Li "cancel" Ta "q" Ta "Escape" .It Li "clear-selection" Ta "Escape" Ta "C-g" -.It Li "copy-end-of-line" Ta "D" Ta "C-k" -.It Li "copy-line" Ta "" Ta "" -.It Li "copy-pipe <command>" Ta "" Ta "" -.It Li "copy-pipe-and-cancel <command>" Ta "" Ta "" -.It Li "copy-selection" Ta "" Ta "" -.It Li "copy-selection-and-cancel" Ta "Enter" Ta "M-w" +.It Li "copy-end-of-line [<prefix>]" Ta "D" Ta "C-k" +.It Li "copy-line [<prefix>]" Ta "" Ta "" +.It Li "copy-pipe <command> [<prefix>]" Ta "" Ta "" +.It Li "copy-pipe-no-clear <command> [<prefix>]" Ta "" Ta "" +.It Li "copy-pipe-and-cancel <command> [<prefix>]" Ta "" Ta "" +.It Li "copy-selection [<prefix>]" Ta "" Ta "" +.It Li "copy-selection-no-clear [<prefix>]" Ta "" Ta "" +.It Li "copy-selection-and-cancel [<prefix>]" Ta "Enter" Ta "M-w" .It Li "cursor-down" Ta "j" Ta "Down" .It Li "cursor-left" Ta "h" Ta "Left" .It Li "cursor-right" Ta "l" Ta "Right" @@ -1222,15 +1232,27 @@ The following commands are supported in copy mode: .It Li "search-forward-incremental <for>" Ta "" Ta "C-s" .It Li "search-reverse" Ta "N" Ta "N" .It Li "select-line" Ta "V" Ta "" +.It Li "select-word" Ta "" Ta "" .It Li "start-of-line" Ta "0" Ta "C-a" .It Li "stop-selection" Ta "" Ta "" .It Li "top-line" Ta "H" Ta "M-R" .El .Pp +Copy commands may take an optional buffer prefix argument which is used +to generate the buffer name (the default is +.Ql buffer +so buffers are named +.Ql buffer0 , +.Ql buffer1 +and so on). +Pipe commands take a command argument which is the command to which the +copied text is piped. The .Ql -and-cancel variants of some commands exit copy mode after they have completed (for copy commands) or when the cursor reaches the bottom (for scrolling commands). +.Ql -no-clear +variants do not clear the selection. .Pp The next and previous word keys use space and the .Ql - , @@ -2513,9 +2535,17 @@ command), a server option with .Fl s , otherwise a session option. +If the option is not a user option, +.Fl w +and +.Fl s +are unnecessary - +.Nm +will infer the type from the option name. If .Fl g is given, the global session or window option is set. +.Pp .Fl F expands formats in the option value. The @@ -3444,6 +3474,15 @@ the server options with .Fl s , otherwise the session options for .Ar target session . +If +.Ar option +is given and is not a user option, +.Fl w +and +.Fl s +are unnecessary - +.Nm +will infer the type from the option name. Global session or window options are listed if .Fl g is used. @@ -235,8 +235,8 @@ enum { /* Termcap codes. */ enum tty_code_code { - TTYC_AX = 0, TTYC_ACSC, + TTYC_AX, TTYC_BCE, TTYC_BEL, TTYC_BLINK, @@ -816,7 +816,7 @@ struct window_pane { int argc; char **argv; char *shell; - const char *cwd; + char *cwd; pid_t pid; char tty[TTY_NAME_MAX]; @@ -1516,6 +1516,15 @@ struct key_table { }; RB_HEAD(key_tables, key_table); +/* Option data. */ +RB_HEAD(options_array, options_array_item); +union options_value { + char *string; + long long number; + struct style style; + struct options_array array; +}; + /* Option table entries. */ enum options_table_type { OPTIONS_TABLE_STRING, @@ -1524,8 +1533,7 @@ enum options_table_type { OPTIONS_TABLE_COLOUR, OPTIONS_TABLE_FLAG, OPTIONS_TABLE_CHOICE, - OPTIONS_TABLE_STYLE, - OPTIONS_TABLE_ARRAY, + OPTIONS_TABLE_STYLE }; enum options_table_scope { @@ -1535,10 +1543,13 @@ enum options_table_scope { OPTIONS_TABLE_WINDOW, }; +#define OPTIONS_TABLE_IS_ARRAY 0x1 + struct options_table_entry { const char *name; enum options_table_type type; enum options_table_scope scope; + int flags; u_int minimum; u_int maximum; @@ -1563,6 +1574,32 @@ struct options_table_entry { #define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]" #define CMD_BUFFER_USAGE "[-b buffer-name]" +/* Spawn common context. */ +struct spawn_context { + struct cmdq_item *item; + + struct session *s; + struct winlink *wl; + + struct window_pane *wp0; + struct layout_cell *lc; + + const char *name; + char **argv; + int argc; + + int idx; + const char *cwd; + + int flags; +#define SPAWN_KILL 0x1 +#define SPAWN_DETACHED 0x2 +#define SPAWN_RESPAWN 0x4 +#define SPAWN_BEFORE 0x8 +#define SPAWN_NONOTIFY 0x10 +#define SPAWN_FULLSIZE 0x20 +}; + /* tmux.c */ extern struct hooks *global_hooks; extern struct options *global_options; @@ -1613,7 +1650,7 @@ struct paste_buffer *paste_walk(struct paste_buffer *); struct paste_buffer *paste_get_top(const char **); struct paste_buffer *paste_get_name(const char *); void paste_free(struct paste_buffer *); -void paste_add(char *, size_t); +void paste_add(const char *, 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 *); @@ -1697,14 +1734,14 @@ struct options_entry *options_get_only(struct options *, const char *); struct options_entry *options_get(struct options *, const char *); void options_remove(struct options_entry *); void options_array_clear(struct options_entry *); -const char *options_array_get(struct options_entry *, u_int); +union options_value *options_array_get(struct options_entry *, u_int); int options_array_set(struct options_entry *, u_int, const char *, int); void options_array_assign(struct options_entry *, const char *); struct options_array_item *options_array_first(struct options_entry *); struct options_array_item *options_array_next(struct options_array_item *); u_int options_array_item_index(struct options_array_item *); -const char *options_array_item_value(struct options_array_item *); +union options_value *options_array_item_value(struct options_array_item *); int options_isarray(struct options_entry *); int options_isstring(struct options_entry *); const char *options_tostring(struct options_entry *, int, int); @@ -2234,17 +2271,17 @@ struct window *window_find_by_id_str(const char *); struct window *window_find_by_id(u_int); void window_update_activity(struct window *); struct window *window_create(u_int, u_int); -struct window *window_create_spawn(const char *, int, char **, const char *, - const char *, const char *, struct environ *, - struct termios *, u_int, u_int, u_int, char **); +void window_destroy(struct window *); +void window_pane_set_event(struct window_pane *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); int window_has_pane(struct window *, struct window_pane *); -int window_set_active_pane(struct window *, struct window_pane *); +int window_set_active_pane(struct window *, struct window_pane *, + int); void window_redraw_active_switch(struct window *, struct window_pane *); -struct window_pane *window_add_pane(struct window *, struct window_pane *, int, - int, u_int); +struct window_pane *window_add_pane(struct window *, struct window_pane *, + u_int, int); void window_resize(struct window *, u_int, u_int); int window_zoom(struct window_pane *); int window_unzoom(struct window *); @@ -2261,9 +2298,6 @@ void window_destroy_panes(struct window *); struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); int window_pane_destroy_ready(struct window_pane *); -int window_pane_spawn(struct window_pane *, int, char **, - const char *, const char *, const char *, struct environ *, - struct termios *, char **); void window_pane_resize(struct window_pane *, u_int, u_int); void window_pane_alternate_on(struct window_pane *, struct grid_cell *, int); @@ -2321,7 +2355,7 @@ void layout_resize_pane_to(struct window_pane *, enum layout_type, u_int); void layout_assign_pane(struct layout_cell *, struct window_pane *); struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type, - int, int, int); + int, int); void layout_close_pane(struct window_pane *); int layout_spread_cell(struct window *, struct layout_cell *); void layout_spread_out(struct window_pane *); @@ -2420,10 +2454,9 @@ int session_alive(struct session *); struct session *session_find(const char *); struct session *session_find_by_id_str(const char *); struct session *session_find_by_id(u_int); -struct session *session_create(const char *, const char *, int, char **, - const char *, const char *, struct environ *, - struct options *, struct termios *, int, char **); -void session_destroy(struct session *, const char *); +struct session *session_create(const char *, const char *, const char *, + struct environ *, struct options *, struct termios *); +void session_destroy(struct session *, int, const char *); void session_add_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *); int session_check_name(const char *); @@ -2497,4 +2530,8 @@ void style_set(struct style *, const struct grid_cell *); void style_copy(struct style *, struct style *); int style_is_default(struct style *); +/* spawn.c */ +struct winlink *spawn_window(struct spawn_context *, char **); +struct window_pane *spawn_pane(struct spawn_context *, char **); + #endif /* TMUX_H */ @@ -399,9 +399,10 @@ tty_keys_build(struct tty *tty) const struct tty_default_key_raw *tdkr; const struct tty_default_key_code *tdkc; u_int i; - const char *s, *value; + const char *s; struct options_entry *o; struct options_array_item *a; + union options_value *ov; if (tty->key_tree != NULL) tty_keys_free(tty); @@ -427,9 +428,9 @@ tty_keys_build(struct tty *tty) if (o != NULL) { a = options_array_first(o); while (a != NULL) { - value = options_array_item_value(a); - if (value != NULL) - tty_keys_add(tty, value, KEYC_USER + i); + ov = options_array_item_value(a); + if (ov != NULL) + tty_keys_add(tty, ov->string, KEYC_USER + i); a = options_array_next(a); } } @@ -976,7 +977,7 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, /* Create a new paste buffer. */ log_debug("%s: %.*s", __func__, outlen, out); - paste_add(out, outlen); + paste_add(NULL, out, outlen); return (0); } @@ -420,6 +420,7 @@ tty_term_find(char *name, int fd, char **cause) struct tty_code *code; struct options_entry *o; struct options_array_item *a; + union options_value *ov; u_int i; int n, error; const char *s, *acs; @@ -497,9 +498,9 @@ tty_term_find(char *name, int fd, char **cause) o = options_get_only(global_options, "terminal-overrides"); a = options_array_first(o); while (a != NULL) { - s = options_array_item_value(a); - if (s != NULL) - tty_term_override(term, s); + ov = options_array_item_value(a); + if (ov != NULL) + tty_term_override(term, ov->string); a = options_array_next(a); } @@ -2361,7 +2361,7 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) if (gc->bg & 8) { gc->bg &= 7; if (colours >= 16) - gc->fg += 90; + gc->bg += 90; } } return; @@ -2388,8 +2388,7 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) /* Is this an aixterm bright colour? */ if (gc->fg >= 90 && gc->fg <= 97) { - xsnprintf(s, sizeof s, "\033[%dm", gc->fg); - tty_puts(tty, s); + tty_putcode1(tty, TTYC_SETAF, gc->fg - 90 + 8); goto save_fg; } @@ -2417,8 +2416,7 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) /* Is this an aixterm bright colour? */ if (gc->bg >= 90 && gc->bg <= 97) { - xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10); - tty_puts(tty, s); + tty_putcode1(tty, TTYC_SETAB, gc->bg - 90 + 8); goto save_bg; } diff --git a/window-buffer.c b/window-buffer.c index 79b02e9b..07cdd80d 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -66,6 +66,7 @@ struct window_buffer_itemdata { }; struct window_buffer_modedata { + struct cmd_find_state fs; struct mode_tree_data *data; char *command; char *format; @@ -137,6 +138,9 @@ window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag, struct paste_buffer *pb; char *text, *cp; struct format_tree *ft; + struct session *s = NULL; + struct winlink *wl = NULL; + struct window_pane *wp = NULL; for (i = 0; i < data->item_size; i++) window_buffer_free_item(data->item_list[i]); @@ -167,6 +171,12 @@ window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag, break; } + if (cmd_find_valid_state(&data->fs)) { + s = data->fs.s; + wl = data->fs.wl; + wp = data->fs.wp; + } + for (i = 0; i < data->item_size; i++) { item = data->item_list[i]; @@ -174,6 +184,7 @@ window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag, if (pb == NULL) continue; ft = format_create(NULL, NULL, FORMAT_NONE, 0); + format_defaults(ft, NULL, s, wl, wp); format_defaults_paste_buffer(ft, pb); if (filter != NULL) { @@ -253,14 +264,15 @@ window_buffer_search(__unused void *modedata, void *itemdata, const char *ss) } static struct screen * -window_buffer_init(struct window_mode_entry *wme, - __unused struct cmd_find_state *fs, struct args *args) +window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, + struct args *args) { struct window_pane *wp = wme->wp; struct window_buffer_modedata *data; struct screen *s; wme->data = data = xcalloc(1, sizeof *data); + cmd_find_copy_state(&data->fs, fs); if (args == NULL || !args_has(args, 'F')) data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT); diff --git a/window-copy.c b/window-copy.c index 062d687f..53dd97d3 100644 --- a/window-copy.c +++ b/window-copy.c @@ -79,11 +79,12 @@ static int window_copy_set_selection(struct window_mode_entry *, int); static int window_copy_update_selection(struct window_mode_entry *, int); static void window_copy_synchronize_cursor(struct window_mode_entry *); static void *window_copy_get_selection(struct window_mode_entry *, size_t *); -static void window_copy_copy_buffer(struct window_mode_entry *, void *, - size_t); +static void window_copy_copy_buffer(struct window_mode_entry *, + const char *, void *, size_t); static void window_copy_copy_pipe(struct window_mode_entry *, - struct session *, const char *); -static void window_copy_copy_selection(struct window_mode_entry *); + struct session *, const char *, const char *); +static void window_copy_copy_selection(struct window_mode_entry *, + const char *); static void window_copy_append_selection(struct window_mode_entry *); static void window_copy_clear_selection(struct window_mode_entry *); static void window_copy_copy_line(struct window_mode_entry *, char **, @@ -154,6 +155,22 @@ enum { WINDOW_COPY_REL_POS_BELOW, }; +enum window_copy_cmd_action { + WINDOW_COPY_CMD_NOTHING, + WINDOW_COPY_CMD_REDRAW, + WINDOW_COPY_CMD_CANCEL, +}; + +struct window_copy_cmd_state { + struct window_mode_entry *wme; + struct args *args; + struct mouse_event *m; + + struct client *c; + struct session *s; + struct winlink *wl; +}; + /* * Copy mode's visible screen (the "screen" field) is filled from one of two * sources: the original contents of the pane (used when we actually enter via @@ -565,18 +582,1071 @@ window_copy_key_table(struct window_mode_entry *wme) return ("copy-mode"); } +static enum window_copy_cmd_action +window_copy_cmd_append_selection(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct session *s = cs->s; + + if (s != NULL) + window_copy_append_selection(wme); + window_copy_clear_selection(wme); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct session *s = cs->s; + + if (s != NULL) + window_copy_append_selection(wme); + window_copy_clear_selection(wme); + return (WINDOW_COPY_CMD_CANCEL); +} + +static enum window_copy_cmd_action +window_copy_cmd_back_to_indentation(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_cursor_back_to_indentation(wme); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct client *c = cs->c; + struct mouse_event *m = cs->m; + struct window_copy_mode_data *data = wme->data; + + if (m != NULL) { + window_copy_start_drag(c, m); + return (WINDOW_COPY_CMD_NOTHING); + } + + data->lineflag = LINE_SEL_NONE; + window_copy_start_selection(wme); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + + data->cursordrag = CURSORDRAG_NONE; + data->lineflag = LINE_SEL_NONE; + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + + data->cx = 0; + data->cy = screen_size_y(&data->screen) - 1; + + window_copy_update_selection(wme, 1); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_cancel(__unused struct window_copy_cmd_state *cs) +{ + return (WINDOW_COPY_CMD_CANCEL); +} + +static enum window_copy_cmd_action +window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_clear_selection(wme); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct client *c = cs->c; + struct session *s = cs->s; + struct winlink *wl = cs->wl; + struct window_pane *wp = wme->wp; + u_int np = wme->prefix; + char *prefix = NULL; + + if (cs->args->argc == 2) + prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + + window_copy_start_selection(wme); + for (; np > 1; np--) + window_copy_cursor_down(wme, 0); + window_copy_cursor_end_of_line(wme); + + if (s != NULL) { + window_copy_copy_selection(wme, prefix); + + free(prefix); + return (WINDOW_COPY_CMD_CANCEL); + } + + free(prefix); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_copy_line(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct client *c = cs->c; + struct session *s = cs->s; + struct winlink *wl = cs->wl; + struct window_pane *wp = wme->wp; + u_int np = wme->prefix; + char *prefix = NULL; + + if (cs->args->argc == 2) + prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + + window_copy_cursor_start_of_line(wme); + window_copy_start_selection(wme); + for (; np > 1; np--) + window_copy_cursor_down(wme, 0); + window_copy_cursor_end_of_line(wme); + + if (s != NULL) { + window_copy_copy_selection(wme, prefix); + + free(prefix); + return (WINDOW_COPY_CMD_CANCEL); + } + + free(prefix); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct client *c = cs->c; + struct session *s = cs->s; + struct winlink *wl = cs->wl; + struct window_pane *wp = wme->wp; + char *prefix = NULL; + + if (cs->args->argc == 2) + prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + + if (s != NULL) + window_copy_copy_selection(wme, prefix); + + free(prefix); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_copy_selection(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_cmd_copy_selection_no_clear(cs); + window_copy_clear_selection(wme); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_cmd_copy_selection_no_clear(cs); + window_copy_clear_selection(wme); + return (WINDOW_COPY_CMD_CANCEL); +} + +static enum window_copy_cmd_action +window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_cursor_down(wme, 0); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_cursor_left(wme); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_cursor_right(wme); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_cursor_up(wme, 0); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_cursor_end_of_line(wme); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + for (; np != 0; np--) { + if (window_copy_pagedown(wme, 1, data->scroll_exit)) + return (WINDOW_COPY_CMD_CANCEL); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs) +{ + + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) { + if (window_copy_pagedown(wme, 1, 1)) + return (WINDOW_COPY_CMD_CANCEL); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_pageup1(wme, 1); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + + data->cx = 0; + data->cy = screen_size_y(&data->screen) - 1; + data->oy = 0; + + window_copy_update_selection(wme, 1); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_history_top(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + + data->cx = 0; + data->cy = 0; + data->oy = screen_hsize(data->backing); + + window_copy_update_selection(wme, 1); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_jump_again(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + switch (data->jumptype) { + case WINDOW_COPY_JUMPFORWARD: + for (; np != 0; np--) + window_copy_cursor_jump(wme); + break; + case WINDOW_COPY_JUMPBACKWARD: + for (; np != 0; np--) + window_copy_cursor_jump_back(wme); + break; + case WINDOW_COPY_JUMPTOFORWARD: + for (; np != 0; np--) + window_copy_cursor_jump_to(wme); + break; + case WINDOW_COPY_JUMPTOBACKWARD: + for (; np != 0; np--) + window_copy_cursor_jump_to_back(wme); + break; + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + switch (data->jumptype) { + case WINDOW_COPY_JUMPFORWARD: + for (; np != 0; np--) + window_copy_cursor_jump_back(wme); + break; + case WINDOW_COPY_JUMPBACKWARD: + for (; np != 0; np--) + window_copy_cursor_jump(wme); + break; + case WINDOW_COPY_JUMPTOFORWARD: + for (; np != 0; np--) + window_copy_cursor_jump_to_back(wme); + break; + case WINDOW_COPY_JUMPTOBACKWARD: + for (; np != 0; np--) + window_copy_cursor_jump_to(wme); + break; + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_middle_line(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + + data->cx = 0; + data->cy = (screen_size_y(&data->screen) - 1) / 2; + + window_copy_update_selection(wme, 1); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_next_paragraph(wme); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_next_space(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_cursor_next_word(wme, " "); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_cursor_next_word_end(wme, " "); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_next_word(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct session *s = cs->s; + u_int np = wme->prefix; + const char *ws; + + ws = options_get_string(s->options, "word-separators"); + for (; np != 0; np--) + window_copy_cursor_next_word(wme, ws); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct session *s = cs->s; + u_int np = wme->prefix; + const char *ws; + + ws = options_get_string(s->options, "word-separators"); + for (; np != 0; np--) + window_copy_cursor_next_word_end(wme, ws); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_other_end(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + if ((np % 2) != 0) + window_copy_other_end(wme); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_page_down(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + for (; np != 0; np--) { + if (window_copy_pagedown(wme, 0, data->scroll_exit)) + return (WINDOW_COPY_CMD_CANCEL); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) { + if (window_copy_pagedown(wme, 0, 1)) + return (WINDOW_COPY_CMD_CANCEL); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_page_up(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_pageup1(wme, 0); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_previous_paragraph(wme); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_previous_space(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_cursor_previous_word(wme, " "); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct session *s = cs->s; + u_int np = wme->prefix; + const char *ws; + + ws = options_get_string(s->options, "word-separators"); + for (; np != 0; np--) + window_copy_cursor_previous_word(wme, ws); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + + data->lineflag = LINE_SEL_NONE; + window_copy_rectangle_toggle(wme); + + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_cursor_down(wme, 1); + if (data->scroll_exit && data->oy == 0) + return (WINDOW_COPY_CMD_CANCEL); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_cursor_down(wme, 1); + if (data->oy == 0) + return (WINDOW_COPY_CMD_CANCEL); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + u_int np = wme->prefix; + + for (; np != 0; np--) + window_copy_cursor_up(wme, 1); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_search_again(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + if (data->searchtype == WINDOW_COPY_SEARCHUP) { + for (; np != 0; np--) + window_copy_search_up(wme); + } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { + for (; np != 0; np--) + window_copy_search_down(wme); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + if (data->searchtype == WINDOW_COPY_SEARCHUP) { + for (; np != 0; np--) + window_copy_search_down(wme); + } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { + for (; np != 0; np--) + window_copy_search_up(wme); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_select_line(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + + data->lineflag = LINE_SEL_LEFT_RIGHT; + data->rectflag = 0; + + window_copy_cursor_start_of_line(wme); + window_copy_start_selection(wme); + for (; np > 1; np--) + window_copy_cursor_down(wme, 0); + window_copy_cursor_end_of_line(wme); + + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_select_word(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct session *s = cs->s; + struct window_copy_mode_data *data = wme->data; + const char *ws; + + data->lineflag = LINE_SEL_LEFT_RIGHT; + data->rectflag = 0; + + ws = options_get_string(s->options, "word-separators"); + window_copy_cursor_previous_word(wme, ws); + window_copy_start_selection(wme); + window_copy_cursor_next_word_end(wme, ws); + + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_cursor_start_of_line(wme); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_top_line(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + + data->cx = 0; + data->cy = 0; + + window_copy_update_selection(wme, 1); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct client *c = cs->c; + struct session *s = cs->s; + struct winlink *wl = cs->wl; + struct window_pane *wp = wme->wp; + char *command = NULL; + char *prefix = NULL; + + if (cs->args->argc == 3) + prefix = format_single(NULL, cs->args->argv[2], c, s, wl, wp); + + if (s != NULL && *cs->args->argv[1] != '\0') { + command = format_single(NULL, cs->args->argv[1], c, s, wl, wp); + window_copy_copy_pipe(wme, s, prefix, command); + free(command); + } + + free(prefix); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_cmd_copy_pipe_no_clear(cs); + window_copy_clear_selection(wme); + return (WINDOW_COPY_CMD_REDRAW); +} + +static enum window_copy_cmd_action +window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + + window_copy_cmd_copy_pipe_no_clear(cs); + window_copy_clear_selection(wme); + return (WINDOW_COPY_CMD_CANCEL); +} + +static enum window_copy_cmd_action +window_copy_cmd_goto_line(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + const char *argument = cs->args->argv[1]; + + if (*argument != '\0') + window_copy_goto_line(wme, argument); + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + const char *argument = cs->args->argv[1]; + + if (*argument != '\0') { + data->jumptype = WINDOW_COPY_JUMPBACKWARD; + data->jumpchar = *argument; + for (; np != 0; np--) + window_copy_cursor_jump_back(wme); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + const char *argument = cs->args->argv[1]; + + if (*argument != '\0') { + data->jumptype = WINDOW_COPY_JUMPFORWARD; + data->jumpchar = *argument; + for (; np != 0; np--) + window_copy_cursor_jump(wme); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + const char *argument = cs->args->argv[1]; + + if (*argument != '\0') { + data->jumptype = WINDOW_COPY_JUMPTOBACKWARD; + data->jumpchar = *argument; + for (; np != 0; np--) + window_copy_cursor_jump_to_back(wme); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + const char *argument = cs->args->argv[1]; + + if (*argument != '\0') { + data->jumptype = WINDOW_COPY_JUMPTOFORWARD; + data->jumpchar = *argument; + for (; np != 0; np--) + window_copy_cursor_jump_to(wme); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + const char *argument = cs->args->argv[1]; + + if (*argument != '\0') { + data->searchtype = WINDOW_COPY_SEARCHUP; + free(data->searchstr); + data->searchstr = xstrdup(argument); + for (; np != 0; np--) + window_copy_search_up(wme); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int np = wme->prefix; + const char *argument = cs->args->argv[1]; + + if (*argument != '\0') { + data->searchtype = WINDOW_COPY_SEARCHDOWN; + free(data->searchstr); + data->searchstr = xstrdup(argument); + for (; np != 0; np--) + window_copy_search_down(wme); + } + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + const char *argument = cs->args->argv[1]; + enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; + const char *ss = data->searchstr; + + if (data->searchx == -1 || data->searchy == -1) { + data->searchx = data->cx; + data->searchy = data->cy; + data->searcho = data->oy; + } else if (ss != NULL && strcmp(argument, ss) != 0) { + data->cx = data->searchx; + data->cy = data->searchy; + data->oy = data->searcho; + action = WINDOW_COPY_CMD_REDRAW; + } + + if (*argument == '\0') { + window_copy_clear_marks(wme); + return (WINDOW_COPY_CMD_REDRAW); + } + + switch (*argument++) { + case '=': + case '-': + data->searchtype = WINDOW_COPY_SEARCHUP; + free(data->searchstr); + data->searchstr = xstrdup(argument); + if (!window_copy_search_up(wme)) { + window_copy_clear_marks(wme); + return (WINDOW_COPY_CMD_REDRAW); + } + break; + case '+': + data->searchtype = WINDOW_COPY_SEARCHDOWN; + free(data->searchstr); + data->searchstr = xstrdup(argument); + if (!window_copy_search_down(wme)) { + window_copy_clear_marks(wme); + return (WINDOW_COPY_CMD_REDRAW); + } + break; + } + return (action); +} + +static enum window_copy_cmd_action +window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + const char *argument = cs->args->argv[1]; + enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; + const char *ss = data->searchstr; + + if (data->searchx == -1 || data->searchy == -1) { + data->searchx = data->cx; + data->searchy = data->cy; + data->searcho = data->oy; + } else if (ss != NULL && strcmp(argument, ss) != 0) { + data->cx = data->searchx; + data->cy = data->searchy; + data->oy = data->searcho; + action = WINDOW_COPY_CMD_REDRAW; + } + + if (*argument == '\0') { + window_copy_clear_marks(wme); + return (WINDOW_COPY_CMD_REDRAW); + } + + switch (*argument++) { + case '=': + case '+': + data->searchtype = WINDOW_COPY_SEARCHDOWN; + free(data->searchstr); + data->searchstr = xstrdup(argument); + if (!window_copy_search_down(wme)) { + window_copy_clear_marks(wme); + return (WINDOW_COPY_CMD_REDRAW); + } + break; + case '-': + data->searchtype = WINDOW_COPY_SEARCHUP; + free(data->searchstr); + data->searchstr = xstrdup(argument); + if (!window_copy_search_up(wme)) { + window_copy_clear_marks(wme); + return (WINDOW_COPY_CMD_REDRAW); + } + } + return (action); +} + +static const struct { + const char *command; + int minargs; + int maxargs; + enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *); +} window_copy_cmd_table[] = { + { "append-selection", 0, 0, + window_copy_cmd_append_selection }, + { "append-selection-and-cancel", 0, 0, + window_copy_cmd_append_selection_and_cancel }, + { "back-to-indentation", 0, 0, + window_copy_cmd_back_to_indentation }, + { "begin-selection", 0, 0, + window_copy_cmd_begin_selection }, + { "bottom-line", 0, 0, + window_copy_cmd_bottom_line }, + { "cancel", 0, 0, + window_copy_cmd_cancel }, + { "clear-selection", 0, 0, + window_copy_cmd_clear_selection }, + { "copy-end-of-line", 0, 1, + window_copy_cmd_copy_end_of_line }, + { "copy-line", 0, 1, + window_copy_cmd_copy_line }, + { "copy-pipe-no-clear", 1, 2, + window_copy_cmd_copy_pipe_no_clear }, + { "copy-pipe", 1, 2, + window_copy_cmd_copy_pipe }, + { "copy-pipe-and-cancel", 1, 2, + window_copy_cmd_copy_pipe_and_cancel }, + { "copy-selection-no-clear", 0, 1, + window_copy_cmd_copy_selection_no_clear }, + { "copy-selection", 0, 1, + window_copy_cmd_copy_selection }, + { "copy-selection-and-cancel", 0, 1, + window_copy_cmd_copy_selection_and_cancel }, + { "cursor-down", 0, 0, + window_copy_cmd_cursor_down }, + { "cursor-left", 0, 0, + window_copy_cmd_cursor_left }, + { "cursor-right", 0, 0, + window_copy_cmd_cursor_right }, + { "cursor-up", 0, 0, + window_copy_cmd_cursor_up }, + { "end-of-line", 0, 0, + window_copy_cmd_end_of_line }, + { "goto-line", 1, 1, + window_copy_cmd_goto_line }, + { "halfpage-down", 0, 0, + window_copy_cmd_halfpage_down }, + { "halfpage-down-and-cancel", 0, 0, + window_copy_cmd_halfpage_down_and_cancel }, + { "halfpage-up", 0, 0, + window_copy_cmd_halfpage_up }, + { "history-bottom", 0, 0, + window_copy_cmd_history_bottom }, + { "history-top", 0, 0, + window_copy_cmd_history_top }, + { "jump-again", 0, 0, + window_copy_cmd_jump_again }, + { "jump-backward", 1, 1, + window_copy_cmd_jump_backward }, + { "jump-forward", 1, 1, + window_copy_cmd_jump_forward }, + { "jump-reverse", 0, 0, + window_copy_cmd_jump_reverse }, + { "jump-to-backward", 1, 1, + window_copy_cmd_jump_to_backward }, + { "jump-to-forward", 1, 1, + window_copy_cmd_jump_to_forward }, + { "middle-line", 0, 0, + window_copy_cmd_middle_line }, + { "next-paragraph", 0, 0, + window_copy_cmd_next_paragraph }, + { "next-space", 0, 0, + window_copy_cmd_next_space }, + { "next-space-end", 0, 0, + window_copy_cmd_next_space_end }, + { "next-word", 0, 0, + window_copy_cmd_next_word }, + { "next-word-end", 0, 0, + window_copy_cmd_next_word_end }, + { "other-end", 0, 0, + window_copy_cmd_other_end }, + { "page-down", 0, 0, + window_copy_cmd_page_down }, + { "page-down-and-cancel", 0, 0, + window_copy_cmd_page_down_and_cancel }, + { "page-up", 0, 0, + window_copy_cmd_page_up }, + { "previous-paragraph", 0, 0, + window_copy_cmd_previous_paragraph }, + { "previous-space", 0, 0, + window_copy_cmd_previous_space }, + { "previous-word", 0, 0, + window_copy_cmd_previous_word }, + { "rectangle-toggle", 0, 0, + window_copy_cmd_rectangle_toggle }, + { "scroll-down", 0, 0, + window_copy_cmd_scroll_down }, + { "scroll-down-and-cancel", 0, 0, + window_copy_cmd_scroll_down_and_cancel }, + { "scroll-up", 0, 0, + window_copy_cmd_scroll_up }, + { "search-again", 0, 0, + window_copy_cmd_search_again }, + { "search-backward", 1, 1, + window_copy_cmd_search_backward }, + { "search-backward-incremental", 1, 1, + window_copy_cmd_search_backward_incremental }, + { "search-forward", 1, 1, + window_copy_cmd_search_forward }, + { "search-forward-incremental", 1, 1, + window_copy_cmd_search_forward_incremental }, + { "search-reverse", 0, 0, + window_copy_cmd_search_reverse }, + { "select-line", 0, 0, + window_copy_cmd_select_line }, + { "select-word", 0, 0, + window_copy_cmd_select_word }, + { "start-of-line", 0, 0, + window_copy_cmd_start_of_line }, + { "stop-selection", 0, 0, + window_copy_cmd_stop_selection }, + { "top-line", 0, 0, + window_copy_cmd_top_line }, +}; + static void window_copy_command(struct window_mode_entry *wme, struct client *c, - struct session *s, __unused struct winlink *wl, struct args *args, + struct session *s, struct winlink *wl, struct args *args, struct mouse_event *m) { - struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; - struct screen *sn = &data->screen; - const char *command, *argument, *ws; - u_int np = wme->prefix; - int cancel = 0, redraw = 0, scroll_exit; - char prefix; + struct window_copy_cmd_state cs; + enum window_copy_cmd_action action; + const char *command; + u_int i; if (args->argc == 0) return; @@ -585,431 +1655,36 @@ window_copy_command(struct window_mode_entry *wme, struct client *c, if (m != NULL && m->valid && !MOUSE_WHEEL(m->b)) window_copy_move_mouse(m); - if (args->argc == 1) { - if (strcmp(command, "append-selection") == 0) { - if (s != NULL) - window_copy_append_selection(wme); - window_copy_clear_selection(wme); - redraw = 1; - } - if (strcmp(command, "append-selection-and-cancel") == 0) { - if (s != NULL) - window_copy_append_selection(wme); - window_copy_clear_selection(wme); - redraw = 1; - cancel = 1; - } - if (strcmp(command, "back-to-indentation") == 0) - window_copy_cursor_back_to_indentation(wme); - if (strcmp(command, "begin-selection") == 0) { - if (m != NULL) - window_copy_start_drag(c, m); - else { - data->lineflag = LINE_SEL_NONE; - window_copy_start_selection(wme); - redraw = 1; - } - } - if (strcmp(command, "stop-selection") == 0) - data->cursordrag = CURSORDRAG_NONE; - if (strcmp(command, "bottom-line") == 0) { - data->cx = 0; - data->cy = screen_size_y(sn) - 1; - window_copy_update_selection(wme, 1); - redraw = 1; - } - if (strcmp(command, "cancel") == 0) - cancel = 1; - if (strcmp(command, "clear-selection") == 0) { - window_copy_clear_selection(wme); - redraw = 1; - } - if (strcmp(command, "copy-end-of-line") == 0) { - window_copy_start_selection(wme); - for (; np > 1; np--) - window_copy_cursor_down(wme, 0); - window_copy_cursor_end_of_line(wme); - redraw = 1; + cs.wme = wme; + cs.args = args; + cs.m = m; - if (s != NULL) { - window_copy_copy_selection(wme); - cancel = 1; - } - } - if (strcmp(command, "copy-line") == 0) { - window_copy_cursor_start_of_line(wme); - window_copy_start_selection(wme); - for (; np > 1; np--) - window_copy_cursor_down(wme, 0); - window_copy_cursor_end_of_line(wme); - redraw = 1; + cs.c = c; + cs.s = s; + cs.wl = wl; - if (s != NULL) { - window_copy_copy_selection(wme); - cancel = 1; - } - } - if (strcmp(command, "copy-selection") == 0) { - if (s != NULL) - window_copy_copy_selection(wme); - window_copy_clear_selection(wme); - redraw = 1; - } - if (strcmp(command, "copy-selection-and-cancel") == 0) { - if (s != NULL) - window_copy_copy_selection(wme); - window_copy_clear_selection(wme); - redraw = 1; - cancel = 1; - } - if (strcmp(command, "cursor-down") == 0) { - for (; np != 0; np--) - window_copy_cursor_down(wme, 0); - } - if (strcmp(command, "cursor-left") == 0) { - for (; np != 0; np--) - window_copy_cursor_left(wme); - } - if (strcmp(command, "cursor-right") == 0) { - for (; np != 0; np--) - window_copy_cursor_right(wme); - } - if (strcmp(command, "cursor-up") == 0) { - for (; np != 0; np--) - window_copy_cursor_up(wme, 0); - } - if (strcmp(command, "end-of-line") == 0) - window_copy_cursor_end_of_line(wme); - if (strcmp(command, "halfpage-down") == 0 || - strcmp(command, "halfpage-down-and-cancel") == 0) { - if (strcmp(command, "halfpage-down-and-cancel") == 0) - scroll_exit = 1; - else - scroll_exit = data->scroll_exit; - for (; np != 0; np--) { - if (window_copy_pagedown(wme, 1, scroll_exit)) { - cancel = 1; - break; - } - } - } - if (strcmp(command, "halfpage-up") == 0) { - for (; np != 0; np--) - window_copy_pageup1(wme, 1); - } - if (strcmp(command, "history-bottom") == 0) { - data->cx = 0; - data->cy = screen_size_y(sn) - 1; - data->oy = 0; - window_copy_update_selection(wme, 1); - redraw = 1; - } - if (strcmp(command, "history-top") == 0) { - data->cx = 0; - data->cy = 0; - data->oy = screen_hsize(data->backing); - window_copy_update_selection(wme, 1); - redraw = 1; - } - if (strcmp(command, "jump-again") == 0) { - switch (data->jumptype) { - case WINDOW_COPY_JUMPFORWARD: - for (; np != 0; np--) - window_copy_cursor_jump(wme); - break; - case WINDOW_COPY_JUMPBACKWARD: - for (; np != 0; np--) - window_copy_cursor_jump_back(wme); + action = WINDOW_COPY_CMD_NOTHING; + for (i = 0; i < nitems(window_copy_cmd_table); i++) { + if (strcmp(window_copy_cmd_table[i].command, command) == 0) { + if (args->argc - 1 < window_copy_cmd_table[i].minargs || + args->argc - 1 > window_copy_cmd_table[i].maxargs) break; - case WINDOW_COPY_JUMPTOFORWARD: - for (; np != 0; np--) - window_copy_cursor_jump_to(wme); - break; - case WINDOW_COPY_JUMPTOBACKWARD: - for (; np != 0; np--) - window_copy_cursor_jump_to_back(wme); - break; - } - } - if (strcmp(command, "jump-reverse") == 0) { - switch (data->jumptype) { - case WINDOW_COPY_JUMPFORWARD: - for (; np != 0; np--) - window_copy_cursor_jump_back(wme); - break; - case WINDOW_COPY_JUMPBACKWARD: - for (; np != 0; np--) - window_copy_cursor_jump(wme); - break; - case WINDOW_COPY_JUMPTOFORWARD: - for (; np != 0; np--) - window_copy_cursor_jump_to_back(wme); - break; - case WINDOW_COPY_JUMPTOBACKWARD: - for (; np != 0; np--) - window_copy_cursor_jump_to(wme); - break; - } - } - if (strcmp(command, "middle-line") == 0) { - data->cx = 0; - data->cy = (screen_size_y(sn) - 1) / 2; - window_copy_update_selection(wme, 1); - redraw = 1; - } - if (strcmp(command, "next-paragraph") == 0) { - for (; np != 0; np--) - window_copy_next_paragraph(wme); - } - if (strcmp(command, "next-space") == 0) { - for (; np != 0; np--) - window_copy_cursor_next_word(wme, " "); - } - if (strcmp(command, "next-space-end") == 0) { - for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, " "); - } - if (strcmp(command, "next-word") == 0) { - ws = options_get_string(s->options, "word-separators"); - for (; np != 0; np--) - window_copy_cursor_next_word(wme, ws); - } - if (strcmp(command, "next-word-end") == 0) { - ws = options_get_string(s->options, "word-separators"); - for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, ws); - } - if (strcmp(command, "other-end") == 0) { - if ((np % 2) != 0) - window_copy_other_end(wme); - } - if (strcmp(command, "page-down") == 0 || - strcmp(command, "page-down-and-cancel") == 0) { - if (strcmp(command, "page-down-and-cancel") == 0) - scroll_exit = 1; - else - scroll_exit = data->scroll_exit; - for (; np != 0; np--) { - if (window_copy_pagedown(wme, 0, scroll_exit)) { - cancel = 1; - break; - } - } - } - if (strcmp(command, "page-up") == 0) { - for (; np != 0; np--) - window_copy_pageup1(wme, 0); - } - if (strcmp(command, "previous-paragraph") == 0) { - for (; np != 0; np--) - window_copy_previous_paragraph(wme); - } - if (strcmp(command, "previous-space") == 0) { - for (; np != 0; np--) - window_copy_cursor_previous_word(wme, " "); - } - if (strcmp(command, "previous-word") == 0) { - ws = options_get_string(s->options, "word-separators"); - for (; np != 0; np--) - window_copy_cursor_previous_word(wme, ws); - } - if (strcmp(command, "rectangle-toggle") == 0) { - data->lineflag = LINE_SEL_NONE; - window_copy_rectangle_toggle(wme); - } - if (strcmp(command, "scroll-down") == 0 || - strcmp(command, "scroll-down-and-cancel") == 0) { - if (strcmp(command, "scroll-down-and-cancel") == 0) - scroll_exit = 1; - else - scroll_exit = data->scroll_exit; - for (; np != 0; np--) - window_copy_cursor_down(wme, 1); - if (scroll_exit && data->oy == 0) - cancel = 1; - } - if (strcmp(command, "scroll-up") == 0) { - for (; np != 0; np--) - window_copy_cursor_up(wme, 1); - } - if (strcmp(command, "search-again") == 0) { - if (data->searchtype == WINDOW_COPY_SEARCHUP) { - for (; np != 0; np--) - window_copy_search_up(wme); - } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { - for (; np != 0; np--) - window_copy_search_down(wme); - } - } - if (strcmp(command, "search-reverse") == 0) { - if (data->searchtype == WINDOW_COPY_SEARCHUP) { - for (; np != 0; np--) - window_copy_search_down(wme); - } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { - for (; np != 0; np--) - window_copy_search_up(wme); - } - } - if (strcmp(command, "select-line") == 0) { - data->lineflag = LINE_SEL_LEFT_RIGHT; - data->rectflag = 0; - window_copy_cursor_start_of_line(wme); - window_copy_start_selection(wme); - for (; np > 1; np--) - window_copy_cursor_down(wme, 0); - window_copy_cursor_end_of_line(wme); - redraw = 1; - } - if (strcmp(command, "select-word") == 0) { - data->lineflag = LINE_SEL_LEFT_RIGHT; - data->rectflag = 0; - ws = options_get_string(s->options, "word-separators"); - window_copy_cursor_previous_word(wme, ws); - window_copy_start_selection(wme); - window_copy_cursor_next_word_end(wme, ws); - redraw = 1; - } - if (strcmp(command, "start-of-line") == 0) - window_copy_cursor_start_of_line(wme); - if (strcmp(command, "top-line") == 0) { - data->cx = 0; - data->cy = 0; - window_copy_update_selection(wme, 1); - redraw = 1; - } - } else if (args->argc == 2 && *args->argv[1] != '\0') { - argument = args->argv[1]; - if (strcmp(command, "copy-pipe") == 0) { - if (s != NULL) - window_copy_copy_pipe(wme, s, argument); - } - if (strcmp(command, "copy-pipe-and-cancel") == 0) { - if (s != NULL) { - window_copy_copy_pipe(wme, s, argument); - cancel = 1; - } - } - if (strcmp(command, "goto-line") == 0) - window_copy_goto_line(wme, argument); - if (strcmp(command, "jump-backward") == 0) { - data->jumptype = WINDOW_COPY_JUMPBACKWARD; - data->jumpchar = *argument; - for (; np != 0; np--) - window_copy_cursor_jump_back(wme); - } - if (strcmp(command, "jump-forward") == 0) { - data->jumptype = WINDOW_COPY_JUMPFORWARD; - data->jumpchar = *argument; - for (; np != 0; np--) - window_copy_cursor_jump(wme); - } - if (strcmp(command, "jump-to-backward") == 0) { - data->jumptype = WINDOW_COPY_JUMPTOBACKWARD; - data->jumpchar = *argument; - for (; np != 0; np--) - window_copy_cursor_jump_to_back(wme); - } - if (strcmp(command, "jump-to-forward") == 0) { - data->jumptype = WINDOW_COPY_JUMPTOFORWARD; - data->jumpchar = *argument; - for (; np != 0; np--) - window_copy_cursor_jump_to(wme); - } - if (strcmp(command, "search-backward") == 0) { - data->searchtype = WINDOW_COPY_SEARCHUP; - free(data->searchstr); - data->searchstr = xstrdup(argument); - for (; np != 0; np--) - window_copy_search_up(wme); - } - if (strcmp(command, "search-forward") == 0) { - data->searchtype = WINDOW_COPY_SEARCHDOWN; - free(data->searchstr); - data->searchstr = xstrdup(argument); - for (; np != 0; np--) - window_copy_search_down(wme); - } - if (strcmp(command, "search-backward-incremental") == 0) { - prefix = *argument++; - if (data->searchx == -1 || data->searchy == -1) { - data->searchx = data->cx; - data->searchy = data->cy; - data->searcho = data->oy; - } else if (data->searchstr != NULL && - strcmp(argument, data->searchstr) != 0) { - data->cx = data->searchx; - data->cy = data->searchy; - data->oy = data->searcho; - redraw = 1; - } - if (*argument == '\0') { - window_copy_clear_marks(wme); - redraw = 1; - } else if (prefix == '=' || prefix == '-') { - data->searchtype = WINDOW_COPY_SEARCHUP; - free(data->searchstr); - data->searchstr = xstrdup(argument); - if (!window_copy_search_up(wme)) { - window_copy_clear_marks(wme); - redraw = 1; - } - } else if (prefix == '+') { - data->searchtype = WINDOW_COPY_SEARCHDOWN; - free(data->searchstr); - data->searchstr = xstrdup(argument); - if (!window_copy_search_down(wme)) { - window_copy_clear_marks(wme); - redraw = 1; - } - } - } - if (strcmp(command, "search-forward-incremental") == 0) { - prefix = *argument++; - if (data->searchx == -1 || data->searchy == -1) { - data->searchx = data->cx; - data->searchy = data->cy; - data->searcho = data->oy; - } else if (data->searchstr != NULL && - strcmp(argument, data->searchstr) != 0) { - data->cx = data->searchx; - data->cy = data->searchy; - data->oy = data->searcho; - redraw = 1; - } - if (*argument == '\0') { - window_copy_clear_marks(wme); - redraw = 1; - } else if (prefix == '=' || prefix == '+') { - data->searchtype = WINDOW_COPY_SEARCHDOWN; - free(data->searchstr); - data->searchstr = xstrdup(argument); - if (!window_copy_search_down(wme)) { - window_copy_clear_marks(wme); - redraw = 1; - } - } else if (prefix == '-') { - data->searchtype = WINDOW_COPY_SEARCHUP; - free(data->searchstr); - data->searchstr = xstrdup(argument); - if (!window_copy_search_up(wme)) { - window_copy_clear_marks(wme); - redraw = 1; - } - } + action = window_copy_cmd_table[i].f (&cs); + break; } } if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) { window_copy_clear_marks(wme); - redraw = 1; + if (action == WINDOW_COPY_CMD_NOTHING) + action = WINDOW_COPY_CMD_REDRAW; data->searchx = data->searchy = -1; } - wme->prefix = 1; - if (cancel) - window_pane_reset_mode(wp); - else if (redraw) + if (action == WINDOW_COPY_CMD_CANCEL) + window_pane_reset_mode(wme->wp); + else if (action == WINDOW_COPY_CMD_REDRAW) window_copy_redraw_screen(wme); } @@ -1721,7 +2396,8 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len) } static void -window_copy_copy_buffer(struct window_mode_entry *wme, void *buf, size_t len) +window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, + void *buf, size_t len) { struct window_pane *wp = wme->wp; struct screen_write_ctx ctx; @@ -1733,41 +2409,35 @@ window_copy_copy_buffer(struct window_mode_entry *wme, void *buf, size_t len) notify_pane("pane-set-clipboard", wp); } - if (paste_set(buf, len, NULL, NULL) != 0) - free(buf); + paste_add(prefix, buf, len); } static void window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, - const char *fmt) + const char *prefix, const char *command) { - struct window_pane *wp = wme->wp; - void *buf; - size_t len; - struct job *job; - char *expanded; + void *buf; + size_t len; + struct job *job; buf = window_copy_get_selection(wme, &len); if (buf == NULL) return; - expanded = format_single(NULL, fmt, NULL, s, NULL, wp); - job = job_run(expanded, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT); + job = job_run(command, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT); bufferevent_write(job_get_event(job), buf, len); - - free(expanded); - window_copy_copy_buffer(wme, buf, len); + window_copy_copy_buffer(wme, prefix, buf, len); } static void -window_copy_copy_selection(struct window_mode_entry *wme) +window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix) { char *buf; size_t len; buf = window_copy_get_selection(wme, &len); if (buf != NULL) - window_copy_copy_buffer(wme, buf, len); + window_copy_copy_buffer(wme, prefix, buf, len); } static void diff --git a/window-tree.c b/window-tree.c index 55dda694..4d73aeab 100644 --- a/window-tree.c +++ b/window-tree.c @@ -1006,7 +1006,7 @@ window_tree_kill_each(__unused void* modedata, void* itemdata, case WINDOW_TREE_SESSION: if (s != NULL) { server_destroy_session(s); - session_destroy(s, __func__); + session_destroy(s, 1, __func__); } break; case WINDOW_TREE_WINDOW: @@ -26,7 +26,6 @@ #include <stdint.h> #include <stdlib.h> #include <string.h> -#include <termios.h> #include <time.h> #include <unistd.h> @@ -71,20 +70,10 @@ const struct window_mode *all_window_modes[] = { NULL }; -static void window_destroy(struct window *); - static struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); static void window_pane_destroy(struct window_pane *); -static void window_pane_read_callback(struct bufferevent *, void *); -static void window_pane_error_callback(struct bufferevent *, short, void *); - -static int winlink_next_index(struct winlinks *, int); - -static struct window_pane *window_pane_choose_best(struct window_pane **, - u_int); - RB_GENERATE(windows, window, entry, window_cmp); RB_GENERATE(winlinks, winlink, entry, winlink_cmp); RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); @@ -340,37 +329,7 @@ window_create(u_int sx, u_int sy) return (w); } -struct window * -window_create_spawn(const char *name, int argc, char **argv, const char *path, - const char *shell, const char *cwd, struct environ *env, - struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause) -{ - struct window *w; - struct window_pane *wp; - - w = window_create(sx, sy); - wp = window_add_pane(w, NULL, 0, 0, hlimit); - layout_init(w, wp); - - if (window_pane_spawn(wp, argc, argv, path, shell, cwd, - env, tio, cause) != 0) { - window_destroy(w); - return (NULL); - } - - w->active = TAILQ_FIRST(&w->panes); - if (name != NULL) { - w->name = xstrdup(name); - options_set_number(w->options, "automatic-rename", 0); - } else - w->name = default_window_name(w); - - notify_window("window-pane-changed", w); - - return (w); -} - -static void +void window_destroy(struct window *w) { log_debug("window @%u destroyed (%d references)", w->id, w->references); @@ -461,17 +420,22 @@ window_has_pane(struct window *w, struct window_pane *wp) } int -window_set_active_pane(struct window *w, struct window_pane *wp) +window_set_active_pane(struct window *w, struct window_pane *wp, int notify) { - log_debug("%s: pane %%%u (was %%%u)", __func__, wp->id, w->active->id); + log_debug("%s: pane %%%u", __func__, wp->id); + if (wp == w->active) return (0); w->last = w->active; + w->active = wp; w->active->active_point = next_active_point++; w->active->flags |= PANE_CHANGED; + tty_update_window_offset(w); - notify_window("window-pane-changed", w); + + if (notify) + notify_window("window-pane-changed", w); return (1); } @@ -569,7 +533,7 @@ window_zoom(struct window_pane *wp) return (-1); if (w->active != wp) - window_set_active_pane(w, wp); + window_set_active_pane(w, wp, 1); TAILQ_FOREACH(wp1, &w->panes, entry) { wp1->saved_layout_cell = wp1->layout_cell; @@ -608,8 +572,8 @@ window_unzoom(struct window *w) } struct window_pane * -window_add_pane(struct window *w, struct window_pane *other, int before, - int full_size, u_int hlimit) +window_add_pane(struct window *w, struct window_pane *other, u_int hlimit, + int flags) { struct window_pane *wp; @@ -620,15 +584,15 @@ window_add_pane(struct window *w, struct window_pane *other, int before, if (TAILQ_EMPTY(&w->panes)) { log_debug("%s: @%u at start", __func__, w->id); TAILQ_INSERT_HEAD(&w->panes, wp, entry); - } else if (before) { + } else if (flags & SPAWN_BEFORE) { log_debug("%s: @%u before %%%u", __func__, w->id, wp->id); - if (full_size) + if (flags & SPAWN_FULLSIZE) TAILQ_INSERT_HEAD(&w->panes, wp, entry); else TAILQ_INSERT_BEFORE(other, wp, entry); } else { log_debug("%s: @%u after %%%u", __func__, w->id, wp->id); - if (full_size) + if (flags & SPAWN_FULLSIZE) TAILQ_INSERT_TAIL(&w->panes, wp, entry); else TAILQ_INSERT_AFTER(&w->panes, other, wp, entry); @@ -888,153 +852,6 @@ window_pane_destroy(struct window_pane *wp) free(wp); } -int -window_pane_spawn(struct window_pane *wp, int argc, char **argv, - const char *path, const char *shell, const char *cwd, struct environ *env, - struct termios *tio, char **cause) -{ - struct winsize ws; - char *argv0, *cmd, **argvp; - const char *ptr, *first, *home; - struct termios tio2; -#ifdef HAVE_UTEMPTER - char s[32]; -#endif - sigset_t set, oldset; - - if (wp->fd != -1) { - bufferevent_free(wp->event); - close(wp->fd); - } - 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); - wp->shell = xstrdup(shell); - } - if (cwd != NULL) { - free((void *)wp->cwd); - wp->cwd = xstrdup(cwd); - } - wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN); - - cmd = cmd_stringify_argv(wp->argc, wp->argv); - log_debug("%s: shell=%s", __func__, wp->shell); - log_debug("%s: cmd=%s", __func__, cmd); - log_debug("%s: cwd=%s", __func__, cwd); - cmd_log_argv(wp->argc, wp->argv, __func__); - environ_log(env, "%s: environment ", __func__); - - memset(&ws, 0, sizeof ws); - ws.ws_col = screen_size_x(&wp->base); - ws.ws_row = screen_size_y(&wp->base); - - sigfillset(&set); - sigprocmask(SIG_BLOCK, &set, &oldset); - switch (wp->pid = fdforkpty(ptm_fd, &wp->fd, wp->tty, NULL, &ws)) { - case -1: - wp->event = NULL; - wp->fd = -1; - - xasprintf(cause, "%s: %s", cmd, strerror(errno)); - free(cmd); - - sigprocmask(SIG_SETMASK, &oldset, NULL); - return (-1); - case 0: - proc_clear_signals(server_proc, 1); - sigprocmask(SIG_SETMASK, &oldset, NULL); - - cwd = NULL; - if (chdir(wp->cwd) == 0) - cwd = wp->cwd; - else if ((home = find_home()) != NULL && chdir(home) == 0) - cwd = home; - else - chdir("/"); - - if (tcgetattr(STDIN_FILENO, &tio2) != 0) - fatal("tcgetattr failed"); - if (tio != NULL) - memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc); - tio2.c_cc[VERASE] = '\177'; -#ifdef IUTF8 - tio2.c_iflag |= IUTF8; -#endif - if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0) - fatal("tcgetattr failed"); - - log_close(); - closefrom(STDERR_FILENO + 1); - - if (path != NULL) - environ_set(env, "PATH", "%s", path); - if (cwd != NULL) - environ_set(env, "PWD", "%s", cwd); - environ_set(env, "TMUX_PANE", "%%%u", wp->id); - environ_push(env); - - setenv("SHELL", wp->shell, 1); - ptr = strrchr(wp->shell, '/'); - - /* - * 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", first, (char *)NULL); - fatal("execl failed"); - } - if (ptr != NULL && *(ptr + 1) != '\0') - xasprintf(&argv0, "-%s", ptr + 1); - else - xasprintf(&argv0, "-%s", wp->shell); - execl(wp->shell, argv0, (char *)NULL); - fatal("execl failed"); - } - log_debug("%s: master=%s", __func__, ttyname(wp->fd)); - log_debug("%s: slave=%s", __func__, wp->tty); - -#ifdef HAVE_UTEMPTER - xsnprintf(s, sizeof s, "tmux(%lu).%%%u", (long) getpid(), wp->id); - utempter_add_record(wp->fd, s); - kill(getpid(), SIGCHLD); -#endif - - sigprocmask(SIG_SETMASK, &oldset, NULL); - setblocking(wp->fd, 0); - - wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, - window_pane_error_callback, wp); - if (wp->event == NULL) - fatalx("out of memory"); - - wp->pipe_off = 0; - wp->flags &= ~PANE_EXITED; - - bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); - bufferevent_enable(wp->event, EV_READ|EV_WRITE); - - free(cmd); - return (0); -} - static void window_pane_read_callback(__unused struct bufferevent *bufev, void *data) { @@ -1070,6 +887,18 @@ window_pane_error_callback(__unused struct bufferevent *bufev, } void +window_pane_set_event(struct window_pane *wp) +{ + setblocking(wp->fd, 0); + + wp->event = bufferevent_new(wp->fd, window_pane_read_callback, + NULL, window_pane_error_callback, wp); + + bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); + bufferevent_enable(wp->event, EV_READ|EV_WRITE); +} + +void window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window_mode_entry *wme; @@ -1617,6 +1446,8 @@ winlink_shuffle_up(struct session *s, struct winlink *wl) { int idx, last; + if (wl == NULL) + return (-1); idx = wl->idx + 1; /* Find the next free index. */ |