From 85338bb75fa4fbabfdbefeb995bdea04efaf2db4 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Jan 2017 19:53:37 +0000 Subject: Add support for custom command aliases, this is an array option which contains items of the form "alias=command". This is consulted when an unknown command is parsed. --- client.c | 15 +++++------ cmd-string.c | 82 +++++++++++++++++++++++++++++++++++++--------------------- key-bindings.c | 2 +- pty.c | 2 +- tmux.1 | 24 +++++++++++++++++ tmux.h | 1 + 6 files changed, 85 insertions(+), 41 deletions(-) diff --git a/client.c b/client.c index 90b1371c..f94ce996 100644 --- a/client.c +++ b/client.c @@ -252,16 +252,13 @@ client_main(struct event_base *base, int argc, char **argv, int flags, * flag. */ cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause); - if (cmdlist == NULL) { - fprintf(stderr, "%s\n", cause); - return (1); - } - cmdflags &= ~CMD_STARTSERVER; - TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { - if (cmd->entry->flags & CMD_STARTSERVER) - cmdflags |= CMD_STARTSERVER; + if (cmdlist != NULL) { + TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { + if (cmd->entry->flags & CMD_STARTSERVER) + cmdflags |= CMD_STARTSERVER; + } + cmd_list_free(cmdlist); } - cmd_list_free(cmdlist); } /* Create client process structure (starts logging). */ diff --git a/cmd-string.c b/cmd-string.c index 1b96b280..822d35b5 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -54,17 +54,15 @@ cmd_string_ungetc(size_t *p) (*p)--; } -struct cmd_list * -cmd_string_parse(const char *s, const char *file, u_int line, char **cause) +int +cmd_string_split(const char *s, int *rargc, char ***rargv) { - size_t p = 0; - int ch, i, argc = 0; - char **argv = NULL, *buf = NULL, *t; - const char *whitespace, *equals; - size_t len = 0; - struct cmd_list *cmdlist = NULL; + size_t p = 0; + int ch, argc = 0, append = 0; + char **argv = NULL, *buf = NULL, *t; + const char *whitespace, *equals; + size_t len = 0; - *cause = NULL; for (;;) { ch = cmd_string_getc(s, &p); switch (ch) { @@ -115,43 +113,67 @@ cmd_string_parse(const char *s, const char *file, u_int line, char **cause) argc--; memmove(argv, argv + 1, argc * (sizeof *argv)); } - if (argc == 0) - goto out; - - cmdlist = cmd_list_parse(argc, argv, file, line, cause); - goto out; + goto done; case '~': - if (buf == NULL) { - t = cmd_string_expand_tilde(s, &p); - if (t == NULL) - goto error; - cmd_string_copy(&buf, t, &len); + if (buf != NULL) { + append = 1; break; } - /* FALLTHROUGH */ + t = cmd_string_expand_tilde(s, &p); + if (t == NULL) + goto error; + cmd_string_copy(&buf, t, &len); + break; default: + append = 1; + break; + } + if (append) { if (len >= SIZE_MAX - 2) goto error; - buf = xrealloc(buf, len + 1); buf[len++] = ch; - break; } + append = 0; } -error: - xasprintf(cause, "invalid or unknown command: %s", s); +done: + *rargc = argc; + *rargv = argv; -out: free(buf); + return (0); - if (argv != NULL) { - for (i = 0; i < argc; i++) - free(argv[i]); - free(argv); - } +error: + if (argv != NULL) + cmd_free_argv(argc, argv); + free(buf); + return (-1); +} +struct cmd_list * +cmd_string_parse(const char *s, const char *file, u_int line, char **cause) +{ + struct cmd_list *cmdlist = NULL; + int argc; + char **argv; + + *cause = NULL; + if (cmd_string_split(s, &argc, &argv) != 0) + goto error; + if (argc != 0) { + cmdlist = cmd_list_parse(argc, argv, file, line, cause); + if (cmdlist == NULL) { + cmd_free_argv(argc, argv); + goto error; + } + } + cmd_free_argv(argc, argv); return (cmdlist); + +error: + xasprintf(cause, "invalid or unknown command: %s", s); + return (NULL); } static void diff --git a/key-bindings.c b/key-bindings.c index 65cc9b10..4cbb34ec 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -383,7 +383,7 @@ key_bindings_init(void) for (i = 0; i < nitems(defaults); i++) { cmdlist = cmd_string_parse(defaults[i], "", i, &cause); if (cmdlist == NULL) - fatalx("bad default key"); + fatalx("bad default key: %s", defaults[i]); cmdq_append(NULL, cmdq_get_command(cmdlist, NULL, NULL, 0)); cmd_list_free(cmdlist); } diff --git a/pty.c b/pty.c index 05a02ef0..39794afc 100644 --- a/pty.c +++ b/pty.c @@ -45,7 +45,7 @@ pty_fork(int ptmfd, int *fd, char *name, size_t namelen, struct winsize *ws) struct ptmget ptm; pid_t pid; - if ((ioctl(ptmfd, PTMGET, &ptm) == -1)) + if (ioctl(ptmfd, PTMGET, &ptm) == -1) return (-1); strlcpy(name, ptm.sn, namelen); diff --git a/tmux.1 b/tmux.1 index b641909f..61396fbd 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2410,6 +2410,30 @@ Available server options are: Set the number of buffers; as new buffers are added to the top of the stack, old ones are removed from the bottom if necessary to maintain this maximum length. +.It Xo Ic command-alias[] +.Ar name=value +.Xc +This is an array of custom aliases for commands. +If an unknown command matches +.Ar name , +it is replaced with +.Ar value . +For example, after: +.Pp +.Dl set -s command-alias[2] zoom='resize-pane -Z' +.Pp +Using: +.Pp +.Dl zoom -t:.1 +.Pp +Is equivalent to: +.Pp +.Dl resize-pane -Z -t:.1 +.Pp +Note that aliases are expanded when a command is parsed rather than when it is +executed, so binding an alias with +.Ic bind-key +will bind the expanded form. .It Ic default-terminal Ar terminal Set the default terminal for new windows created in this session - the default value of the diff --git a/tmux.h b/tmux.h index 58fe992b..20ec044a 100644 --- a/tmux.h +++ b/tmux.h @@ -1828,6 +1828,7 @@ void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...); void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...); /* cmd-string.c */ +int cmd_string_split(const char *, int *, char ***); struct cmd_list *cmd_string_parse(const char *, const char *, u_int, char **); /* cmd-wait-for.c */ -- cgit From 126d364abe2e8c063efa5a888de6997ca05641fc Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Jan 2017 19:59:19 +0000 Subject: server-info can become an alias rather than a command. --- cmd-show-messages.c | 15 ++---------- cmd.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++---- options-table.c | 10 ++++++++ 3 files changed, 77 insertions(+), 18 deletions(-) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 57c83881..bc9fc0db 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -45,17 +45,6 @@ const struct cmd_entry cmd_show_messages_entry = { .exec = cmd_show_messages_exec }; -const struct cmd_entry cmd_server_info_entry = { - .name = "server-info", - .alias = "info", - - .args = { "", 0, 0 }, - .usage = "", - - .flags = CMD_AFTERHOOK, - .exec = cmd_show_messages_exec -}; - static int cmd_show_messages_terminals(struct cmdq_item *, int); static int cmd_show_messages_jobs(struct cmdq_item *, int); @@ -109,11 +98,11 @@ cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) int done, blank; done = blank = 0; - if (args_has(args, 'T') || self->entry == &cmd_server_info_entry) { + if (args_has(args, 'T')) { blank = cmd_show_messages_terminals(item, blank); done = 1; } - if (args_has(args, 'J') || self->entry == &cmd_server_info_entry) { + if (args_has(args, 'J')) { cmd_show_messages_jobs(item, blank); done = 1; } diff --git a/cmd.c b/cmd.c index 9119f7ac..751be910 100644 --- a/cmd.c +++ b/cmd.c @@ -93,7 +93,6 @@ extern const struct cmd_entry cmd_select_pane_entry; extern const struct cmd_entry cmd_select_window_entry; extern const struct cmd_entry cmd_send_keys_entry; extern const struct cmd_entry cmd_send_prefix_entry; -extern const struct cmd_entry cmd_server_info_entry; extern const struct cmd_entry cmd_set_buffer_entry; extern const struct cmd_entry cmd_set_environment_entry; extern const struct cmd_entry cmd_set_hook_entry; @@ -182,7 +181,6 @@ const struct cmd_entry *cmd_table[] = { &cmd_select_window_entry, &cmd_send_keys_entry, &cmd_send_prefix_entry, - &cmd_server_info_entry, &cmd_set_buffer_entry, &cmd_set_environment_entry, &cmd_set_hook_entry, @@ -308,21 +306,74 @@ cmd_stringify_argv(int argc, char **argv) return (buf); } +static int +cmd_try_alias(int *argc, char ***argv) +{ + struct options_entry *o; + int old_argc = *argc, new_argc; + char **old_argv = *argv, **new_argv; + u_int size, idx; + int i; + size_t wanted; + const char *s, *cp = NULL; + + o = options_get_only(global_options, "command-alias"); + if (o == NULL || options_array_size(o, &size) == -1 || size == 0) + return (-1); + + wanted = strlen(old_argv[0]); + for (idx = 0; idx < size; idx++) { + s = options_array_get(o, idx); + if (s == NULL) + continue; + + cp = strchr(s, '='); + if (cp == NULL || (size_t)(cp - s) != wanted) + continue; + if (strncmp(old_argv[0], s, wanted) == 0) + break; + } + if (idx == size) + return (-1); + + if (cmd_string_split(cp + 1, &new_argc, &new_argv) != 0) + return (-1); + + *argc = new_argc + old_argc - 1; + *argv = xcalloc((*argc) + 1, sizeof **argv); + + for (i = 0; i < new_argc; i++) + (*argv)[i] = xstrdup(new_argv[i]); + for (i = 1; i < old_argc; i++) + (*argv)[new_argc + i - 1] = xstrdup(old_argv[i]); + + log_debug("alias: %s=%s", old_argv[0], cp + 1); + for (i = 0; i < *argc; i++) + log_debug("alias: argv[%d] = %s", i, (*argv)[i]); + + cmd_free_argv(new_argc, new_argv); + return (0); +} + struct cmd * cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) { + const char *name; const struct cmd_entry **entryp, *entry; struct cmd *cmd; struct args *args; char s[BUFSIZ]; - int ambiguous = 0; + int ambiguous, allocated = 0; *cause = NULL; if (argc == 0) { xasprintf(cause, "no command"); return (NULL); } + name = argv[0]; +retry: + ambiguous = 0; entry = NULL; for (entryp = cmd_table; *entryp != NULL; entryp++) { if ((*entryp)->alias != NULL && @@ -342,10 +393,17 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) if (strcmp(entry->name, argv[0]) == 0) break; } + if ((ambiguous || entry == NULL) && + server_proc != NULL && + !allocated && + cmd_try_alias(&argc, &argv) == 0) { + allocated = 1; + goto retry; + } if (ambiguous) goto ambiguous; if (entry == NULL) { - xasprintf(cause, "unknown command: %s", argv[0]); + xasprintf(cause, "unknown command: %s", name); return (NULL); } @@ -365,6 +423,8 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) cmd->file = xstrdup(file); cmd->line = line; + if (allocated) + cmd_free_argv(argc, argv); return (cmd); ambiguous: @@ -378,7 +438,7 @@ ambiguous: break; } s[strlen(s) - 2] = '\0'; - xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s); + xasprintf(cause, "ambiguous command: %s, could be: %s", name, s); return (NULL); usage: diff --git a/options-table.c b/options-table.c index b9559070..48e2f2c8 100644 --- a/options-table.c +++ b/options-table.c @@ -65,6 +65,16 @@ const struct options_table_entry options_table[] = { .default_num = 20 }, + { .name = "command-alias", + .type = OPTIONS_TABLE_ARRAY, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "split-pane=split-window," + "splitp=split-window," + "server-info=show-messages -JT," + "info=show-messages -JT", + .separator = "," + }, + { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, -- cgit From 16e43d6a42ea15893e77abb4efece5a965321522 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Jan 2017 20:01:34 +0000 Subject: Remove some lies about terminal-overrides from tmux.1. --- tmux.1 | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tmux.1 b/tmux.1 index 61396fbd..347642b3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2525,12 +2525,6 @@ terminal type, the option could be set to the string: The terminal entry value is passed through .Xr strunvis 3 before interpretation. -The default value forcibly corrects the -.Ql colors -entry for terminals which support 256 colours: -.Bd -literal -offset indent -"*256col*:colors=256,xterm*:XT" -.Ed .El .Pp Available session options are: -- cgit From b77dd75b5717d3b95a97c7b60c9e054338d7d85e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Jan 2017 20:05:15 +0000 Subject: Convert terminal-overrides to an array option. --- options-table.c | 5 +- tmux.1 | 26 ++++------- tty-term.c | 139 +++++++++++++++++++++++++++++--------------------------- 3 files changed, 84 insertions(+), 86 deletions(-) diff --git a/options-table.c b/options-table.c index 48e2f2c8..2b506b8c 100644 --- a/options-table.c +++ b/options-table.c @@ -122,11 +122,12 @@ const struct options_table_entry options_table[] = { }, { .name = "terminal-overrides", - .type = OPTIONS_TABLE_STRING, + .type = OPTIONS_TABLE_ARRAY, .scope = OPTIONS_TABLE_SERVER, .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" + ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT", + .separator = "," }, { .name = "assume-paste-time", diff --git a/tmux.1 b/tmux.1 index 347642b3..f30b45f1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2495,12 +2495,12 @@ disallowedWindowOps: 20,21,SetXprop Or changing this property from the .Xr xterm 1 interactive menu when required. -.It Ic terminal-overrides Ar string -Contains a list of entries which override terminal descriptions read using -.Xr terminfo 5 . -.Ar string -is a comma-separated list of items each a colon-separated string made up of a -terminal type pattern (matched using +.It Ic terminal-overrides[] Ar string +Allow terminal descriptions read using +.Xr terminfo 5 +to be overriden. +Each entry is a colon-separated string made up of a terminal type pattern +(matched using .Xr fnmatch 3 ) and a set of .Em name=value @@ -2511,16 +2511,10 @@ For example, to set the .Xr terminfo 5 entry to .Ql \ee[H\ee[2J -for all terminal types and the -.Ql dch1 -entry to -.Ql \ee[P -for the -.Ql rxvt -terminal type, the option could be set to the string: -.Bd -literal -offset indent -"*:clear=\ee[H\ee[2J,rxvt:dch1=\ee[P" -.Ed +for all terminal types matching +.Ql rxvt* : +.Pp +.Dl "rxvt*:clear=\ee[H\ee[2J" .Pp The terminal entry value is passed through .Xr strunvis 3 diff --git a/tty-term.c b/tty-term.c index 0496f1f3..02305971 100644 --- a/tty-term.c +++ b/tty-term.c @@ -294,86 +294,82 @@ tty_term_strip(const char *s) } static void -tty_term_override(struct tty_term *term, const char *overrides) +tty_term_override(struct tty_term *term, const char *override) { const struct tty_term_code_entry *ent; struct tty_code *code; - char *termnext, *termstr; - char *entnext, *entstr; - char *s, *ptr, *val; + char *next, *s, *copy, *cp, *value; const char *errstr; u_int i; - int n, removeflag; + int n, remove; - s = xstrdup(overrides); + copy = next = xstrdup(override); - termnext = s; - while ((termstr = strsep(&termnext, ",")) != NULL) { - entnext = termstr; + s = strsep(&next, ":"); + if (s == NULL || next == NULL || fnmatch(s, term->name, 0) != 0) { + free(copy); + return; + } - entstr = strsep(&entnext, ":"); - if (entstr == NULL || entnext == NULL) - continue; - if (fnmatch(entstr, term->name, 0) != 0) + while ((s = strsep(&next, ":")) != NULL) { + if (*s == '\0') continue; - while ((entstr = strsep(&entnext, ":")) != NULL) { - if (*entstr == '\0') + value = NULL; + + remove = 0; + if ((cp = strchr(s, '=')) != NULL) { + *cp++ = '\0'; + value = xstrdup(cp); + if (strunvis(value, cp) == -1) { + free(value); + value = xstrdup(cp); + } + } else if (s[strlen(s) - 1] == '@') { + s[strlen(s) - 1] = '\0'; + remove = 1; + } else + value = xstrdup(""); + + if (remove) + log_debug("%s override: %s@", term->name, s); + else + log_debug("%s override: %s=%s", term->name, s, value); + + for (i = 0; i < tty_term_ncodes(); i++) { + ent = &tty_term_codes[i]; + if (strcmp(s, ent->name) != 0) continue; + code = &term->codes[i]; - val = NULL; - removeflag = 0; - if ((ptr = strchr(entstr, '=')) != NULL) { - *ptr++ = '\0'; - val = xstrdup(ptr); - if (strunvis(val, ptr) == -1) { - free(val); - val = xstrdup(ptr); - } - } else if (entstr[strlen(entstr) - 1] == '@') { - entstr[strlen(entstr) - 1] = '\0'; - removeflag = 1; - } else - val = xstrdup(""); - - log_debug("%s override: %s %s", - term->name, entstr, removeflag ? "@" : val); - for (i = 0; i < tty_term_ncodes(); i++) { - ent = &tty_term_codes[i]; - if (strcmp(entstr, ent->name) != 0) - continue; - code = &term->codes[i]; - - if (removeflag) { - code->type = TTYCODE_NONE; - continue; - } - switch (ent->type) { - case TTYCODE_NONE: - break; - case TTYCODE_STRING: - if (code->type == TTYCODE_STRING) - free(code->value.string); - code->value.string = xstrdup(val); - code->type = ent->type; - break; - case TTYCODE_NUMBER: - n = strtonum(val, 0, INT_MAX, &errstr); - if (errstr != NULL) - break; - code->value.number = n; - code->type = ent->type; - break; - case TTYCODE_FLAG: - code->value.flag = 1; - code->type = ent->type; + if (remove) { + code->type = TTYCODE_NONE; + continue; + } + switch (ent->type) { + case TTYCODE_NONE: + break; + case TTYCODE_STRING: + if (code->type == TTYCODE_STRING) + free(code->value.string); + code->value.string = xstrdup(value); + code->type = ent->type; + break; + case TTYCODE_NUMBER: + n = strtonum(value, 0, INT_MAX, &errstr); + if (errstr != NULL) break; - } + code->value.number = n; + code->type = ent->type; + break; + case TTYCODE_FLAG: + code->value.flag = 1; + code->type = ent->type; + break; } - - free(val); } - } + free(value); + } free(s); } @@ -383,7 +379,8 @@ tty_term_find(char *name, int fd, char **cause) struct tty_term *term; const struct tty_term_code_entry *ent; struct tty_code *code; - u_int i; + struct options_entry *o; + u_int size, i; int n, error; const char *s, *acs; @@ -457,8 +454,14 @@ tty_term_find(char *name, int fd, char **cause) } /* Apply terminal overrides. */ - s = options_get_string(global_options, "terminal-overrides"); - tty_term_override(term, s); + o = options_get_only(global_options, "terminal-overrides"); + if (options_array_size(o, &size) != -1) { + for (i = 0; i < size; i++) { + s = options_array_get(o, i); + if (s != NULL) + tty_term_override(term, s); + } + } /* Delete curses data. */ del_curterm(cur_term); -- cgit From 4b2821ff9861b193226442e9bc5507a9ddffa827 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Jan 2017 20:15:32 +0000 Subject: Make update-environment an array as well. --- cmd-attach-session.c | 18 ++++-------------- cmd-new-session.c | 9 +++------ cmd-switch-client.c | 8 +++----- environ.c | 28 +++++++++++++++------------- options-table.c | 3 +-- tmux.1 | 10 +++------- tmux.h | 2 +- 7 files changed, 30 insertions(+), 48 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 5807eefc..f61b5930 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -54,7 +54,6 @@ cmd_attach_session(struct cmdq_item *item, int dflag, int rflag, struct client *c = item->client, *c_loop; struct winlink *wl = item->state.tflag.wl; struct window_pane *wp = item->state.tflag.wp; - const char *update; char *cause, *cwd; struct format_tree *ft; @@ -95,12 +94,8 @@ cmd_attach_session(struct cmdq_item *item, int dflag, int rflag, server_client_detach(c_loop, MSG_DETACH); } } - - if (!Eflag) { - update = options_get_string(s->options, - "update-environment"); - environ_update(update, c->environ, s->environ); - } + if (!Eflag) + environ_update(s->options, c->environ, s->environ); c->session = s; server_client_set_key_table(c, NULL); @@ -116,7 +111,6 @@ cmd_attach_session(struct cmdq_item *item, int dflag, int rflag, free(cause); return (CMD_RETURN_ERROR); } - if (rflag) c->flags |= CLIENT_READONLY; @@ -127,12 +121,8 @@ cmd_attach_session(struct cmdq_item *item, int dflag, int rflag, server_client_detach(c_loop, MSG_DETACH); } } - - if (!Eflag) { - update = options_get_string(s->options, - "update-environment"); - environ_update(update, c->environ, s->environ); - } + if (!Eflag) + environ_update(s->options, c->environ, s->environ); c->session = s; server_client_set_key_table(c, NULL); diff --git a/cmd-new-session.c b/cmd-new-session.c index 7df7dcb6..10304bc0 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -73,7 +73,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) struct window *w; struct environ *env; struct termios tio, *tiop; - const char *newname, *target, *update, *errstr, *template; + const char *newname, *target, *errstr, *template; const char *path, *cmd, *cwd, *to_free = NULL; char **argv, *cause, *cp; int detached, already_attached, idx, argc; @@ -234,11 +234,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) /* Construct the environment. */ env = environ_create(); - if (c != NULL && !args_has(args, 'E')) { - update = options_get_string(global_s_options, - "update-environment"); - environ_update(update, c->environ, env); - } + if (c != NULL && !args_has(args, 'E')) + environ_update(global_s_options, c->environ, env); /* Create the new session. */ idx = -1 - options_get_number(global_s_options, "base-index"); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 81f70d9c..cd46db39 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -53,7 +53,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) struct client *c = state->c; struct session *s = item->state.tflag.s; struct window_pane *wp; - const char *tablename, *update; + const char *tablename; struct key_table *table; if (args_has(args, 'r')) @@ -102,10 +102,8 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) } } - if (!args_has(args, 'E')) { - update = options_get_string(s->options, "update-environment"); - environ_update(update, c->environ, s->environ); - } + if (!args_has(args, 'E')) + environ_update(s->options, c->environ, s->environ); if (c->session != NULL && c->session != s) c->last_session = c->session; diff --git a/environ.c b/environ.c index 016d256a..4bb794dd 100644 --- a/environ.c +++ b/environ.c @@ -169,25 +169,27 @@ environ_unset(struct environ *env, const char *name) free(envent); } -/* - * Copy a space-separated list of variables from a destination into a source - * environment. - */ +/* Copy variables from a destination into a source * environment. */ void -environ_update(const char *vars, struct environ *srcenv, - struct environ *dstenv) +environ_update(struct options *oo, struct environ *src, struct environ *dst) { struct environ_entry *envent; - char *copyvars, *var, *next; + struct options_entry *o; + u_int size, idx; + const char *value; - copyvars = next = xstrdup(vars); - while ((var = strsep(&next, " ")) != NULL) { - if ((envent = environ_find(srcenv, var)) == NULL) - environ_clear(dstenv, var); + o = options_get(oo, "update-environment"); + if (o == NULL || options_array_size(o, &size) == -1) + return; + for (idx = 0; idx < size; idx++) { + value = options_array_get(o, idx); + if (value == NULL) + continue; + if ((envent = environ_find(src, value)) == NULL) + environ_clear(dst, value); else - environ_set(dstenv, envent->name, "%s", envent->value); + environ_set(dst, envent->name, "%s", envent->value); } - free(copyvars); } /* Push environment into the real environment - use after fork(). */ diff --git a/options-table.c b/options-table.c index 2b506b8c..8af4d934 100644 --- a/options-table.c +++ b/options-table.c @@ -482,11 +482,10 @@ const struct options_table_entry options_table[] = { }, { .name = "update-environment", - .type = OPTIONS_TABLE_STRING, + .type = OPTIONS_TABLE_ARRAY, .scope = OPTIONS_TABLE_SESSION, .default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID " "SSH_CONNECTION WINDOWID XAUTHORITY" - }, { .name = "visual-activity", diff --git a/tmux.1 b/tmux.1 index f30b45f1..6f680fdf 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2869,19 +2869,15 @@ For how to specify see the .Ic message-command-style option. -.It Ic update-environment Ar variables -Set a space-separated string containing a list of environment variables to be -copied into the session environment when a new session is created or an -existing session is attached. +.It Ic update-environment[] Ar variable +Set list of environment variables to be copied into the session environment +when a new session is created or an existing session is attached. Any variables that do not exist in the source environment are set to be removed from the session environment (as if .Fl r was given to the .Ic set-environment command). -The default is -"DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID -XAUTHORITY". .It Xo Ic visual-activity .Op Ic on | off .Xc diff --git a/tmux.h b/tmux.h index 20ec044a..4a82871f 100644 --- a/tmux.h +++ b/tmux.h @@ -1669,7 +1669,7 @@ void printflike(3, 4) environ_set(struct environ *, const char *, const char *, void environ_clear(struct environ *, const char *); void environ_put(struct environ *, const char *); void environ_unset(struct environ *, const char *); -void environ_update(const char *, struct environ *, struct environ *); +void environ_update(struct options *, struct environ *, struct environ *); void environ_push(struct environ *); void environ_log(struct environ *, const char *); -- cgit From ffc28a7765f4b560427404c9679966232655e67e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Jan 2017 20:24:54 +0000 Subject: Fix set -u on array options. --- cmd-set-option.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 84c12e56..f7e6b730 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -68,6 +68,7 @@ static enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; + int append = args_has(args, 'a'); struct cmd_find_state *fs = &item->state.tflag; struct session *s = fs->s; struct winlink *wl = fs->wl; @@ -160,17 +161,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "not an array: %s", args->argv[0]); return (CMD_RETURN_ERROR); } - } else if (*name != '@' && options_array_size(parent, NULL) != -1) { - if (value == NULL) { - cmdq_error(item, "empty value"); - return (-1); - } - if (o == NULL) - o = options_empty(oo, options_table_entry(parent)); - if (!args_has(args, 'a')) - options_array_clear(o); - options_array_assign(o, value); - return (CMD_RETURN_NORMAL); } /* With -o, check this option is not already set. */ @@ -204,16 +194,26 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) options_remove(o); } else options_array_set(o, idx, NULL, 0); - } else if (*name == '@') - options_set_string(oo, name, args_has(args, 'a'), "%s", value); - else if (idx == -1) { + } else if (*name == '@') { + if (value == NULL) { + cmdq_error(item, "empty value"); + return (CMD_RETURN_ERROR); + } + options_set_string(oo, name, append, "%s", value); + } else if (idx == -1 && options_array_size(parent, NULL) == -1) { error = cmd_set_option_set(self, item, oo, parent, value); if (error != 0) return (CMD_RETURN_ERROR); } else { + if (value == NULL) { + cmdq_error(item, "empty value"); + return (CMD_RETURN_ERROR); + } if (o == NULL) o = options_empty(oo, options_table_entry(parent)); - if (options_array_set(o, idx, value, 1) != 0) { + if (idx == -1) + options_array_assign(o, value); + else if (options_array_set(o, idx, value, append) != 0) { cmdq_error(item, "invalid index: %s", args->argv[0]); return (CMD_RETURN_ERROR); } -- cgit