When adding a list of commands to the queue, instead of automatically

creating a new state for each group of commands, require the caller to
create one and use it for all the commands in the list. This means the
current target works even with list with multiple groups (which can
happen if they are defined with newlines).
pull/2172/head
nicm 2020-04-13 15:55:51 +00:00
parent adb76fd1ce
commit 3f86d6d460
16 changed files with 187 additions and 121 deletions

4
cfg.c
View File

@ -183,7 +183,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
return (0);
}
new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
new_item0 = cmdq_get_command(pr->cmdlist, NULL);
if (item != NULL)
new_item0 = cmdq_insert_after(item, new_item0);
else
@ -229,7 +229,7 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
return (0);
}
new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
new_item0 = cmdq_get_command(pr->cmdlist, NULL);
if (item != NULL)
new_item0 = cmdq_insert_after(item, new_item0);
else

View File

@ -177,7 +177,7 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
new_item = cmdq_get_command(pr->cmdlist, NULL);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;

View File

@ -109,7 +109,7 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
new_item = cmdq_get_command(pr->cmdlist, NULL);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;

View File

@ -225,7 +225,7 @@ cmd_display_panes_key(struct client *c, struct key_event *event)
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
new_item = cmdq_get_command(pr->cmdlist, NULL);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;

View File

@ -56,7 +56,6 @@ struct cmd_if_shell_data {
struct client *client;
struct cmdq_item *item;
struct key_event event;
};
static enum cmd_retval
@ -64,7 +63,7 @@ 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 key_event *event = cmdq_get_event(item);
struct cmdq_state *state = cmdq_get_state(item);
struct cmd_if_shell_data *cdata;
char *shellcmd, *cmd;
const char *file;
@ -101,8 +100,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
free(pr->error);
return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, target, event,
0);
new_item = cmdq_get_command(pr->cmdlist, state);
cmdq_insert_after(item, new_item);
cmd_list_free(pr->cmdlist);
break;
@ -117,7 +115,6 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->cmd_else = xstrdup(args->argv[2]);
else
cdata->cmd_else = NULL;
memcpy(&cdata->event, event, sizeof cdata->event);
if (!args_has(args, 'b'))
cdata->client = cmdq_get_client(item);
@ -161,8 +158,8 @@ cmd_if_shell_callback(struct job *job)
{
struct cmd_if_shell_data *cdata = job_get_data(job);
struct client *c = cdata->client;
struct key_event *event = &cdata->event;
struct cmdq_item *new_item = NULL;
struct cmdq_state *new_state = NULL;
char *cmd;
int status;
struct cmd_parse_result *pr;
@ -185,7 +182,13 @@ cmd_if_shell_callback(struct job *job)
free(pr->error);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, event, 0);
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;
}

View File

@ -344,10 +344,10 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
free(cp);
}
if (!detached) {
if (!detached)
c->flags |= CLIENT_ATTACHED;
if (!args_has(args, 'd'))
cmd_find_from_session(current, s, 0);
}
cmd_find_from_session(&fs, s, 0);
cmdq_insert_hook(s, item, &fs, "after-new-session");

View File

@ -145,6 +145,13 @@ cmdq_get_client(struct cmdq_item *item)
return (item->client);
}
/* Get item state. */
struct cmdq_state *
cmdq_get_state(struct cmdq_item *item)
{
return (item->state);
}
/* Get item target. */
struct cmd_find_state *
cmdq_get_target(struct cmdq_item *item)
@ -180,6 +187,70 @@ cmdq_get_flags(struct cmdq_item *item)
return (item->state->flags);
}
/* Create a new state. */
struct cmdq_state *
cmdq_new_state(struct cmd_find_state *current, struct key_event *event,
int flags)
{
struct cmdq_state *state;
state = xcalloc(1, sizeof *state);
state->references = 1;
state->flags = flags;
if (event != NULL)
memcpy(&state->event, event, sizeof state->event);
else
state->event.key = KEYC_NONE;
if (current != NULL && cmd_find_valid_state(current))
cmd_find_copy_state(&state->current, current);
else
cmd_find_clear_state(&state->current, 0);
return (state);
}
/* Add a reference to a state. */
struct cmdq_state *
cmdq_link_state(struct cmdq_state *state)
{
state->references++;
return (state);
}
/* Make a copy of a state. */
struct cmdq_state *
cmdq_copy_state(struct cmdq_state *state)
{
return (cmdq_new_state(&state->current, &state->event, state->flags));
}
/* Free a state. */
void
cmdq_free_state(struct cmdq_state *state)
{
if (--state->references == 0)
free(state);
}
/* Add a format to command queue. */
void
cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...)
{
va_list ap;
char *value;
va_start(ap, fmt);
xvasprintf(&value, fmt, ap);
va_end(ap);
if (state->formats == NULL)
state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
format_add(state->formats, key, "%s", value);
free(value);
}
/* Merge formats from item. */
void
cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
@ -249,12 +320,14 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
/* Insert a hook. */
void
cmdq_insert_hook(struct session *s, struct cmdq_item *item,
struct cmd_find_state *fs, const char *fmt, ...)
struct cmd_find_state *current, const char *fmt, ...)
{
struct cmdq_state *state = item->state;
struct options *oo;
va_list ap;
char *name;
struct cmdq_item *new_item;
struct cmdq_state *new_state;
struct options_entry *o;
struct options_array_item *a;
struct cmd_list *cmdlist;
@ -277,25 +350,27 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item,
}
log_debug("running hook %s (parent %p)", name, item);
/*
* The hooks get a new state because they should not update the current
* target or formats for any subsequent commands.
*/
new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS);
cmdq_add_format(new_state, "hook", "%s", name);
a = options_array_first(o);
while (a != NULL) {
cmdlist = options_array_item_value(a)->cmdlist;
if (cmdlist == NULL) {
a = options_array_next(a);
continue;
if (cmdlist != NULL) {
new_item = cmdq_get_command(cmdlist, new_state);
if (item != NULL)
item = cmdq_insert_after(item, new_item);
else
item = cmdq_append(NULL, new_item);
}
new_item = cmdq_get_command(cmdlist, fs, NULL,
CMDQ_STATE_NOHOOKS);
cmdq_format(new_item, "hook", "%s", name);
if (item != NULL)
item = cmdq_insert_after(item, new_item);
else
item = cmdq_append(NULL, new_item);
a = options_array_next(a);
}
cmdq_free_state(new_state);
free(name);
}
@ -310,17 +385,11 @@ cmdq_continue(struct cmdq_item *item)
static void
cmdq_remove(struct cmdq_item *item)
{
if (item->state != NULL && --item->state->references == 0) {
if (item->state->formats != NULL)
format_free(item->state->formats);
free(item->state);
}
if (item->client != NULL)
server_client_unref(item->client);
if (item->cmdlist != NULL)
cmd_list_free(item->cmdlist);
cmdq_free_state(item->state);
TAILQ_REMOVE(item->queue, item, entry);
@ -347,45 +416,34 @@ cmdq_remove_group(struct cmdq_item *item)
/* Get a command for the command queue. */
struct cmdq_item *
cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
struct key_event *event, int flags)
cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state)
{
struct cmdq_item *item, *first = NULL, *last = NULL;
struct cmd *cmd;
const struct cmd_entry *entry;
struct cmdq_state *state = NULL;
u_int group, last_group = 0;
int created = 0;
cmd = cmd_list_first(cmdlist, &group);
if (state == NULL) {
state = cmdq_new_state(NULL, NULL, 0);
created = 1;
}
cmd = cmd_list_first(cmdlist);
while (cmd != NULL) {
if (group != last_group) {
state = xcalloc(1, sizeof *state);
if (current != NULL)
cmd_find_copy_state(&state->current, current);
else
cmd_find_clear_state(&state->current, 0);
if (event != NULL) {
memcpy(&state->event, event,
sizeof state->event);
}
state->flags = flags;
last_group = group;
}
entry = cmd_get_entry(cmd);
item = xcalloc(1, sizeof *item);
xasprintf(&item->name, "[%s/%p]", entry->name, item);
item->type = CMDQ_COMMAND;
item->group = group;
item->state = state;
item->group = cmd_get_group(cmd);
item->state = cmdq_link_state(state);
item->cmdlist = cmdlist;
item->cmd = cmd;
log_debug("%s: %s group %u", __func__, item->name, item->group);
state->references++;
cmdlist->references++;
log_debug("%s: %s group %u", __func__, item->name, item->group);
if (first == NULL)
first = item;
@ -393,8 +451,11 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
last->next = item;
last = item;
cmd = cmd_list_next(cmd, &group);
cmd = cmd_list_next(cmd);
}
if (created)
cmdq_free_state(state);
return (first);
}
@ -484,7 +545,9 @@ cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
item = xcalloc(1, sizeof *item);
xasprintf(&item->name, "[%s/%p]", name, item);
item->type = CMDQ_CALLBACK;
item->group = 0;
item->state = cmdq_new_state(NULL, NULL, 0);
item->cb = cb;
item->data = data;
@ -518,25 +581,6 @@ cmdq_fire_callback(struct cmdq_item *item)
return (item->cb(item, item->data));
}
/* Add a format to command queue. */
void
cmdq_format(struct cmdq_item *item, const char *key, const char *fmt, ...)
{
struct cmdq_state *state = item->state;
va_list ap;
char *value;
va_start(ap, fmt);
xvasprintf(&value, fmt, ap);
va_end(ap);
if (state->formats == NULL)
state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
format_add(state->formats, key, "%s", value);
free(value);
}
/* Process next item on command queue. */
u_int
cmdq_next(struct client *c)

23
cmd.c
View File

@ -392,6 +392,13 @@ cmd_get_args(struct cmd *cmd)
return (cmd->args);
}
/* Get group for command. */
u_int
cmd_get_group(struct cmd *cmd)
{
return (cmd->group);
}
/* Get file and line for command. */
void
cmd_get_source(struct cmd *cmd, const char **file, u_int *line)
@ -646,24 +653,16 @@ cmd_list_print(struct cmd_list *cmdlist, int escaped)
/* Get first command in list. */
struct cmd *
cmd_list_first(struct cmd_list *cmdlist, u_int *group)
cmd_list_first(struct cmd_list *cmdlist)
{
struct cmd *cmd;
cmd = TAILQ_FIRST(cmdlist->list);
if (cmd != NULL && group != NULL)
*group = cmd->group;
return (cmd);
return (TAILQ_FIRST(cmdlist->list));
}
/* Get next command in list. */
struct cmd *
cmd_list_next(struct cmd *cmd, u_int *group)
cmd_list_next(struct cmd *cmd)
{
cmd = TAILQ_NEXT(cmd, qentry);
if (cmd != NULL && group != NULL)
*group = cmd->group;
return (cmd);
return (TAILQ_NEXT(cmd, qentry));
}
/* Do all of the commands in this command list have this flag? */

View File

@ -60,6 +60,7 @@ control_callback(__unused struct client *c, __unused const char *path,
{
char *line;
struct cmdq_item *item;
struct cmdq_state *state;
struct cmd_parse_result *pr;
if (closed || error != 0)
@ -85,9 +86,10 @@ control_callback(__unused struct client *c, __unused const char *path,
cmdq_append(c, item);
break;
case CMD_PARSE_SUCCESS:
item = cmdq_get_command(pr->cmdlist, NULL, NULL,
CMDQ_STATE_CONTROL);
state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL);
item = cmdq_get_command(pr->cmdlist, state);
cmdq_append(c, item);
cmdq_free_state(state);
cmd_list_free(pr->cmdlist);
break;
}

View File

@ -521,7 +521,7 @@ key_bindings_init(void)
pr = cmd_parse_from_string(defaults[i], NULL);
if (pr->status != CMD_PARSE_SUCCESS)
fatalx("bad default key: %s", defaults[i]);
cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL, NULL, 0));
cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL));
cmd_list_free(pr->cmdlist);
}
}
@ -538,6 +538,7 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
struct client *c, struct key_event *event, struct cmd_find_state *fs)
{
struct cmdq_item *new_item;
struct cmdq_state *new_state;
int readonly, flags = 0;
if (c == NULL || (~c->flags & CLIENT_READONLY))
@ -549,7 +550,9 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
else {
if (bd->flags & KEY_BINDING_REPEAT)
flags |= CMDQ_STATE_REPEAT;
new_item = cmdq_get_command(bd->cmdlist, fs, event, flags);
new_state = cmdq_new_state(fs, event, flags);
new_item = cmdq_get_command(bd->cmdlist, new_state);
cmdq_free_state(new_state);
}
if (item != NULL)
new_item = cmdq_insert_after(item, new_item);

5
menu.c
View File

@ -185,6 +185,7 @@ menu_key_cb(struct client *c, struct key_event *event)
int count = menu->count, old = md->choice;
const struct menu_item *item;
struct cmdq_item *new_item;
struct cmdq_state *new_state;
struct cmd_parse_result *pr;
const char *name;
@ -285,7 +286,9 @@ chosen:
event = cmdq_get_event(md->item);
else
event = NULL;
new_item = cmdq_get_command(pr->cmdlist, &md->fs, event, 0);
new_state = cmdq_new_state(&md->fs, event, 0);
new_item = cmdq_get_command(pr->cmdlist, new_state);
cmdq_free_state(new_state);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;

View File

@ -1064,6 +1064,7 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
const char *template, const char *name)
{
struct cmdq_item *new_item;
struct cmdq_state *new_state;
char *command;
struct cmd_parse_result *pr;
@ -1085,7 +1086,9 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
free(pr->error);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, fs, NULL, 0);
new_state = cmdq_new_state(fs, NULL, 0);
new_item = cmdq_get_command(pr->cmdlist, new_state);
cmdq_free_state(new_state);
cmdq_append(c, new_item);
cmd_list_free(pr->cmdlist);
break;

View File

@ -36,19 +36,19 @@ struct notify_entry {
};
static void
notify_hook_formats(struct cmdq_item *item, struct session *s, struct window *w,
int pane)
notify_hook_formats(struct cmdq_state *state, struct session *s,
struct window *w, int pane)
{
if (s != NULL) {
cmdq_format(item, "hook_session", "$%u", s->id);
cmdq_format(item, "hook_session_name", "%s", s->name);
cmdq_add_format(state, "hook_session", "$%u", s->id);
cmdq_add_format(state, "hook_session_name", "%s", s->name);
}
if (w != NULL) {
cmdq_format(item, "hook_window", "@%u", w->id);
cmdq_format(item, "hook_window_name", "%s", w->name);
cmdq_add_format(state, "hook_window", "@%u", w->id);
cmdq_add_format(state, "hook_window_name", "%s", w->name);
}
if (pane != -1)
cmdq_format(item, "hook_pane", "%%%d", pane);
cmdq_add_format(state, "hook_pane", "%%%d", pane);
}
static void
@ -57,6 +57,7 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne)
struct cmd_find_state fs;
struct options *oo;
struct cmdq_item *new_item;
struct cmdq_state *new_state;
struct session *s = ne->session;
struct window *w = ne->window;
struct options_entry *o;
@ -87,22 +88,21 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne)
if (o == NULL)
return;
new_state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS);
cmdq_add_format(new_state, "hook", "%s", ne->name);
notify_hook_formats(new_state, s, w, ne->pane);
a = options_array_first(o);
while (a != NULL) {
cmdlist = options_array_item_value(a)->cmdlist;
if (cmdlist == NULL) {
a = options_array_next(a);
continue;
if (cmdlist != NULL) {
new_item = cmdq_get_command(cmdlist, new_state);
item = cmdq_insert_after(item, new_item);
}
new_item = cmdq_get_command(cmdlist, &fs, NULL,
CMDQ_STATE_NOHOOKS);
cmdq_format(new_item, "hook", "%s", ne->name);
notify_hook_formats(new_item, s, w, ne->pane);
item = cmdq_insert_after(item, new_item);
a = options_array_next(a);
}
cmdq_free_state(new_state);
}
static enum cmd_retval

View File

@ -223,6 +223,7 @@ popup_key_cb(struct client *c, struct key_event *event)
struct mouse_event *m = &event->m;
struct cmd_find_state *fs = &pd->fs;
struct cmdq_item *new_item;
struct cmdq_state *new_state;
struct cmd_parse_result *pr;
struct format_tree *ft;
const char *cmd, *buf;
@ -308,7 +309,9 @@ popup_key_cb(struct client *c, struct key_event *event)
event = cmdq_get_event(pd->item);
else
event = NULL;
new_item = cmdq_get_command(pr->cmdlist, fs, event, 0);
new_state = cmdq_new_state(&pd->fs, event, 0);
new_item = cmdq_get_command(pr->cmdlist, new_state);
cmdq_free_state(new_state);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;

View File

@ -1949,7 +1949,7 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
}
cmd_free_argv(argc, argv);
cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL, NULL, 0));
cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL));
cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL));
cmd_list_free(pr->cmdlist);

20
tmux.h
View File

@ -2058,6 +2058,7 @@ char *cmd_stringify_argv(int, char **);
char *cmd_get_alias(const char *);
const struct cmd_entry *cmd_get_entry(struct cmd *);
struct args *cmd_get_args(struct cmd *);
u_int cmd_get_group(struct cmd *);
void cmd_get_source(struct cmd *, const char **, u_int *);
struct cmd *cmd_parse(int, char **, const char *, u_int, char **);
void cmd_free(struct cmd *);
@ -2067,8 +2068,8 @@ void cmd_list_append(struct cmd_list *, struct cmd *);
void cmd_list_move(struct cmd_list *, struct cmd_list *);
void cmd_list_free(struct cmd_list *);
char *cmd_list_print(struct cmd_list *, int);
struct cmd *cmd_list_first(struct cmd_list *, u_int *);
struct cmd *cmd_list_next(struct cmd *, u_int *);
struct cmd *cmd_list_first(struct cmd_list *);
struct cmd *cmd_list_next(struct cmd *);
int cmd_list_all_have(struct cmd_list *, int);
int cmd_list_any_have(struct cmd_list *, int);
int cmd_mouse_at(struct window_pane *, struct mouse_event *,
@ -2093,18 +2094,25 @@ struct cmd_parse_result *cmd_parse_from_arguments(int, char **,
struct cmd_parse_input *);
/* cmd-queue.c */
struct cmdq_state *cmdq_new_state(struct cmd_find_state *, struct key_event *,
int);
struct cmdq_state *cmdq_link_state(struct cmdq_state *);
struct cmdq_state *cmdq_copy_state(struct cmdq_state *);
void cmdq_free_state(struct cmdq_state *);
void printflike(3, 4) cmdq_add_format(struct cmdq_state *, const char *,
const char *, ...);
void cmdq_merge_formats(struct cmdq_item *, struct format_tree *);
struct cmdq_list *cmdq_new(void);
void cmdq_free(struct cmdq_list *);
const char *cmdq_get_name(struct cmdq_item *);
struct client *cmdq_get_client(struct cmdq_item *);
struct cmdq_state *cmdq_get_state(struct cmdq_item *);
struct cmd_find_state *cmdq_get_target(struct cmdq_item *);
struct cmd_find_state *cmdq_get_source(struct cmdq_item *);
struct key_event *cmdq_get_event(struct cmdq_item *);
struct cmd_find_state *cmdq_get_current(struct cmdq_item *);
int cmdq_get_flags(struct cmdq_item *);
void cmdq_merge_formats(struct cmdq_item *, struct format_tree *);
struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *,
struct key_event *, int);
struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmdq_state *);
#define cmdq_get_callback(cb, data) cmdq_get_callback1(#cb, cb, data)
struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *);
struct cmdq_item *cmdq_get_error(const char *);
@ -2113,8 +2121,6 @@ struct cmdq_item *cmdq_append(struct client *, struct cmdq_item *);
void cmdq_insert_hook(struct session *, struct cmdq_item *,
struct cmd_find_state *, const char *, ...);
void cmdq_continue(struct cmdq_item *);
void printflike(3, 4) cmdq_format(struct cmdq_item *, const char *,
const char *, ...);
u_int cmdq_next(struct client *);
void cmdq_guard(struct cmdq_item *, const char *, int);
void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...);