From 3ed37a207988bc6e96dc673ae4564a4efd682ea6 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Aug 2021 08:17:41 +0000 Subject: [PATCH 1/5] Limit width and height to tty correctly, GitHub issue 2843. --- cmd-display-menu.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 347a7090..378c62e1 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -218,7 +218,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, else if (n < 0) n = 0; *px = n; - log_debug("%s: -x: %s = %s = %u", __func__, xp, p, *px); + log_debug("%s: -x: %s = %s = %u (-w %u)", __func__, xp, p, *px, w); free(p); /* Expand vertical position */ @@ -244,7 +244,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, else if (n < 0) n = 0; *py = n; - log_debug("%s: -y: %s = %s = %u", __func__, yp, p, *py); + log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h); free(p); return (1); @@ -359,10 +359,10 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) } } - if (w > tty->sx - 1) - w = tty->sx - 1; - if (h > tty->sy - 1) - h = tty->sy - 1; + if (w > tty->sx) + w = tty->sx; + if (h > tty->sy) + h = tty->sy; if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h)) return (CMD_RETURN_NORMAL); From 4a753dbefc2e67c218cf41141eaa6afab00f774a Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Aug 2021 11:04:21 +0000 Subject: [PATCH 2/5] Fix a few memory leaks. --- cmd-parse.y | 9 ++++----- cmd-source-file.c | 5 +++++ key-bindings.c | 5 ++++- spawn.c | 1 + tmux.c | 1 + 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index c27d530e..a08c5819 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -426,7 +426,7 @@ command : assignment arg = xcalloc(1, sizeof *arg); arg->type = CMD_PARSE_STRING; - arg->string = xstrdup($2); + arg->string = $2; TAILQ_INSERT_HEAD(&$$->arguments, arg, entry); } | optional_assignment TOKEN arguments @@ -443,7 +443,7 @@ command : assignment arg = xcalloc(1, sizeof *arg); arg->type = CMD_PARSE_STRING; - arg->string = xstrdup($2); + arg->string = $2; TAILQ_INSERT_HEAD(&$$->arguments, arg, entry); } @@ -543,13 +543,13 @@ argument : TOKEN { $$ = xcalloc(1, sizeof *$$); $$->type = CMD_PARSE_STRING; - $$->string = xstrdup($1); + $$->string = $1; } | EQUALS { $$ = xcalloc(1, sizeof *$$); $$->type = CMD_PARSE_STRING; - $$->string = xstrdup($1); + $$->string = $1; } | '{' argument_statements { @@ -817,7 +817,6 @@ cmd_parse_build_command(struct cmd_parse_command *cmd, goto out; values[count].type = ARGS_COMMANDS; values[count].cmdlist = pr->cmdlist; - values[count].cmdlist->references++; break; } count++; diff --git a/cmd-source-file.c b/cmd-source-file.c index fbe871ca..0bc02e05 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -66,6 +66,7 @@ static void cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata) { struct cmdq_item *new_item; + u_int i; if (cfg_finished) { if (cdata->retval == CMD_RETURN_ERROR && @@ -76,6 +77,8 @@ cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata) cmdq_insert_after(cdata->after, new_item); } + for (i = 0; i < cdata->nfiles; i++) + free(cdata->files[i]); free(cdata->files); free(cdata); } @@ -177,6 +180,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "%s: %s", path, error); retval = CMD_RETURN_ERROR; } + globfree(&g); free(pattern); continue; } @@ -184,6 +188,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) for (j = 0; j < g.gl_pathc; j++) cmd_source_file_add(cdata, g.gl_pathv[j]); + globfree(&g); } free(expanded); diff --git a/key-bindings.c b/key-bindings.c index c0a959e2..e3b21d61 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -187,6 +187,7 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat, { struct key_table *table; struct key_binding *bd; + char *s; table = key_bindings_get_table(name, 1); @@ -216,8 +217,10 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat, bd->flags |= KEY_BINDING_REPEAT; bd->cmdlist = cmdlist; + s = cmd_list_print(bd->cmdlist, 0); log_debug("%s: %#llx %s = %s", __func__, bd->key, - key_string_lookup_key(bd->key, 1), cmd_list_print(bd->cmdlist, 0)); + key_string_lookup_key(bd->key, 1), s); + free(s); } void diff --git a/spawn.c b/spawn.c index 98b70312..294eb50f 100644 --- a/spawn.c +++ b/spawn.c @@ -179,6 +179,7 @@ spawn_window(struct spawn_context *sc, char **cause) /* Set the name of the new window. */ if (~sc->flags & SPAWN_RESPAWN) { + free(w->name); if (sc->name != NULL) { w->name = format_single(item, sc->name, c, s, NULL, NULL); diff --git a/tmux.c b/tmux.c index 55ab9751..ceaa99da 100644 --- a/tmux.c +++ b/tmux.c @@ -211,6 +211,7 @@ make_label(const char *label, char **cause) free(paths); xasprintf(&base, "%s/tmux-%ld", path, (long)uid); + free(path); if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) { xasprintf(cause, "couldn't create directory %s (%s)", base, strerror(errno)); From 1f0c0914c75980069c6f78872bdee5ceb0ef64e2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Aug 2021 11:08:26 +0000 Subject: [PATCH 3/5] Revert one of previous, for some reason it is being freed. --- cmd-parse.y | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd-parse.y b/cmd-parse.y index a08c5819..f79c7fd3 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -817,6 +817,7 @@ cmd_parse_build_command(struct cmd_parse_command *cmd, goto out; values[count].type = ARGS_COMMANDS; values[count].cmdlist = pr->cmdlist; + values[count].cmdlist->references++; break; } count++; From 03b83a5a34a4257be9029e1f5195dcddcd531caa Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Aug 2021 11:48:21 +0000 Subject: [PATCH 4/5] Key bindings steal a reference to the command instead of adding their own, it was correct not to add a reference when parsing, but the bind-key then needs to add one. --- cmd-bind-key.c | 1 + cmd-parse.y | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 4a6c8541..bb905bce 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -75,6 +75,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) value = args_value(args, 1); if (count == 2 && value->type == ARGS_COMMANDS) { key_bindings_add(tablename, key, note, repeat, value->cmdlist); + value->cmdlist->references++; return (CMD_RETURN_NORMAL); } diff --git a/cmd-parse.y b/cmd-parse.y index f79c7fd3..a08c5819 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -817,7 +817,6 @@ cmd_parse_build_command(struct cmd_parse_command *cmd, goto out; values[count].type = ARGS_COMMANDS; values[count].cmdlist = pr->cmdlist; - values[count].cmdlist->references++; break; } count++; From 210e71edf36198e3f22525e7f548a71a5681d25c Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Aug 2021 12:33:55 +0000 Subject: [PATCH 5/5] Move command argument parsing common functions and don't bother to parse again if given a command rather than a string. --- arguments.c | 137 +++++++++++++++++++++++++++++++++ cmd-command-prompt.c | 178 +++++++++++++++++++++---------------------- cmd-confirm-before.c | 49 +++++------- cmd-display-panes.c | 38 ++++----- cmd-if-shell.c | 112 +++++++++------------------ cmd-run-shell.c | 53 +++++-------- tmux.h | 9 +++ 7 files changed, 330 insertions(+), 246 deletions(-) diff --git a/arguments.c b/arguments.c index 49bc8b2a..1f786529 100644 --- a/arguments.c +++ b/arguments.c @@ -29,8 +29,10 @@ * Manipulate command arguments. */ +/* List of argument values. */ TAILQ_HEAD(args_values, args_value); +/* Single arguments flag. */ struct args_entry { u_char flag; struct args_values values; @@ -38,12 +40,20 @@ struct args_entry { RB_ENTRY(args_entry) entry; }; +/* Parsed argument flags and values. */ struct args { struct args_tree tree; u_int count; struct args_value *values; }; +/* Prepared command state. */ +struct args_command_state { + struct cmd_list *cmdlist; + char *cmd; + struct cmd_parse_input pi; +}; + static struct args_entry *args_find(struct args *, u_char); static int args_cmp(struct args_entry *, struct args_entry *); @@ -477,6 +487,133 @@ args_string(struct args *args, u_int idx) return (args_value_as_string(&args->values[idx])); } +/* Make a command now. */ +struct cmd_list * +args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx) +{ + struct args_command_state *state; + char *error; + struct cmd_list *cmdlist; + + state = args_make_commands_prepare(self, item, idx, NULL, 0, 0); + cmdlist = args_make_commands(state, 0, NULL, &error); + args_make_commands_free(state); + if (cmdlist == NULL) { + cmdq_error(item, "%s", error); + free(error); + } + return (cmdlist); +} + +/* Save bits to make a command later. */ +struct args_command_state * +args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx, + const char *default_command, int wait, int expand) +{ + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct client *tc = cmdq_get_target_client(item); + struct args_value *value; + struct args_command_state *state; + const char *cmd; + + state = xcalloc(1, sizeof *state); + + if (idx < args->count) { + value = &args->values[idx]; + if (value->type == ARGS_COMMANDS) { + state->cmdlist = value->cmdlist; + state->cmdlist->references++; + return (state); + } + cmd = value->string; + } else { + if (default_command == NULL) + fatalx("argument out of range"); + cmd = default_command; + } + + + if (expand) + state->cmd = format_single_from_target(item, cmd); + else + state->cmd = xstrdup(cmd); + log_debug("%s: %s", __func__, state->cmd); + + if (wait) + state->pi.item = item; + cmd_get_source(self, &state->pi.file, &state->pi.line); + state->pi.c = tc; + if (state->pi.c != NULL) + state->pi.c->references++; + cmd_find_copy_state(&state->pi.fs, target); + + return (state); +} + +/* Return argument as command. */ +struct cmd_list * +args_make_commands(struct args_command_state *state, int argc, char **argv, + char **error) +{ + struct cmd_parse_result *pr; + char *cmd, *new_cmd; + int i; + + if (state->cmdlist != NULL) + return (state->cmdlist); + + cmd = xstrdup(state->cmd); + for (i = 0; i < argc; i++) { + new_cmd = cmd_template_replace(cmd, argv[i], i + 1); + log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd); + free(cmd); + cmd = new_cmd; + } + log_debug("%s: %s", __func__, cmd); + + pr = cmd_parse_from_string(cmd, &state->pi); + free(cmd); + switch (pr->status) { + case CMD_PARSE_ERROR: + *error = pr->error; + return (NULL); + case CMD_PARSE_SUCCESS: + return (pr->cmdlist); + } +} + +/* Free commands state. */ +void +args_make_commands_free(struct args_command_state *state) +{ + if (state->cmdlist != NULL) + cmd_list_free(state->cmdlist); + if (state->pi.c != NULL) + server_client_unref(state->pi.c); + free(state->cmd); + free(state); +} + +/* Get prepared command. */ +char * +args_make_commands_get_command(struct args_command_state *state) +{ + struct cmd *first; + int n; + char *s; + + if (state->cmdlist != NULL) { + first = cmd_list_first(state->cmdlist); + if (first == NULL) + return (xstrdup("")); + return (xstrdup(cmd_get_entry(first)->name)); + } + n = strcspn(state->cmd, " ,"); + xasprintf(&s, "%.*s", n, state->cmd); + return (s); +} + /* Get first value in argument. */ struct args_value * args_first_value(struct args *args, u_char flag) diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index f80760ce..a877897e 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -48,21 +48,24 @@ const struct cmd_entry cmd_command_prompt_entry = { .exec = cmd_command_prompt_exec }; +struct cmd_command_prompt_prompt { + char *input; + char *prompt; +}; + struct cmd_command_prompt_cdata { - struct cmdq_item *item; - struct cmd_parse_input pi; + struct cmdq_item *item; + struct args_command_state *state; - int flags; - enum prompt_type prompt_type; + int flags; + enum prompt_type prompt_type; - char *inputs; - char *next_input; + struct cmd_command_prompt_prompt *prompts; + u_int count; + u_int current; - char *prompts; - char *next_prompt; - - char *template; - int idx; + int argc; + char **argv; }; static enum cmd_retval @@ -71,12 +74,12 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct client *tc = cmdq_get_target_client(item); struct cmd_find_state *target = cmdq_get_target(item); - const char *inputs, *prompts, *type, *s; + const char *type, *s, *input; struct cmd_command_prompt_cdata *cdata; - char *prompt, *comma, *input = NULL; + char *tmp, *prompts, *prompt, *next_prompt; + char *inputs, *next_input; u_int count = args_count(args); - size_t n; - int wait = !args_has(args, 'b'); + int wait = !args_has(args, 'b'), space = 1; if (tc->prompt_string != NULL) return (CMD_RETURN_NORMAL); @@ -84,50 +87,49 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) wait = 0; cdata = xcalloc(1, sizeof *cdata); - cdata->idx = 1; - - cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); - if (wait) - cdata->pi.item = item; - cdata->pi.c = tc; - cmd_find_copy_state(&cdata->pi.fs, target); - if (wait) cdata->item = item; + cdata->state = args_make_commands_prepare(self, item, 0, "%1", wait, + args_has(args, 'F')); - if (count != 0) { - s = args_string(args, 0); - if (args_has(args, 'F')) - cdata->template = format_single_from_target(item, s); - else - cdata->template = xstrdup(s); - } else - cdata->template = xstrdup("%1"); - - if ((prompts = args_get(args, 'p')) != NULL) - cdata->prompts = xstrdup(prompts); - else if (count != 0) { - n = strcspn(cdata->template, " ,"); - xasprintf(&cdata->prompts, "(%.*s) ", (int)n, cdata->template); + if ((s = args_get(args, 'p')) == NULL) { + if (count != 0) { + tmp = args_make_commands_get_command(cdata->state); + xasprintf(&prompts, "(%s)", tmp); + free(tmp); + } else { + prompts = xstrdup(":"); + space = 0; + } + next_prompt = prompts; } else - cdata->prompts = xstrdup(":"); - - /* Get first prompt. */ - cdata->next_prompt = cdata->prompts; - comma = strsep(&cdata->next_prompt, ","); - if (prompts == NULL) - prompt = xstrdup(comma); + next_prompt = prompts = xstrdup (s); + if ((s = args_get(args, 'I')) != NULL) + next_input = inputs = xstrdup(s); else - xasprintf(&prompt, "%s ", comma); + next_input = NULL; + while ((prompt = strsep(&next_prompt, ",")) != NULL) { + cdata->prompts = xreallocarray(cdata->prompts, cdata->count + 1, + sizeof *cdata->prompts); + if (!space) + tmp = xstrdup(prompt); + else + xasprintf(&tmp, "%s ", prompt); + cdata->prompts[cdata->count].prompt = tmp; - /* Get initial prompt input. */ - if ((inputs = args_get(args, 'I')) != NULL) { - cdata->inputs = xstrdup(inputs); - cdata->next_input = cdata->inputs; - input = strsep(&cdata->next_input, ","); + if (next_input != NULL) { + input = strsep(&next_input, ","); + if (input == NULL) + input = ""; + } else + input = ""; + cdata->prompts[cdata->count].input = xstrdup(input); + + cdata->count++; } + free(inputs); + free(prompts); - /* Get prompt type. */ if ((type = args_get(args, 'T')) != NULL) { cdata->prompt_type = status_prompt_type(type); if (cdata->prompt_type == PROMPT_TYPE_INVALID) { @@ -145,10 +147,9 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags |= PROMPT_INCREMENTAL; else if (args_has(args, 'k')) cdata->flags |= PROMPT_KEY; - status_prompt_set(tc, target, prompt, input, - cmd_command_prompt_callback, cmd_command_prompt_free, cdata, - cdata->flags, cdata->prompt_type); - free(prompt); + status_prompt_set(tc, target, cdata->prompts[0].prompt, + cdata->prompts[0].input, cmd_command_prompt_callback, + cmd_command_prompt_free, cdata, cdata->flags, cdata->prompt_type); if (!wait) return (CMD_RETURN_NORMAL); @@ -159,51 +160,39 @@ static int cmd_command_prompt_callback(struct client *c, void *data, const char *s, int done) { - struct cmd_command_prompt_cdata *cdata = data; - char *new_template, *prompt, *comma, *error; - char *input = NULL; - struct cmdq_item *item = cdata->item; - enum cmd_parse_status status; + struct cmd_command_prompt_cdata *cdata = data; + char *error; + struct cmdq_item *item = cdata->item, *new_item; + struct cmd_list *cmdlist; + struct cmd_command_prompt_prompt *prompt; if (s == NULL) goto out; - if (done && (cdata->flags & PROMPT_INCREMENTAL)) - goto out; - - new_template = cmd_template_replace(cdata->template, s, cdata->idx); if (done) { - free(cdata->template); - cdata->template = new_template; + if (cdata->flags & PROMPT_INCREMENTAL) + goto out; + + cmd_append_argv(&cdata->argc, &cdata->argv, s); + if (++cdata->current != cdata->count) { + prompt = &cdata->prompts[cdata->current]; + status_prompt_update(c, prompt->prompt, prompt->input); + return (1); + } } - /* - * Check if there are more prompts; if so, get its respective input - * and update the prompt data. - */ - if (done && (comma = strsep(&cdata->next_prompt, ",")) != NULL) { - xasprintf(&prompt, "%s ", comma); - input = strsep(&cdata->next_input, ","); - status_prompt_update(c, prompt, input); - - free(prompt); - cdata->idx++; - return (1); - } - - if (item != NULL) { - status = cmd_parse_and_insert(new_template, &cdata->pi, item, - cmdq_get_state(item), &error); - } else { - status = cmd_parse_and_append(new_template, &cdata->pi, c, NULL, - &error); - } - if (status == CMD_PARSE_ERROR) { + cmdlist = args_make_commands(cdata->state, cdata->argc, cdata->argv, + &error); + if (cmdlist == NULL) { cmdq_append(c, cmdq_get_error(error)); free(error); + } else if (item == NULL) { + new_item = cmdq_get_command(cmdlist, NULL); + cmdq_append(c, new_item); + } else { + new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); + cmdq_insert_after(item, new_item); } - if (!done) - free(new_template); if (c->prompt_inputcb != cmd_command_prompt_callback) return (1); @@ -217,9 +206,14 @@ static void cmd_command_prompt_free(void *data) { struct cmd_command_prompt_cdata *cdata = data; + u_int i; - free(cdata->inputs); + for (i = 0; i < cdata->count; i++) { + free(cdata->prompts[i].prompt); + free(cdata->prompts[i].input); + } free(cdata->prompts); - free(cdata->template); + cmd_free_argv(cdata->argc, cdata->argv); + args_make_commands_free(cdata->state); free(cdata); } diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 580c379a..4fe43302 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -47,9 +47,8 @@ const struct cmd_entry cmd_confirm_before_entry = { }; struct cmd_confirm_before_data { - char *cmd; struct cmdq_item *item; - struct cmd_parse_input pi; + struct cmd_list *cmdlist; }; static enum cmd_retval @@ -59,31 +58,25 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) struct cmd_confirm_before_data *cdata; struct client *tc = cmdq_get_target_client(item); struct cmd_find_state *target = cmdq_get_target(item); - char *cmd, *copy, *new_prompt, *tmp; - const char *prompt; + char *new_prompt; + const char *prompt, *cmd; int wait = !args_has(args, 'b'); cdata = xcalloc(1, sizeof *cdata); - cdata->cmd = xstrdup(args_string(args, 0)); + cdata->cmdlist = args_make_commands_now(self, item, 0); + if (cdata->cmdlist == NULL) + return (CMD_RETURN_ERROR); + + if (wait) + cdata->item = item; if ((prompt = args_get(args, 'p')) != NULL) xasprintf(&new_prompt, "%s ", prompt); else { - tmp = copy = xstrdup(cdata->cmd); - cmd = strsep(&tmp, " \t"); + cmd = cmd_get_entry(cmd_list_first(cdata->cmdlist))->name; xasprintf(&new_prompt, "Confirm '%s'? (y/n) ", cmd); - free(copy); } - cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); - if (wait) - cdata->pi.item = item; - cdata->pi.c = tc; - cmd_find_copy_state(&cdata->pi.fs, target); - - if (wait) - cdata->item = item; - status_prompt_set(tc, target, new_prompt, NULL, cmd_confirm_before_callback, cmd_confirm_before_free, cdata, PROMPT_SINGLE, PROMPT_TYPE_COMMAND); @@ -99,10 +92,7 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, __unused int done) { struct cmd_confirm_before_data *cdata = data; - const char *cmd = cdata->cmd; - char *error; - struct cmdq_item *item = cdata->item; - enum cmd_parse_status status; + struct cmdq_item *item = cdata->item, *new_item; int retcode = 1; if (c->flags & CLIENT_DEAD) @@ -114,14 +104,13 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, goto out; retcode = 0; - if (item != NULL) { - status = cmd_parse_and_insert(cmd, &cdata->pi, item, - cmdq_get_state(item), &error); - } else - status = cmd_parse_and_append(cmd, &cdata->pi, c, NULL, &error); - if (status == CMD_PARSE_ERROR) { - cmdq_append(c, cmdq_get_error(error)); - free(error); + if (item == NULL) { + new_item = cmdq_get_command(cdata->cmdlist, NULL); + cmdq_append(c, new_item); + } else { + new_item = cmdq_get_command(cdata->cmdlist, + cmdq_get_state(item)); + cmdq_insert_after(item, new_item); } out: @@ -139,6 +128,6 @@ cmd_confirm_before_free(void *data) { struct cmd_confirm_before_data *cdata = data; - free(cdata->cmd); + cmd_list_free(cdata->cmdlist); free(cdata); } diff --git a/cmd-display-panes.c b/cmd-display-panes.c index bc171638..169ab3d5 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -42,8 +42,8 @@ const struct cmd_entry cmd_display_panes_entry = { }; struct cmd_display_panes_data { - struct cmdq_item *item; - char *command; + struct cmdq_item *item; + struct args_command_state *state; }; static void @@ -207,7 +207,7 @@ cmd_display_panes_free(__unused struct client *c, void *data) if (cdata->item != NULL) cmdq_continue(cdata->item); - free(cdata->command); + args_make_commands_free(cdata->state); free(cdata); } @@ -215,10 +215,11 @@ static int cmd_display_panes_key(struct client *c, void *data, struct key_event *event) { struct cmd_display_panes_data *cdata = data; - char *cmd, *expanded, *error; + char *expanded, *error; + struct cmdq_item *item = cdata->item, *new_item; + struct cmd_list *cmdlist; struct window *w = c->session->curw->window; struct window_pane *wp; - enum cmd_parse_status status; u_int index; key_code key; @@ -239,15 +240,19 @@ cmd_display_panes_key(struct client *c, void *data, struct key_event *event) window_unzoom(w); xasprintf(&expanded, "%%%u", wp->id); - cmd = cmd_template_replace(cdata->command, expanded, 1); - status = cmd_parse_and_append(cmd, NULL, c, NULL, &error); - if (status == CMD_PARSE_ERROR) { + cmdlist = args_make_commands(cdata->state, 1, &expanded, &error); + if (cmdlist == NULL) { cmdq_append(c, cmdq_get_error(error)); free(error); + } else if (item == NULL) { + new_item = cmdq_get_command(cmdlist, NULL); + cmdq_append(c, new_item); + } else { + new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); + cmdq_insert_after(item, new_item); } - free(cmd); free(expanded); return (1); } @@ -261,6 +266,7 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) u_int delay; char *cause; struct cmd_display_panes_data *cdata; + int wait = !args_has(args, 'b'); if (tc->overlay_draw != NULL) return (CMD_RETURN_NORMAL); @@ -275,15 +281,11 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) } else delay = options_get_number(s->options, "display-panes-time"); - cdata = xmalloc(sizeof *cdata); - if (args_count(args)) - cdata->command = xstrdup(args_string(args, 0)); - else - cdata->command = xstrdup("select-pane -t '%%'"); - if (args_has(args, 'b')) - cdata->item = NULL; - else + cdata = xcalloc(1, sizeof *cdata); + if (wait) cdata->item = item; + cdata->state = args_make_commands_prepare(self, item, 0, + "select-pane -t \"%%%\"", wait, 0); if (args_has(args, 'N')) { server_client_set_overlay(tc, delay, NULL, NULL, @@ -295,7 +297,7 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) cmd_display_panes_free, NULL, cdata); } - if (args_has(args, 'b')) + if (!wait) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } diff --git a/cmd-if-shell.c b/cmd-if-shell.c index a04bdddd..7804ef27 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -31,8 +31,8 @@ static enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmdq_item *); -static void cmd_if_shell_callback(struct job *); -static void cmd_if_shell_free(void *); +static void cmd_if_shell_callback(struct job *); +static void cmd_if_shell_free(void *); const struct cmd_entry cmd_if_shell_entry = { .name = "if-shell", @@ -49,10 +49,8 @@ const struct cmd_entry cmd_if_shell_entry = { }; struct cmd_if_shell_data { - struct cmd_parse_input input; - - char *cmd_if; - char *cmd_else; + struct cmd_list *cmd_if; + struct cmd_list *cmd_else; struct client *client; struct cmdq_item *item; @@ -63,46 +61,42 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - struct cmdq_state *state = cmdq_get_state(item); struct cmd_if_shell_data *cdata; - char *shellcmd, *error; - const char *cmd = NULL, *file; + struct cmdq_item *new_item; + char *shellcmd; struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; - struct cmd_parse_input pi; - enum cmd_parse_status status; + struct cmd_list *cmdlist = NULL; u_int count = args_count(args); shellcmd = format_single_from_target(item, args_string(args, 0)); if (args_has(args, 'F')) { if (*shellcmd != '0' && *shellcmd != '\0') - cmd = args_string(args, 1); + cmdlist = args_make_commands_now(self, item, 1); else if (count == 3) - cmd = args_string(args, 2); - free(shellcmd); - if (cmd == NULL) + cmdlist = args_make_commands_now(self, item, 2); + else { + free(shellcmd); return (CMD_RETURN_NORMAL); - - memset(&pi, 0, sizeof pi); - cmd_get_source(self, &pi.file, &pi.line); - pi.item = item; - pi.c = tc; - cmd_find_copy_state(&pi.fs, target); - - status = cmd_parse_and_insert(cmd, &pi, item, state, &error); - if (status == CMD_PARSE_ERROR) { - cmdq_error(item, "%s", error); - free(error); - return (CMD_RETURN_ERROR); } + free(shellcmd); + if (cmdlist == NULL) + return (CMD_RETURN_ERROR); + new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); + cmdq_insert_after(item, new_item); return (CMD_RETURN_NORMAL); } cdata = xcalloc(1, sizeof *cdata); - cdata->cmd_if = xstrdup(args_string(args, 1)); - if (count == 3) - cdata->cmd_else = xstrdup(args_string(args, 2)); + cdata->cmd_if = args_make_commands_now(self, item, 1); + if (cdata->cmd_if == NULL) + return (CMD_RETURN_ERROR); + if (count == 3) { + cdata->cmd_else = args_make_commands_now(self, item, 2); + if (cdata->cmd_else == NULL) + return (CMD_RETURN_ERROR); + } if (!args_has(args, 'b')) cdata->client = cmdq_get_client(item); @@ -114,14 +108,6 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'b')) cdata->item = item; - cmd_get_source(self, &file, &cdata->input.line); - if (file != NULL) - cdata->input.file = xstrdup(file); - cdata->input.c = tc; - if (cdata->input.c != NULL) - cdata->input.c->references++; - cmd_find_copy_state(&cdata->input.fs, target); - if (job_run(shellcmd, 0, NULL, s, server_client_get_cwd(cmdq_get_client(item), s), NULL, cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1, @@ -143,43 +129,24 @@ cmd_if_shell_callback(struct job *job) { struct cmd_if_shell_data *cdata = job_get_data(job); struct client *c = cdata->client; - struct cmdq_item *new_item = NULL; - struct cmdq_state *new_state = NULL; - char *cmd; + struct cmdq_item *item = cdata->item, *new_item; + struct cmd_list *cmdlist; int status; - struct cmd_parse_result *pr; status = job_get_status(job); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - cmd = cdata->cmd_else; + cmdlist = cdata->cmd_else; else - cmd = cdata->cmd_if; - if (cmd == NULL) + cmdlist = cdata->cmd_if; + if (cmdlist == NULL) goto out; - pr = cmd_parse_from_string(cmd, &cdata->input); - switch (pr->status) { - case CMD_PARSE_ERROR: - if (cdata->item != NULL) - cmdq_error(cdata->item, "%s", pr->error); - free(pr->error); - break; - case CMD_PARSE_SUCCESS: - if (cdata->item == NULL) - new_state = cmdq_new_state(NULL, NULL, 0); - else - new_state = cmdq_get_state(cdata->item); - new_item = cmdq_get_command(pr->cmdlist, new_state); - if (cdata->item == NULL) - cmdq_free_state(new_state); - cmd_list_free(pr->cmdlist); - break; - } - if (new_item != NULL) { - if (cdata->item == NULL) - cmdq_append(c, new_item); - else - cmdq_insert_after(cdata->item, new_item); + if (item == NULL) { + new_item = cmdq_get_command(cmdlist, NULL); + cmdq_append(c, new_item); + } else { + new_item = cmdq_get_command(cmdlist, cmdq_get_state(item)); + cmdq_insert_after(item, new_item); } out: @@ -195,12 +162,9 @@ cmd_if_shell_free(void *data) if (cdata->client != NULL) server_client_unref(cdata->client); - free(cdata->cmd_else); - free(cdata->cmd_if); - - if (cdata->input.c != NULL) - server_client_unref(cdata->input.c); - free((void *)cdata->input.file); + if (cdata->cmd_else != NULL) + cmd_list_free(cdata->cmd_else); + cmd_list_free(cdata->cmd_if); free(cdata); } diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 207e4710..f23a8ec3 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -53,14 +53,13 @@ const struct cmd_entry cmd_run_shell_entry = { struct cmd_run_shell_data { struct client *client; char *cmd; - int shell; + struct cmd_list *cmdlist; char *cwd; struct cmdq_item *item; struct session *s; int wp_id; struct event timer; int flags; - struct cmd_parse_input pi; }; static void @@ -100,11 +99,10 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) struct client *tc = cmdq_get_target_client(item); struct session *s = target->s; struct window_pane *wp = target->wp; - const char *delay; + const char *delay, *cmd; double d; struct timeval tv; char *end; - const char *cmd = args_string(args, 0); int wait = !args_has(args, 'b'); if ((delay = args_get(args, 'd')) != NULL) { @@ -117,16 +115,14 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); cdata = xcalloc(1, sizeof *cdata); - if (cmd != NULL) - cdata->cmd = format_single_from_target(item, cmd); - - cdata->shell = !args_has(args, 'C'); - if (!cdata->shell) { - cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); - if (wait) - cdata->pi.item = item; - cdata->pi.c = tc; - cmd_find_copy_state(&cdata->pi.fs, target); + if (!args_has(args, 'C')) { + cmd = args_string(args, 0); + if (cmd != NULL) + cdata->cmd = format_single_from_target(item, cmd); + } else { + cdata->cmdlist = args_make_commands_now(self, item, 0); + if (cdata->cmdlist == NULL) + return (CMD_RETURN_ERROR); } if (args_has(args, 't') && wp != NULL) @@ -170,11 +166,9 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) struct cmd_run_shell_data *cdata = arg; struct client *c = cdata->client; const char *cmd = cdata->cmd; - char *error; - struct cmdq_item *item = cdata->item; - enum cmd_parse_status status; + struct cmdq_item *item = cdata->item, *new_item; - if (cmd != NULL && cdata->shell) { + if (cdata->cmdlist == NULL && cmd != NULL) { if (job_run(cmd, 0, NULL, cdata->s, cdata->cwd, NULL, cmd_run_shell_callback, cmd_run_shell_free, cdata, cdata->flags, -1, -1) == NULL) @@ -182,21 +176,14 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) return; } - if (cmd != NULL) { - if (item != NULL) { - status = cmd_parse_and_insert(cmd, &cdata->pi, item, - cmdq_get_state(item), &error); + if (cdata->cmdlist != NULL) { + if (item == NULL) { + new_item = cmdq_get_command(cdata->cmdlist, NULL); + cmdq_append(c, new_item); } else { - status = cmd_parse_and_append(cmd, &cdata->pi, c, NULL, - &error); - } - if (status == CMD_PARSE_ERROR) { - if (cdata->item == NULL) { - *error = toupper((u_char)*error); - status_message_set(c, -1, 1, 0, "%s", error); - } else - cmdq_error(cdata->item, "%s", error); - free(error); + new_item = cmdq_get_command(cdata->cmdlist, + cmdq_get_state(item)); + cmdq_insert_after(item, new_item); } } @@ -265,6 +252,8 @@ cmd_run_shell_free(void *data) session_remove_ref(cdata->s, __func__); if (cdata->client != NULL) server_client_unref(cdata->client); + if (cdata->cmdlist != NULL) + cmd_list_free(cdata->cmdlist); free(cdata->cwd); free(cdata->cmd); free(cdata); diff --git a/tmux.h b/tmux.h index fa0268a2..6bdc7e6a 100644 --- a/tmux.h +++ b/tmux.h @@ -37,6 +37,7 @@ extern char **environ; struct args; +struct args_command_state; struct client; struct cmd; struct cmd_find_state; @@ -2213,6 +2214,14 @@ u_char args_next(struct args_entry **); u_int args_count(struct args *); struct args_value *args_value(struct args *, u_int); const char *args_string(struct args *, u_int); +struct cmd_list *args_make_commands_now(struct cmd *, struct cmdq_item *, + u_int); +struct args_command_state *args_make_commands_prepare(struct cmd *, + struct cmdq_item *, u_int, const char *, int, int); +struct cmd_list *args_make_commands(struct args_command_state *, int, char **, + char **); +void args_make_commands_free(struct args_command_state *); +char *args_make_commands_get_command(struct args_command_state *); struct args_value *args_first_value(struct args *, u_char); struct args_value *args_next_value(struct args_value *); long long args_strtonum(struct args *, u_char, long long, long long,