Rewrite command queue handling. Each client still has a command queue,

but there is also now a global command queue. Instead of command queues
being dispatched on demand from wherever the command happens to be
added, they are now all dispatched from the top level server
loop. Command queues may now also include callbacks as well as commands,
and items may be inserted after the current command as well as at the end.

This all makes command queues significantly more predictable and easier
to use, and avoids the complex multiple nested command queues used by
source-file, if-shell and friends.

A mass rename of struct cmdq to a better name (cmdq_item probably) is
coming.
pull/593/head
nicm 2016-10-16 17:55:14 +00:00
parent bfe14b5312
commit ddc4512d2e
29 changed files with 817 additions and 702 deletions

70
cfg.c
View File

@ -29,14 +29,25 @@
#include "tmux.h"
char *cfg_file;
static struct cmd_q *cfg_cmd_q;
int cfg_finished;
int cfg_references;
static char **cfg_causes;
static u_int cfg_ncauses;
struct client *cfg_client;
static void cfg_default_done(struct cmd_q *);
static enum cmd_retval
cfg_done(__unused struct cmd_q *cmdq, __unused void *data)
{
if (cfg_finished)
return (CMD_RETURN_NORMAL);
cfg_finished = 1;
if (!RB_EMPTY(&sessions))
cfg_show_causes(RB_MIN(sessions, &sessions));
if (cfg_client != NULL)
server_client_unref(cfg_client);
return (CMD_RETURN_NORMAL);
}
void
set_cfg_file(const char *path)
@ -51,30 +62,24 @@ start_cfg(void)
const char *home;
int quiet = 0;
cfg_cmd_q = cmdq_new(NULL);
cfg_cmd_q->emptyfn = cfg_default_done;
cfg_finished = 0;
cfg_references = 1;
cfg_client = TAILQ_FIRST(&clients);
if (cfg_client != NULL)
cfg_client->references++;
load_cfg(TMUX_CONF, cfg_cmd_q, 1);
load_cfg(TMUX_CONF, cfg_client, NULL, 1);
if (cfg_file == NULL && (home = find_home()) != NULL) {
xasprintf(&cfg_file, "%s/.tmux.conf", home);
quiet = 1;
}
if (cfg_file != NULL)
load_cfg(cfg_file, cfg_cmd_q, quiet);
load_cfg(cfg_file, cfg_client, NULL, quiet);
cmdq_continue(cfg_cmd_q);
cmdq_append(cfg_client, cmdq_get_callback(cfg_done, NULL));
}
int
load_cfg(const char *path, struct cmd_q *cmdq, int quiet)
load_cfg(const char *path, struct client *c, struct cmd_q *cmdq, int quiet)
{
FILE *f;
char delim[3] = { '\\', '\\', '\0' };
@ -82,6 +87,7 @@ load_cfg(const char *path, struct cmd_q *cmdq, int quiet)
size_t line = 0;
char *buf, *cause1, *p;
struct cmd_list *cmdlist;
struct cmd_q *new_cmdq;
log_debug("loading %s", path);
if ((f = fopen(path, "rb")) == NULL) {
@ -117,8 +123,13 @@ load_cfg(const char *path, struct cmd_q *cmdq, int quiet)
if (cmdlist == NULL)
continue;
cmdq_append(cmdq, cmdlist, NULL);
new_cmdq = cmdq_get_command(cmdlist, NULL, NULL, 0);
if (cmdq != NULL)
cmdq_insert_after(cmdq, new_cmdq);
else
cmdq_append(c, new_cmdq);
cmd_list_free(cmdlist);
found++;
}
fclose(f);
@ -126,37 +137,6 @@ load_cfg(const char *path, struct cmd_q *cmdq, int quiet)
return (found);
}
static void
cfg_default_done(__unused struct cmd_q *cmdq)
{
log_debug("%s: %u references%s", __func__, cfg_references,
cfg_finished ? " (finished)" : "");
if (cfg_finished || --cfg_references != 0)
return;
cfg_finished = 1;
if (!RB_EMPTY(&sessions))
cfg_show_causes(RB_MIN(sessions, &sessions));
cmdq_free(cfg_cmd_q);
cfg_cmd_q = NULL;
if (cfg_client != NULL) {
/*
* The client command queue starts with client_exit set to 1 so
* only continue if not empty (that is, we have been delayed
* during configuration parsing for long enough that the
* MSG_COMMAND has arrived), else the client will exit before
* the MSG_COMMAND which might tell it not to.
*/
if (!TAILQ_EMPTY(&cfg_client->cmdq->queue))
cmdq_continue(cfg_client->cmdq);
server_client_unref(cfg_client);
cfg_client = NULL;
}
}
void
cfg_add_cause(const char *fmt, ...)
{

View File

@ -145,7 +145,7 @@ cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag,
if (~c->flags & CLIENT_CONTROL)
proc_send(c->peer, MSG_READY, -1, NULL, 0);
hooks_run(c->session->hooks, c, NULL, "client-attached");
cmdq->client_exit = 0;
c->flags |= CLIENT_ATTACHED;
}
recalculate_sizes();
alerts_check_session(s);

View File

@ -121,12 +121,24 @@ cmd_command_prompt_exec(struct cmd *self, struct cmd_q *cmdq)
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_command_prompt_error(struct cmd_q *cmdq, void *data)
{
char *error = data;
cmdq_error(cmdq, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
static int
cmd_command_prompt_callback(void *data, const char *s)
{
struct cmd_command_prompt_cdata *cdata = data;
struct client *c = cdata->c;
struct cmd_list *cmdlist;
struct cmd_q *new_cmdq;
char *cause, *new_template, *prompt, *ptr;
char *input = NULL;
@ -153,17 +165,19 @@ cmd_command_prompt_callback(void *data, const char *s)
if (cmd_string_parse(new_template, &cmdlist, NULL, 0, &cause) != 0) {
if (cause != NULL) {
*cause = toupper((u_char) *cause);
status_message_set(c, "%s", cause);
free(cause);
}
return (0);
new_cmdq = cmdq_get_callback(cmd_command_prompt_error,
cause);
} else
new_cmdq = NULL;
} else {
new_cmdq = cmdq_get_command(cmdlist, NULL, NULL, 0);
cmd_list_free(cmdlist);
}
cmdq_run(c->cmdq, cmdlist, NULL);
cmd_list_free(cmdlist);
if (new_cmdq != NULL)
cmdq_append(c, new_cmdq);
if (c->prompt_callbackfn != (void *) &cmd_command_prompt_callback)
if (c->prompt_callbackfn != (void *)&cmd_command_prompt_callback)
return (1);
return (0);
}

View File

@ -83,12 +83,24 @@ cmd_confirm_before_exec(struct cmd *self, struct cmd_q *cmdq)
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_confirm_before_error(struct cmd_q *cmdq, void *data)
{
char *error = data;
cmdq_error(cmdq, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
static int
cmd_confirm_before_callback(void *data, const char *s)
{
struct cmd_confirm_before_data *cdata = data;
struct client *c = cdata->client;
struct cmd_list *cmdlist;
struct cmd_q *new_cmdq;
char *cause;
if (c->flags & CLIENT_DEAD)
@ -101,14 +113,17 @@ cmd_confirm_before_callback(void *data, const char *s)
if (cmd_string_parse(cdata->cmd, &cmdlist, NULL, 0, &cause) != 0) {
if (cause != NULL) {
cmdq_error(c->cmdq, "%s", cause);
free(cause);
}
return (0);
new_cmdq = cmdq_get_callback(cmd_confirm_before_error,
cause);
} else
new_cmdq = NULL;
} else {
new_cmdq = cmdq_get_command(cmdlist, NULL, NULL, 0);
cmd_list_free(cmdlist);
}
cmdq_run(c->cmdq, cmdlist, NULL);
cmd_list_free(cmdlist);
if (new_cmdq != NULL)
cmdq_append(c, new_cmdq);
return (0);
}

View File

@ -61,7 +61,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq)
struct window_pane *wp = cmdq->state.tflag.wp;
if (args_has(args, 'M')) {
if ((wp = cmd_mouse_pane(&cmdq->item->mouse, &s, NULL)) == NULL)
if ((wp = cmd_mouse_pane(&cmdq->mouse, &s, NULL)) == NULL)
return (CMD_RETURN_NORMAL);
if (c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
@ -80,7 +80,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq)
if (args_has(args, 'M')) {
if (wp->mode != NULL && wp->mode != &window_copy_mode)
return (CMD_RETURN_NORMAL);
window_copy_start_drag(c, &cmdq->item->mouse);
window_copy_start_drag(c, &cmdq->mouse);
}
if (wp->mode == &window_copy_mode && args_has(self->args, 'u'))
window_copy_pageup(wp, 0);

View File

@ -65,32 +65,48 @@ cmd_display_panes_exec(struct cmd *self, struct cmd_q *cmdq)
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_display_panes_error(struct cmd_q *cmdq, void *data)
{
char *error = data;
cmdq_error(cmdq, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
static void
cmd_display_panes_callback(struct client *c, struct window_pane *wp)
{
struct cmd_list *cmdlist;
struct cmd_q *new_cmdq;
char *template, *cmd, *expanded, *cause;
template = c->identify_callback_data;
if (wp != NULL) {
xasprintf(&expanded, "%%%u", wp->id);
cmd = cmd_template_replace(template, expanded, 1);
if (wp == NULL)
goto out;
xasprintf(&expanded, "%%%u", wp->id);
cmd = cmd_template_replace(template, expanded, 1);
if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) {
if (cause != NULL) {
*cause = toupper((u_char) *cause);
status_message_set(c, "%s", cause);
free(cause);
}
} else {
cmdq_run(c->cmdq, cmdlist, NULL);
cmd_list_free(cmdlist);
}
free(cmd);
free(expanded);
if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) {
if (cause != NULL) {
new_cmdq = cmdq_get_callback(cmd_display_panes_error,
cause);
} else
new_cmdq = NULL;
} else {
new_cmdq = cmdq_get_command(cmdlist, NULL, NULL, 0);
cmd_list_free(cmdlist);
}
if (new_cmdq != NULL)
cmdq_append(c, new_cmdq);
free(cmd);
free(expanded);
out:
free(c->identify_callback_data);
c->identify_callback_data = NULL;
c->identify_callback = NULL;

View File

@ -1006,7 +1006,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_find_state *current,
/* Mouse target is a plain = or {mouse}. */
if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
m = &cmdq->item->mouse;
m = &cmdq->mouse;
switch (type) {
case CMD_FIND_PANE:
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);

View File

@ -31,9 +31,9 @@
static enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmd_q *);
static void cmd_if_shell_callback(struct job *);
static void cmd_if_shell_done(struct cmd_q *);
static void cmd_if_shell_free(void *);
static enum cmd_retval cmd_if_shell_error(struct cmd_q *, 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",
@ -56,11 +56,9 @@ struct cmd_if_shell_data {
char *cmd_if;
char *cmd_else;
struct client *client;
struct cmd_q *cmdq;
struct mouse_event mouse;
int bflag;
int references;
};
static enum cmd_retval
@ -70,6 +68,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq)
struct cmd_if_shell_data *cdata;
char *shellcmd, *cmd, *cause;
struct cmd_list *cmdlist;
struct cmd_q *new_cmdq;
struct session *s = cmdq->state.tflag.s;
struct winlink *wl = cmdq->state.tflag.wl;
struct window_pane *wp = cmdq->state.tflag.wp;
@ -104,7 +103,8 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq)
}
return (CMD_RETURN_ERROR);
}
cmdq_run(cmdq, cmdlist, &cmdq->item->mouse);
new_cmdq = cmdq_get_command(cmdlist, NULL, &cmdq->mouse, 0);
cmdq_insert_after(cmdq, new_cmdq);
cmd_list_free(cmdlist);
return (CMD_RETURN_NORMAL);
}
@ -121,92 +121,80 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq)
else
cdata->cmd_else = NULL;
cdata->bflag = args_has(args, 'b');
cdata->client = cmdq->client;
cdata->client->references++;
cdata->cmdq = cmdq;
memcpy(&cdata->mouse, &cmdq->item->mouse, sizeof cdata->mouse);
cmdq->references++;
if (!args_has(args, 'b'))
cdata->cmdq = cmdq;
else
cdata->cmdq = NULL;
memcpy(&cdata->mouse, &cmdq->mouse, sizeof cdata->mouse);
cdata->references = 1;
job_run(shellcmd, s, cwd, cmd_if_shell_callback, cmd_if_shell_free,
cdata);
free(shellcmd);
if (cdata->bflag)
if (args_has(args, 'b'))
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
static enum cmd_retval
cmd_if_shell_error(struct cmd_q *cmdq, void *data)
{
char *error = data;
cmdq_error(cmdq, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
static void
cmd_if_shell_callback(struct job *job)
{
struct cmd_if_shell_data *cdata = job->data;
struct cmd_q *cmdq = cdata->cmdq, *cmdq1;
struct client *c = cdata->client;
struct cmd_list *cmdlist;
char *cause, *cmd;
if (cmdq->flags & CMD_Q_DEAD)
return;
struct cmd_q *new_cmdq;
char *cause, *cmd, *file = cdata->file;
u_int line = cdata->line;
if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0)
cmd = cdata->cmd_else;
else
cmd = cdata->cmd_if;
if (cmd == NULL)
return;
goto out;
if (cmd_string_parse(cmd, &cmdlist, cdata->file, cdata->line,
&cause) != 0) {
if (cause != NULL) {
cmdq_error(cmdq, "%s", cause);
free(cause);
}
return;
if (cmd_string_parse(cmd, &cmdlist, file, line, &cause) != 0) {
if (cause != NULL)
new_cmdq = cmdq_get_callback(cmd_if_shell_error, cause);
else
new_cmdq = NULL;
} else {
new_cmdq = cmdq_get_command(cmdlist, NULL, &cdata->mouse, 0);
cmd_list_free(cmdlist);
}
cmdq1 = cmdq_new(cmdq->client);
cmdq1->emptyfn = cmd_if_shell_done;
cmdq1->data = cdata;
if (new_cmdq != NULL) {
if (cdata->cmdq == NULL)
cmdq_append(c, new_cmdq);
else
cmdq_insert_after(cdata->cmdq, new_cmdq);
}
cdata->references++;
cmdq_run(cmdq1, cmdlist, &cdata->mouse);
cmd_list_free(cmdlist);
}
static void
cmd_if_shell_done(struct cmd_q *cmdq1)
{
struct cmd_if_shell_data *cdata = cmdq1->data;
struct cmd_q *cmdq = cdata->cmdq;
if (cmdq1->client_exit >= 0)
cmdq->client_exit = cmdq1->client_exit;
cmdq_free(cmdq1);
if (--cdata->references != 0)
return;
if (!cmdq_free(cmdq) && !cdata->bflag)
cmdq_continue(cmdq);
free(cdata->cmd_else);
free(cdata->cmd_if);
free(cdata->file);
free(cdata);
out:
if (cdata->cmdq != NULL)
cdata->cmdq->flags &= ~CMD_Q_WAITING;
}
static void
cmd_if_shell_free(void *data)
{
struct cmd_if_shell_data *cdata = data;
struct cmd_q *cmdq = cdata->cmdq;
if (--cdata->references != 0)
return;
if (!cmdq_free(cmdq) && !cdata->bflag)
cmdq_continue(cmdq);
server_client_unref(cdata->client);
free(cdata->cmd_else);
free(cdata->cmd_if);

View File

@ -46,17 +46,24 @@ const struct cmd_entry cmd_load_buffer_entry = {
.exec = cmd_load_buffer_exec
};
struct cmd_load_buffer_data {
struct cmd_q *cmdq;
char *bufname;
};
static enum cmd_retval
cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
{
struct args *args = self->args;
struct client *c = cmdq->client;
struct session *s;
FILE *f;
const char *path, *bufname, *cwd;
char *pdata, *new_pdata, *cause, *file, resolved[PATH_MAX];
size_t psize;
int ch, error;
struct args *args = self->args;
struct cmd_load_buffer_data *cdata;
struct client *c = cmdq->client;
struct session *s;
FILE *f;
const char *path, *bufname, *cwd;
char *pdata, *new_pdata, *cause, *file;
char resolved[PATH_MAX];
size_t psize;
int ch, error;
bufname = NULL;
if (args_has(args, 'b'))
@ -64,8 +71,12 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
path = args->argv[0];
if (strcmp(path, "-") == 0) {
cdata = xcalloc(1, sizeof *cdata);
cdata->cmdq = cmdq;
cdata->bufname = xstrdup(bufname);
error = server_set_stdin_callback(c, cmd_load_buffer_callback,
(void *)bufname, &cause);
cdata, &cause);
if (error != 0) {
cmdq_error(cmdq, "%s: %s", path, cause);
free(cause);
@ -136,9 +147,9 @@ error:
static void
cmd_load_buffer_callback(struct client *c, int closed, void *data)
{
const char *bufname = data;
char *pdata, *cause, *saved;
size_t psize;
struct cmd_load_buffer_data *cdata = data;
char *pdata, *cause, *saved;
size_t psize;
if (!closed)
return;
@ -146,7 +157,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data)
server_client_unref(c);
if (c->flags & CLIENT_DEAD)
return;
goto out;
psize = EVBUFFER_LENGTH(c->stdin_data);
if (psize == 0 || (pdata = malloc(psize + 1)) == NULL)
@ -156,7 +167,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data)
pdata[psize] = '\0';
evbuffer_drain(c->stdin_data, psize);
if (paste_set(pdata, psize, bufname, &cause) != 0) {
if (paste_set(pdata, psize, cdata->bufname, &cause) != 0) {
/* No context so can't use server_client_msg_error. */
if (~c->flags & CLIENT_UTF8) {
saved = cause;
@ -168,7 +179,9 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data)
free(pdata);
free(cause);
}
out:
cmdq_continue(c->cmdq);
cdata->cmdq->flags &= ~CMD_Q_WAITING;
free(cdata->bufname);
free(cdata);
}

View File

@ -312,14 +312,14 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
}
if (!detached)
cmdq->client_exit = 0;
c->flags |= CLIENT_ATTACHED;
if (to_free != NULL)
free((void *)to_free);
cmd_find_from_session(&fs, s);
if (hooks_wait(s->hooks, cmdq, &fs, "after-new-session") == 0)
return (CMD_RETURN_WAIT);
hooks_insert(s->hooks, cmdq, &fs, "after-new-session");
return (CMD_RETURN_NORMAL);
error:

View File

@ -157,8 +157,8 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq)
free((void *)to_free);
cmd_find_from_winlink(&fs, s, wl);
if (hooks_wait(s->hooks, cmdq, &fs, "after-new-window") == 0)
return (CMD_RETURN_WAIT);
hooks_insert(s->hooks, cmdq, &fs, "after-new-window");
return (CMD_RETURN_NORMAL);
error:

View File

@ -25,47 +25,316 @@
#include "tmux.h"
static enum cmd_retval cmdq_continue_one(struct cmd_q *);
static void cmdq_flush(struct cmd_q *);
/* Global command queue. */
static struct cmd_q_list global_queue = TAILQ_HEAD_INITIALIZER(global_queue);
/* Create new command queue. */
/* Get command queue name. */
static const char *
cmdq_name(struct client *c)
{
static char s[32];
if (c == NULL)
return ("<global>");
xsnprintf(s, sizeof s, "<%p>", c);
return (s);
}
/* Get command queue from client. */
static struct cmd_q_list *
cmdq_get(struct client *c)
{
if (c == NULL)
return (&global_queue);
return (&c->queue);
}
/* Append an item. */
void
cmdq_append(struct client *c, struct cmd_q *cmdq)
{
struct cmd_q_list *queue = cmdq_get(c);
struct cmd_q *next;
do {
next = cmdq->next;
cmdq->next = NULL;
if (c != NULL)
c->references++;
cmdq->client = c;
cmdq->queue = queue;
TAILQ_INSERT_TAIL(queue, cmdq, entry);
cmdq = next;
} while (cmdq != NULL);
}
/* Insert an item. */
void
cmdq_insert_after(struct cmd_q *after, struct cmd_q *cmdq)
{
struct client *c = after->client;
struct cmd_q_list *queue = after->queue;
struct cmd_q *next;
do {
next = cmdq->next;
cmdq->next = NULL;
if (c != NULL)
c->references++;
cmdq->client = c;
cmdq->queue = queue;
if (after->next != NULL)
TAILQ_INSERT_AFTER(queue, after->next, cmdq, entry);
else
TAILQ_INSERT_AFTER(queue, after, cmdq, entry);
after->next = cmdq;
cmdq = next;
} while (cmdq != NULL);
}
/* Remove an item. */
static void
cmdq_remove(struct cmd_q *cmdq)
{
free((void *)cmdq->hook);
if (cmdq->client != NULL)
server_client_unref(cmdq->client);
if (cmdq->type == CMD_Q_COMMAND)
cmd_list_free(cmdq->cmdlist);
TAILQ_REMOVE(cmdq->queue, cmdq, entry);
free(cmdq);
}
/* Set command group. */
static u_int
cmdq_next_group(void)
{
static u_int group;
return (++group);
}
/* Remove all subsequent items that match this item's group. */
static void
cmdq_remove_group(struct cmd_q *cmdq)
{
struct cmd_q *this, *next;
this = TAILQ_NEXT(cmdq, entry);
while (this != NULL) {
next = TAILQ_NEXT(this, entry);
if (this->group == cmdq->group)
cmdq_remove(this);
this = next;
}
}
/* Get a command for the command queue. */
struct cmd_q *
cmdq_new(struct client *c)
cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
struct mouse_event *m, int flags)
{
struct cmd_q *cmdq, *first = NULL, *last = NULL;
struct cmd *cmd;
u_int group = cmdq_next_group();
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
cmdq = xcalloc(1, sizeof *cmdq);
cmdq->type = CMD_Q_COMMAND;
cmdq->group = group;
cmdq->flags = flags;
cmdq->cmdlist = cmdlist;
cmdq->cmd = cmd;
if (current != NULL)
cmd_find_copy_state(&cmdq->current, current);
if (m != NULL)
memcpy(&cmdq->mouse, m, sizeof cmdq->mouse);
cmdlist->references++;
if (first == NULL)
first = cmdq;
if (last != NULL)
last->next = cmdq;
last = cmdq;
}
return (first);
}
/* Fire command on command queue. */
static enum cmd_retval
cmdq_fire_command(struct cmd_q *cmdq)
{
struct client *c = cmdq->client;
struct cmd *cmd = cmdq->cmd;
enum cmd_retval retval;
const char *name;
struct cmd_find_state *fsp, fs;
int flags;
flags = !!(cmd->flags & CMD_CONTROL);
cmdq_guard(cmdq, "begin", flags);
if (cmd_prepare_state(cmd, cmdq) != 0) {
retval = CMD_RETURN_ERROR;
goto out;
}
if (cmdq->client == NULL)
cmdq->client = cmd_find_client(cmdq, NULL, CMD_FIND_QUIET);
retval = cmd->entry->exec(cmd, cmdq);
if (retval == CMD_RETURN_ERROR)
goto out;
if (cmd->entry->flags & CMD_AFTERHOOK) {
name = cmd->entry->name;
if (cmd_find_valid_state(&cmdq->state.tflag))
fsp = &cmdq->state.tflag;
else {
if (cmd_find_current(&fs, cmdq, CMD_FIND_QUIET) != 0)
goto out;
fsp = &fs;
}
hooks_insert(fsp->s->hooks, cmdq, fsp, "after-%s", name);
}
out:
cmdq->client = c;
if (retval == CMD_RETURN_ERROR)
cmdq_guard(cmdq, "error", flags);
else
cmdq_guard(cmdq, "end", flags);
return (retval);
}
/* Get a callback for the command queue. */
struct cmd_q *
cmdq_get_callback(cmd_q_cb cb, void *data)
{
struct cmd_q *cmdq;
cmdq = xcalloc(1, sizeof *cmdq);
cmdq->references = 1;
cmdq->type = CMD_Q_CALLBACK;
cmdq->group = 0;
cmdq->flags = 0;
cmdq->client = c;
cmdq->client_exit = -1;
TAILQ_INIT(&cmdq->queue);
cmdq->item = NULL;
cmdq->cmd = NULL;
cmd_find_clear_state(&cmdq->current, NULL, 0);
cmdq->parent = NULL;
cmdq->cb = cb;
cmdq->data = data;
return (cmdq);
}
/* Free command queue */
int
cmdq_free(struct cmd_q *cmdq)
/* Fire callback on callback queue. */
static enum cmd_retval
cmdq_fire_callback(struct cmd_q *cmdq)
{
log_debug("cmdq %p free: %u references", cmdq, cmdq->references);
return (cmdq->cb(cmdq, cmdq->data));
}
if (--cmdq->references != 0) {
if (cmdq->flags & CMD_Q_DEAD)
return (1);
/* Process next item on command queue. */
u_int
cmdq_next(struct client *c)
{
struct cmd_q_list *queue = cmdq_get(c);
const char *name = cmdq_name(c);
struct cmd_q *cmdq;
enum cmd_retval retval;
u_int items = 0;
static u_int number;
if (TAILQ_EMPTY(queue)) {
log_debug("%s %s: empty", __func__, name);
return (0);
}
if (TAILQ_FIRST(queue)->flags & CMD_Q_WAITING) {
log_debug("%s %s: waiting", __func__, name);
return (0);
}
cmdq_flush(cmdq);
free(cmdq);
return (1);
log_debug("%s %s: enter", __func__, name);
for (;;) {
cmdq = TAILQ_FIRST(queue);
if (cmdq == NULL)
break;
log_debug("%s %s: type %d, flags %x", __func__, name,
cmdq->type, cmdq->flags);
/*
* Any item with the waiting flag set waits until an external
* event clears the flag (for example, a job - look at
* run-shell).
*/
if (cmdq->flags & CMD_Q_WAITING)
goto waiting;
/*
* Items are only fired once, once the fired flag is set, a
* waiting flag can only be cleared by an external event.
*/
if (~cmdq->flags & CMD_Q_FIRED) {
cmdq->time = time(NULL);
cmdq->number = ++number;
switch (cmdq->type)
{
case CMD_Q_COMMAND:
retval = cmdq_fire_command(cmdq);
/*
* If a command returns an error, remove any
* subsequent commands in the same group.
*/
if (retval == CMD_RETURN_ERROR)
cmdq_remove_group(cmdq);
break;
case CMD_Q_CALLBACK:
retval = cmdq_fire_callback(cmdq);
break;
default:
retval = CMD_RETURN_ERROR;
break;
}
cmdq->flags |= CMD_Q_FIRED;
if (retval == CMD_RETURN_WAIT) {
cmdq->flags |= CMD_Q_WAITING;
goto waiting;
}
items++;
}
cmdq_remove(cmdq);
}
log_debug("%s %s: exit (empty)", __func__, name);
return (items);
waiting:
log_debug("%s %s: exit (wait)", __func__, name);
return (items);
}
/* Print a guard line. */
void
cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags)
{
struct client *c = cmdq->client;
if (c == NULL || !(c->flags & CLIENT_CONTROL))
return;
evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
(long)cmdq->time, cmdq->number, flags);
server_client_push_stdout(c);
}
/* Show message from command. */
@ -140,175 +409,3 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...)
free(msg);
}
/* Print a guard line. */
void
cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags)
{
struct client *c = cmdq->client;
if (c == NULL || !(c->flags & CLIENT_CONTROL))
return;
evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
(long) cmdq->time, cmdq->number, flags);
server_client_push_stdout(c);
}
/* Add command list to queue and begin processing if needed. */
void
cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m)
{
cmdq_append(cmdq, cmdlist, m);
if (cmdq->item == NULL) {
cmdq->cmd = NULL;
cmdq_continue(cmdq);
}
}
/* Add command list to queue. */
void
cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m)
{
struct cmd_q_item *item;
item = xcalloc(1, sizeof *item);
item->cmdlist = cmdlist;
TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry);
cmdlist->references++;
if (m != NULL)
memcpy(&item->mouse, m, sizeof item->mouse);
else
item->mouse.valid = 0;
}
/* Process one command. */
static enum cmd_retval
cmdq_continue_one(struct cmd_q *cmdq)
{
struct cmd_list *cmdlist = cmdq->item->cmdlist;
struct cmd *cmd = cmdq->cmd;
enum cmd_retval retval;
char *tmp;
int flags = !!(cmd->flags & CMD_CONTROL);
const char *name;
struct cmd_find_state *fsp, fs;
cmdlist->references++;
tmp = cmd_print(cmd);
log_debug("cmdq %p: %s", cmdq, tmp);
free(tmp);
cmdq->time = time(NULL);
cmdq->number++;
cmdq_guard(cmdq, "begin", flags);
if (cmd_prepare_state(cmd, cmdq, cmdq->parent) != 0)
goto error;
retval = cmd->entry->exec(cmd, cmdq);
if (retval == CMD_RETURN_ERROR)
goto error;
if (~cmd->entry->flags & CMD_AFTERHOOK)
goto end;
if (cmd_find_valid_state(&cmdq->state.tflag))
fsp = &cmdq->state.tflag;
else {
if (cmd_find_current(&fs, cmdq, CMD_FIND_QUIET) != 0)
goto end;
fsp = &fs;
}
name = cmd->entry->name;
if (hooks_wait(fsp->s->hooks, cmdq, fsp, "after-%s", name) == 0)
retval = CMD_RETURN_WAIT;
end:
cmdq_guard(cmdq, "end", flags);
cmd_list_free(cmdlist);
return (retval);
error:
cmdq_guard(cmdq, "error", flags);
cmd_list_free(cmdlist);
return (CMD_RETURN_ERROR);
}
/* Continue processing command queue. Returns 1 if finishes empty. */
int
cmdq_continue(struct cmd_q *cmdq)
{
struct client *c = cmdq->client;
struct cmd_q_item *next;
enum cmd_retval retval;
int empty;
log_debug("continuing cmdq %p: flags %#x (%p)", cmdq, cmdq->flags, c);
cmdq->references++;
empty = TAILQ_EMPTY(&cmdq->queue);
if (empty)
goto empty;
if (cmdq->item == NULL) {
cmdq->item = TAILQ_FIRST(&cmdq->queue);
cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list);
} else
cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry);
do {
while (cmdq->cmd != NULL) {
retval = cmdq_continue_one(cmdq);
if (retval == CMD_RETURN_ERROR)
break;
if (retval == CMD_RETURN_WAIT)
goto out;
if (retval == CMD_RETURN_STOP) {
cmdq_flush(cmdq);
goto empty;
}
cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry);
}
next = TAILQ_NEXT(cmdq->item, qentry);
TAILQ_REMOVE(&cmdq->queue, cmdq->item, qentry);
cmd_list_free(cmdq->item->cmdlist);
free(cmdq->item);
cmdq->item = next;
if (cmdq->item != NULL)
cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list);
} while (cmdq->item != NULL);
empty:
log_debug("cmdq %p empty", cmdq);
if (cmdq->client_exit > 0)
cmdq->client->flags |= CLIENT_EXIT;
if (cmdq->emptyfn != NULL)
cmdq->emptyfn(cmdq);
empty = 1;
out:
cmdq_free(cmdq);
return (empty);
}
/* Flush command queue. */
static void
cmdq_flush(struct cmd_q *cmdq)
{
struct cmd_q_item *item, *item1;
TAILQ_FOREACH_SAFE(item, &cmdq->queue, qentry, item1) {
TAILQ_REMOVE(&cmdq->queue, item, qentry);
cmd_list_free(item->cmdlist);
free(item);
}
cmdq->item = NULL;
}

View File

@ -60,12 +60,12 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq)
int x, y;
if (args_has(args, 'M')) {
if (cmd_mouse_window(&cmdq->item->mouse, &s) == NULL)
if (cmd_mouse_window(&cmdq->mouse, &s) == NULL)
return (CMD_RETURN_NORMAL);
if (c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update;
cmd_resize_pane_mouse_update(c, &cmdq->item->mouse);
cmd_resize_pane_mouse_update(c, &cmdq->mouse);
return (CMD_RETURN_NORMAL);
}

View File

@ -51,7 +51,6 @@ const struct cmd_entry cmd_run_shell_entry = {
struct cmd_run_shell_data {
char *cmd;
struct cmd_q *cmdq;
int bflag;
int wp_id;
};
@ -92,6 +91,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq)
cwd = s->cwd;
else
cwd = NULL;
ft = format_create(cmdq, 0);
format_defaults(ft, cmdq->state.c, s, wl, wp);
shellcmd = format_expand(ft, args->argv[0]);
@ -99,20 +99,24 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq)
cdata = xcalloc(1, sizeof *cdata);
cdata->cmd = shellcmd;
cdata->bflag = args_has(args, 'b');
if (args_has(args, 't') && wp != NULL)
cdata->wp_id = wp->id;
else
cdata->wp_id = -1;
cdata->cmdq = cmdq;
cmdq->references++;
if (args_has(args, 't') && wp != NULL)
cdata->wp_id = wp->id;
else
cdata->wp_id = -1;
if (!args_has(args, 'b'))
cdata->cmdq = cmdq;
job_run(shellcmd, s, cwd, cmd_run_shell_callback, cmd_run_shell_free,
cdata);
if (cdata->bflag)
if (args_has(args, 'b'))
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
@ -121,16 +125,11 @@ static void
cmd_run_shell_callback(struct job *job)
{
struct cmd_run_shell_data *cdata = job->data;
struct cmd_q *cmdq = cdata->cmdq;
char *cmd, *msg, *line;
char *cmd = cdata->cmd, *msg, *line;
size_t size;
int retcode;
u_int lines;
if (cmdq->flags & CMD_Q_DEAD)
return;
cmd = cdata->cmd;
lines = 0;
do {
if ((line = evbuffer_readline(job->event->input)) != NULL) {
@ -163,16 +162,15 @@ cmd_run_shell_callback(struct job *job)
if (msg != NULL)
cmd_run_shell_print(job, msg);
free(msg);
if (cdata->cmdq != NULL)
cdata->cmdq->flags &= ~CMD_Q_WAITING;
}
static void
cmd_run_shell_free(void *data)
{
struct cmd_run_shell_data *cdata = data;
struct cmd_q *cmdq = cdata->cmdq;
if (!cmdq_free(cmdq) && !cdata->bflag)
cmdq_continue(cmdq);
free(cdata->cmd);
free(cdata);

View File

@ -62,7 +62,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq)
struct client *c = cmdq->state.c;
struct window_pane *wp = cmdq->state.tflag.wp;
struct session *s = cmdq->state.tflag.s;
struct mouse_event *m = &cmdq->item->mouse;
struct mouse_event *m = &cmdq->mouse;
const u_char *keystr;
int i, literal;
key_code key;

View File

@ -28,7 +28,7 @@
static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmd_q *);
static void cmd_source_file_done(struct cmd_q *);
static enum cmd_retval cmd_source_file_done(struct cmd_q *, void *);
const struct cmd_entry cmd_source_file_entry = {
.name = "source-file",
@ -45,53 +45,31 @@ static enum cmd_retval
cmd_source_file_exec(struct cmd *self, struct cmd_q *cmdq)
{
struct args *args = self->args;
struct cmd_q *cmdq1;
struct client *c = cmdq->client;
int quiet;
cmdq1 = cmdq_new(cmdq->client);
cmdq1->emptyfn = cmd_source_file_done;
cmdq1->data = cmdq;
struct cmd_q *new_cmdq;
quiet = args_has(args, 'q');
switch (load_cfg(args->argv[0], cmdq1, quiet)) {
switch (load_cfg(args->argv[0], c, cmdq, quiet)) {
case -1:
cmdq_free(cmdq1);
if (cfg_references == 0) {
if (cfg_finished)
cfg_print_causes(cmdq);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_ERROR);
case 0:
cmdq_free(cmdq1);
if (cfg_references == 0)
if (cfg_finished)
cfg_print_causes(cmdq);
return (CMD_RETURN_NORMAL);
}
log_debug("%s: cmdq %p, parent %p", __func__, cmdq1, cmdq);
cmdq->references++;
cfg_references++;
cmdq_continue(cmdq1);
return (CMD_RETURN_WAIT);
if (cfg_finished) {
new_cmdq = cmdq_get_callback(cmd_source_file_done, NULL);
cmdq_insert_after(cmdq, new_cmdq);
}
return (CMD_RETURN_NORMAL);
}
static void
cmd_source_file_done(struct cmd_q *cmdq1)
static enum cmd_retval
cmd_source_file_done(struct cmd_q *cmdq, __unused void *data)
{
struct cmd_q *cmdq = cmdq1->data;
log_debug("%s: cmdq %p, parent %p", __func__, cmdq1, cmdq);
if (cmdq1->client_exit >= 0)
cmdq->client_exit = cmdq1->client_exit;
cmdq_free(cmdq1);
cfg_references--;
if (cmdq_free(cmdq))
return;
if (cfg_references == 0)
cfg_print_causes(cmdq);
cmdq_continue(cmdq);
cfg_print_causes(cmdq);
return (CMD_RETURN_NORMAL);
}

View File

@ -188,8 +188,8 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq)
fs.w = w;
fs.wp = new_wp;
cmd_find_log_state(__func__, &fs);
if (hooks_wait(s->hooks, cmdq, &fs, "after-split-window") == 0)
return (CMD_RETURN_WAIT);
hooks_insert(s->hooks, cmdq, &fs, "after-split-window");
return (CMD_RETURN_NORMAL);
error:

View File

@ -41,13 +41,18 @@ const struct cmd_entry cmd_wait_for_entry = {
.exec = cmd_wait_for_exec
};
struct wait_item {
struct cmd_q *cmdq;
TAILQ_ENTRY(wait_item) entry;
};
struct wait_channel {
const char *name;
int locked;
int woken;
TAILQ_HEAD(, cmd_q) waiters;
TAILQ_HEAD(, cmd_q) lockers;
TAILQ_HEAD(, wait_item) waiters;
TAILQ_HEAD(, wait_item) lockers;
RB_ENTRY(wait_channel) entry;
};
@ -135,7 +140,7 @@ static enum cmd_retval
cmd_wait_for_signal(__unused struct cmd_q *cmdq, const char *name,
struct wait_channel *wc)
{
struct cmd_q *wq, *wq1;
struct wait_item *wi, *wi1;
if (wc == NULL)
wc = cmd_wait_for_add(name);
@ -147,10 +152,11 @@ cmd_wait_for_signal(__unused struct cmd_q *cmdq, const char *name,
}
log_debug("signal wait channel %s, with waiters", wc->name);
TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) {
TAILQ_REMOVE(&wc->waiters, wq, waitentry);
if (!cmdq_free(wq))
cmdq_continue(wq);
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
wi->cmdq->flags &= ~CMD_Q_WAITING;
TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi);
}
cmd_wait_for_remove(wc);
@ -158,10 +164,10 @@ cmd_wait_for_signal(__unused struct cmd_q *cmdq, const char *name,
}
static enum cmd_retval
cmd_wait_for_wait(struct cmd_q *cmdq, const char *name,
struct wait_channel *wc)
cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, struct wait_channel *wc)
{
struct client *c = cmdq->client;
struct client *c = cmdq->client;
struct wait_item *wi;
if (c == NULL || c->session != NULL) {
cmdq_error(cmdq, "not able to wait");
@ -178,16 +184,18 @@ cmd_wait_for_wait(struct cmd_q *cmdq, const char *name,
}
log_debug("wait channel %s not woken (%p)", wc->name, c);
TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry);
cmdq->references++;
wi = xcalloc(1, sizeof *wi);
wi->cmdq = cmdq;
TAILQ_INSERT_TAIL(&wc->waiters, wi, entry);
return (CMD_RETURN_WAIT);
}
static enum cmd_retval
cmd_wait_for_lock(struct cmd_q *cmdq, const char *name,
struct wait_channel *wc)
cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, struct wait_channel *wc)
{
struct wait_item *wi;
if (cmdq->client == NULL || cmdq->client->session != NULL) {
cmdq_error(cmdq, "not able to lock");
return (CMD_RETURN_ERROR);
@ -197,8 +205,9 @@ cmd_wait_for_lock(struct cmd_q *cmdq, const char *name,
wc = cmd_wait_for_add(name);
if (wc->locked) {
TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry);
cmdq->references++;
wi = xcalloc(1, sizeof *wi);
wi->cmdq = cmdq;
TAILQ_INSERT_TAIL(&wc->lockers, wi, entry);
return (CMD_RETURN_WAIT);
}
wc->locked = 1;
@ -210,17 +219,17 @@ static enum cmd_retval
cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name,
struct wait_channel *wc)
{
struct cmd_q *wq;
struct wait_item *wi;
if (wc == NULL || !wc->locked) {
cmdq_error(cmdq, "channel %s not locked", name);
return (CMD_RETURN_ERROR);
}
if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) {
TAILQ_REMOVE(&wc->lockers, wq, waitentry);
if (!cmdq_free(wq))
cmdq_continue(wq);
if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) {
wi->cmdq->flags &= ~CMD_Q_WAITING;
TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi);
} else {
wc->locked = 0;
cmd_wait_for_remove(wc);
@ -233,19 +242,19 @@ void
cmd_wait_for_flush(void)
{
struct wait_channel *wc, *wc1;
struct cmd_q *wq, *wq1;
struct wait_item *wi, *wi1;
RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) {
TAILQ_REMOVE(&wc->waiters, wq, waitentry);
if (!cmdq_free(wq))
cmdq_continue(wq);
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
wi->cmdq->flags &= ~CMD_Q_WAITING;
TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi);
}
wc->woken = 1;
TAILQ_FOREACH_SAFE(wq, &wc->lockers, waitentry, wq1) {
TAILQ_REMOVE(&wc->lockers, wq, waitentry);
if (!cmdq_free(wq))
cmdq_continue(wq);
TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
wi->cmdq->flags &= ~CMD_Q_WAITING;
TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi);
}
wc->locked = 0;
cmd_wait_for_remove(wc);

51
cmd.c
View File

@ -390,12 +390,11 @@ usage:
static int
cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag,
struct cmd_q *cmdq, struct cmd_q *parent)
struct cmd_q *cmdq)
{
int targetflags, error;
struct cmd_find_state *fs = NULL;
struct cmd_find_state *current = NULL;
struct cmd_find_state tmp;
struct cmd_find_state current;
if (flag == CMD_NONE ||
flag == CMD_CLIENT ||
@ -449,21 +448,12 @@ cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag,
default:
fatalx("unknown %cflag %d", c, flag);
}
log_debug("%s: flag %c %d %#x", __func__, c, flag, targetflags);
if (parent != NULL) {
if (c == 't')
current = &parent->state.tflag;
else if (c == 's')
current = &parent->state.sflag;
}
if (current == NULL || !cmd_find_valid_state(current)) {
error = cmd_find_current(&tmp, cmdq, targetflags);
if (error != 0 && ~targetflags & CMD_FIND_QUIET)
return (-1);
current = &tmp;
}
if (!cmd_find_empty_state(current) && !cmd_find_valid_state(current))
error = cmd_find_current(&current, cmdq, targetflags);
if (error != 0 && ~targetflags & CMD_FIND_QUIET)
return (-1);
if (!cmd_find_empty_state(&current) && !cmd_find_valid_state(&current))
fatalx("invalid current state");
switch (flag) {
@ -475,13 +465,13 @@ cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag,
case CMD_SESSION_CANFAIL:
case CMD_SESSION_PREFERUNATTACHED:
case CMD_SESSION_WITHPANE:
error = cmd_find_target(fs, current, cmdq, target,
error = cmd_find_target(fs, &current, cmdq, target,
CMD_FIND_SESSION, targetflags);
if (error != 0 && ~targetflags & CMD_FIND_QUIET)
return (-1);
break;
case CMD_MOVEW_R:
error = cmd_find_target(fs, current, cmdq, target,
error = cmd_find_target(fs, &current, cmdq, target,
CMD_FIND_SESSION, CMD_FIND_QUIET);
if (error == 0)
break;
@ -490,7 +480,7 @@ cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag,
case CMD_WINDOW_CANFAIL:
case CMD_WINDOW_MARKED:
case CMD_WINDOW_INDEX:
error = cmd_find_target(fs, current, cmdq, target,
error = cmd_find_target(fs, &current, cmdq, target,
CMD_FIND_WINDOW, targetflags);
if (error != 0 && ~targetflags & CMD_FIND_QUIET)
return (-1);
@ -498,7 +488,7 @@ cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag,
case CMD_PANE:
case CMD_PANE_CANFAIL:
case CMD_PANE_MARKED:
error = cmd_find_target(fs, current, cmdq, target,
error = cmd_find_target(fs, &current, cmdq, target,
CMD_FIND_PANE, targetflags);
if (error != 0 && ~targetflags & CMD_FIND_QUIET)
return (-1);
@ -510,14 +500,14 @@ cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag,
}
int
cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq, struct cmd_q *parent)
cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq)
{
const struct cmd_entry *entry = cmd->entry;
struct cmd_state *state = &cmdq->state;
char *tmp;
enum cmd_entry_flag flag;
const char *s;
int error;
const struct cmd_entry *entry = cmd->entry;
struct cmd_state *state = &cmdq->state;
char *tmp;
enum cmd_entry_flag flag;
const char *s;
int error;
tmp = cmd_print(cmd);
log_debug("preparing state for %s (client %p)", tmp, cmdq->client);
@ -546,18 +536,19 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq, struct cmd_q *parent)
state->c = cmd_find_client(cmdq, s, 1);
break;
}
log_debug("using client %p", state->c);
s = args_get(cmd->args, 't');
log_debug("preparing -t state: target %s", s == NULL ? "none" : s);
error = cmd_prepare_state_flag('t', s, entry->tflag, cmdq, parent);
error = cmd_prepare_state_flag('t', s, entry->tflag, cmdq);
if (error != 0)
return (error);
s = args_get(cmd->args, 's');
log_debug("preparing -s state: target %s", s == NULL ? "none" : s);
error = cmd_prepare_state_flag('s', s, entry->sflag, cmdq, parent);
error = cmd_prepare_state_flag('s', s, entry->sflag, cmdq);
if (error != 0)
return (error);

View File

@ -49,6 +49,21 @@ control_write_buffer(struct client *c, struct evbuffer *buffer)
server_client_push_stdout(c);
}
/* Control error callback. */
static enum cmd_retval
control_error(struct cmd_q *cmdq, void *data)
{
struct client *c = cmdq->client;
char *error = data;
cmdq_guard(cmdq, "begin", 1);
control_write(c, "parse error: %s", error);
cmdq_guard(cmdq, "error", 1);
free(error);
return (CMD_RETURN_NORMAL);
}
/* Control input callback. Read lines and fire commands. */
void
control_callback(struct client *c, int closed, __unused void *data)
@ -56,6 +71,7 @@ control_callback(struct client *c, int closed, __unused void *data)
char *line, *cause;
struct cmd_list *cmdlist;
struct cmd *cmd;
struct cmd_q *cmdq;
if (closed)
c->flags |= CLIENT_EXIT;
@ -70,18 +86,13 @@ control_callback(struct client *c, int closed, __unused void *data)
}
if (cmd_string_parse(line, &cmdlist, NULL, 0, &cause) != 0) {
c->cmdq->time = time(NULL);
c->cmdq->number++;
cmdq_guard(c->cmdq, "begin", 1);
control_write(c, "parse error: %s", cause);
cmdq_guard(c->cmdq, "error", 1);
free(cause);
cmdq = cmdq_get_callback(control_error, cause);
cmdq_append(c, cmdq);
} else {
TAILQ_FOREACH(cmd, &cmdlist->list, qentry)
cmd->flags |= CMD_CONTROL;
cmdq_run(c->cmdq, cmdlist, NULL);
cmdq = cmdq_get_command(cmdlist, NULL, NULL, 0);
cmdq_append(c, cmdq);
cmd_list_free(cmdlist);
}

View File

@ -473,7 +473,6 @@ struct format_tree *
format_create(struct cmd_q *cmdq, int flags)
{
struct format_tree *ft;
struct cmd *cmd;
if (!event_initialized(&format_job_event)) {
evtimer_set(&format_job_event, format_job_timer, NULL);
@ -491,11 +490,9 @@ format_create(struct cmd_q *cmdq, int flags)
format_add_tv(ft, "start_time", &start_time);
if (cmdq != NULL && cmdq->cmd != NULL)
format_add(ft, "command_name", "%s", cmdq->cmd->entry->name);
if (cmdq != NULL && cmdq->parent != NULL) {
cmd = cmdq->parent->cmd;
format_add(ft, "command_hooked", "%s", cmd->entry->name);
}
format_add(ft, "command", "%s", cmdq->cmd->entry->name);
if (cmdq != NULL && cmdq->hook != NULL)
format_add(ft, "hook", "%s", cmdq->hook);
return (ft);
}

68
hooks.c
View File

@ -33,7 +33,6 @@ RB_GENERATE_STATIC(hooks_tree, hook, entry, hooks_cmp);
static struct hook *hooks_find1(struct hooks *, const char *);
static void hooks_free1(struct hooks *, struct hook *);
static void hooks_emptyfn(struct cmd_q *);
static int
hooks_cmp(struct hook *hook1, struct hook *hook2)
@ -140,28 +139,14 @@ hooks_find(struct hooks *hooks, const char *name)
return (hook);
}
static void
hooks_emptyfn(struct cmd_q *hooks_cmdq)
{
struct cmd_q *cmdq = hooks_cmdq->data;
if (cmdq != NULL) {
if (hooks_cmdq->client_exit >= 0)
cmdq->client_exit = hooks_cmdq->client_exit;
if (!cmdq_free(cmdq))
cmdq_continue(cmdq);
}
cmdq_free(hooks_cmdq);
}
int
void
hooks_run(struct hooks *hooks, struct client *c, struct cmd_find_state *fs,
const char *fmt, ...)
{
struct hook *hook;
struct cmd_q *hooks_cmdq;
va_list ap;
char *name;
struct cmd_q *new_cmdq, *loop;
va_start(ap, fmt);
xvasprintf(&name, fmt, ap);
@ -170,34 +155,30 @@ hooks_run(struct hooks *hooks, struct client *c, struct cmd_find_state *fs,
hook = hooks_find(hooks, name);
if (hook == NULL) {
free(name);
return (-1);
return;
}
log_debug("running hook %s", name);
new_cmdq = cmdq_get_command(hook->cmdlist, fs, NULL, CMD_Q_NOHOOKS);
for (loop = new_cmdq; loop != NULL; loop = loop->next)
loop->hook = xstrdup(name);
free(name);
hooks_cmdq = cmdq_new(c);
hooks_cmdq->flags |= CMD_Q_NOHOOKS;
if (fs != NULL)
cmd_find_copy_state(&hooks_cmdq->current, fs);
hooks_cmdq->parent = NULL;
cmdq_run(hooks_cmdq, hook->cmdlist, NULL);
cmdq_free(hooks_cmdq);
return (0);
cmdq_append(c, new_cmdq);
}
int
hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, struct cmd_find_state *fs,
void
hooks_insert(struct hooks *hooks, struct cmd_q *cmdq, struct cmd_find_state *fs,
const char *fmt, ...)
{
struct hook *hook;
struct cmd_q *hooks_cmdq;
va_list ap;
char *name;
struct cmd_q *new_cmdq, *loop;
if (cmdq->flags & CMD_Q_NOHOOKS)
return (-1);
return;
va_start(ap, fmt);
xvasprintf(&name, fmt, ap);
@ -206,23 +187,18 @@ hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, struct cmd_find_state *fs,
hook = hooks_find(hooks, name);
if (hook == NULL) {
free(name);
return (-1);
return;
}
log_debug("running hook %s (parent %p)", name, cmdq);
new_cmdq = cmdq_get_command(hook->cmdlist, fs, NULL, CMD_Q_NOHOOKS);
for (loop = new_cmdq; loop != NULL; loop = loop->next)
loop->hook = xstrdup(name);
free(name);
hooks_cmdq = cmdq_new(cmdq->client);
hooks_cmdq->flags |= CMD_Q_NOHOOKS;
if (fs != NULL)
cmd_find_copy_state(&hooks_cmdq->current, fs);
hooks_cmdq->parent = cmdq;
hooks_cmdq->emptyfn = hooks_emptyfn;
hooks_cmdq->data = cmdq;
if (cmdq != NULL)
cmdq->references++;
cmdq_run(hooks_cmdq, hook->cmdlist, NULL);
return (0);
cmdq_insert_after(cmdq, new_cmdq);
else
cmdq_append(NULL, new_cmdq);
}

View File

@ -377,18 +377,22 @@ key_bindings_init(void)
struct cmd_list *cmdlist;
char *cause;
int error;
struct cmd_q *cmdq;
cmdq = cmdq_new(NULL);
for (i = 0; i < nitems(defaults); i++) {
error = cmd_string_parse(defaults[i], &cmdlist,
"<default-keys>", i, &cause);
if (error != 0)
fatalx("bad default key");
cmdq_run(cmdq, cmdlist, NULL);
cmdq_append(NULL, cmdq_get_command(cmdlist, NULL, NULL, 0));
cmd_list_free(cmdlist);
}
cmdq_free(cmdq);
}
static enum cmd_retval
key_bindings_read_only(struct cmd_q *cmdq, __unused void *data)
{
cmdq_error(cmdq, "client is read-only");
return (CMD_RETURN_ERROR);
}
void
@ -403,10 +407,8 @@ key_bindings_dispatch(struct key_binding *bd, struct client *c,
if (!(cmd->entry->flags & CMD_READONLY))
readonly = 0;
}
if (!readonly && (c->flags & CLIENT_READONLY)) {
cmdq_error(c->cmdq, "client is read-only");
return;
}
cmdq_run(c->cmdq, bd->cmdlist, m);
if (!readonly && (c->flags & CLIENT_READONLY))
cmdq_append(c, cmdq_get_callback(key_bindings_read_only, NULL));
else
cmdq_append(c, cmdq_get_command(bd->cmdlist, NULL, m, 0));
}

View File

@ -66,7 +66,7 @@ notify_hook(struct notify_entry *ne)
const char *name;
struct cmd_find_state fs;
struct hook *hook;
struct cmd_q *hooks_cmdq;
struct cmd_q *new_cmdq, *loop;
name = notify_hooks[ne->type];
if (name == NULL)
@ -87,14 +87,12 @@ notify_hook(struct notify_entry *ne)
return;
log_debug("notify hook %s", name);
hooks_cmdq = cmdq_new(NULL);
hooks_cmdq->flags |= CMD_Q_NOHOOKS;
new_cmdq = cmdq_get_command(hook->cmdlist, &fs, NULL, CMD_Q_NOHOOKS);
cmd_find_copy_state(&hooks_cmdq->current, &fs);
hooks_cmdq->parent = NULL;
for (loop = new_cmdq; loop != NULL; loop = loop->next)
loop->hook = xstrdup(name);
cmdq_run(hooks_cmdq, hook->cmdlist, NULL);
cmdq_free(hooks_cmdq);
cmdq_append(NULL, new_cmdq);
}
static void

View File

@ -126,8 +126,7 @@ server_client_create(int fd)
c->fd = -1;
c->cwd = NULL;
c->cmdq = cmdq_new(c);
c->cmdq->client_exit = 1;
TAILQ_INIT(&c->queue);
c->stdin_data = evbuffer_new();
c->stdout_data = evbuffer_new();
@ -244,10 +243,6 @@ server_client_lost(struct client *c)
free(c->prompt_string);
free(c->prompt_buffer);
c->cmdq->flags |= CMD_Q_DEAD;
cmdq_free(c->cmdq);
c->cmdq = NULL;
environ_free(c->environ);
proc_remove_peer(c->peer);
@ -281,6 +276,9 @@ server_client_free(__unused int fd, __unused short events, void *arg)
log_debug("free client %p (%d references)", c, c->references);
if (!TAILQ_EMPTY(&c->queue))
fatalx("queue not empty");
if (c->references == 0)
free(c);
}
@ -1254,6 +1252,29 @@ server_client_dispatch(struct imsg *imsg, void *arg)
}
}
/* Callback when command is done. */
static enum cmd_retval
server_client_command_done(struct cmd_q *cmdq, __unused void *data)
{
struct client *c = cmdq->client;
if (~c->flags & CLIENT_ATTACHED)
c->flags |= CLIENT_EXIT;
return (CMD_RETURN_NORMAL);
}
/* Show an error message. */
static enum cmd_retval
server_client_command_error(struct cmd_q *cmdq, void *data)
{
char *error = data;
cmdq_error(cmdq, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
/* Handle command message. */
static void
server_client_dispatch_command(struct client *c, struct imsg *imsg)
@ -1276,7 +1297,7 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
argc = data.argc;
if (cmd_unpack_argv(buf, len, argc, &argv) != 0) {
cmdq_error(c->cmdq, "command too long");
cause = xstrdup("command too long");
goto error;
}
@ -1287,20 +1308,19 @@ server_client_dispatch_command(struct client *c, struct imsg *imsg)
}
if ((cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause)) == NULL) {
cmdq_error(c->cmdq, "%s", cause);
cmd_free_argv(argc, argv);
goto error;
}
cmd_free_argv(argc, argv);
if (c != cfg_client || cfg_finished)
cmdq_run(c->cmdq, cmdlist, NULL);
else
cmdq_append(c->cmdq, cmdlist, NULL);
cmdq_append(c, cmdq_get_command(cmdlist, NULL, NULL, 0));
cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL));
cmd_list_free(cmdlist);
return;
error:
cmdq_append(c, cmdq_get_callback(server_client_command_error, cause));
if (cmdlist != NULL)
cmd_list_free(cmdlist);

View File

@ -190,9 +190,17 @@ static int
server_loop(void)
{
struct client *c;
u_int items;
notify_drain();
do {
items = cmdq_next(NULL);
TAILQ_FOREACH(c, &clients, entry)
items += cmdq_next(c);
} while (items != 0);
server_client_loop();
notify_drain();
if (!options_get_number(global_options, "exit-unattached")) {
if (!RB_EMPTY(&sessions))

4
tmux.1
View File

@ -3456,8 +3456,7 @@ The following variables are available, where appropriate:
.It Li "client_tty" Ta "" Ta "Pseudo terminal of client"
.It Li "client_utf8" Ta "" Ta "1 if client supports utf8"
.It Li "client_width" Ta "" Ta "Width of client"
.It Li "command_hooked" Ta "" Ta "Name of command hooked, if any"
.It Li "command_name" Ta "" Ta "Name of command in use, if any"
.It Li "command" Ta "" Ta "Name of command in use, if any"
.It Li "command_list_name" Ta "" Ta "Command name if listing commands"
.It Li "command_list_alias" Ta "" Ta "Command alias if listing commands"
.It Li "command_list_usage" Ta "" Ta "Command usage if listing commands"
@ -3467,6 +3466,7 @@ The following variables are available, where appropriate:
.It Li "history_bytes" Ta "" Ta "Number of bytes in window history"
.It Li "history_limit" Ta "" Ta "Maximum window history lines"
.It Li "history_size" Ta "" Ta "Size of history in bytes"
.It Li "hook" Ta "" Ta "Name of running hook, if any"
.It Li "host" Ta "#H" Ta "Hostname of local host"
.It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)"
.It Li "insert_flag" Ta "" Ta "Pane insert flag"

268
tmux.h
View File

@ -40,6 +40,8 @@ extern char **environ;
struct args;
struct client;
struct cmd_q;
struct cmd_q_list;
struct environ;
struct input_ctx;
struct mode_key_cmdstr;
@ -631,7 +633,6 @@ struct grid {
struct hook {
const char *name;
struct cmd_q *cmdq;
struct cmd_list *cmdlist;
RB_ENTRY(hook) entry;
@ -1160,101 +1161,6 @@ struct message_entry {
TAILQ_ENTRY(message_entry) entry;
};
/* Client connection. */
struct client {
struct tmuxpeer *peer;
pid_t pid;
int fd;
struct event event;
int retval;
struct timeval creation_time;
struct timeval activity_time;
struct environ *environ;
char *title;
const char *cwd;
char *term;
char *ttyname;
struct tty tty;
void (*stdin_callback)(struct client *, int, void *);
void *stdin_callback_data;
struct evbuffer *stdin_data;
int stdin_closed;
struct evbuffer *stdout_data;
struct evbuffer *stderr_data;
struct event repeat_timer;
struct event click_timer;
u_int click_button;
struct event status_timer;
struct screen status;
#define CLIENT_TERMINAL 0x1
#define CLIENT_LOGIN 0x2
#define CLIENT_EXIT 0x4
#define CLIENT_REDRAW 0x8
#define CLIENT_STATUS 0x10
#define CLIENT_REPEAT 0x20
#define CLIENT_SUSPENDED 0x40
/* 0x80 unused */
#define CLIENT_IDENTIFY 0x100
#define CLIENT_DEAD 0x200
#define CLIENT_BORDERS 0x400
#define CLIENT_READONLY 0x800
#define CLIENT_REDRAWWINDOW 0x1000
#define CLIENT_CONTROL 0x2000
#define CLIENT_CONTROLCONTROL 0x4000
#define CLIENT_FOCUSED 0x8000
#define CLIENT_UTF8 0x10000
#define CLIENT_256COLOURS 0x20000
#define CLIENT_IDENTIFIED 0x40000
#define CLIENT_STATUSFORCE 0x80000
#define CLIENT_DOUBLECLICK 0x100000
#define CLIENT_TRIPLECLICK 0x200000
int flags;
struct key_table *keytable;
struct event identify_timer;
void (*identify_callback)(struct client *, struct window_pane *);
void *identify_callback_data;
char *message_string;
struct event message_timer;
u_int message_next;
TAILQ_HEAD(, message_entry) message_log;
char *prompt_string;
struct utf8_data *prompt_buffer;
size_t prompt_index;
int (*prompt_callbackfn)(void *, const char *);
void (*prompt_freefn)(void *);
void *prompt_data;
u_int prompt_hindex;
enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode;
#define PROMPT_SINGLE 0x1
#define PROMPT_NUMERIC 0x2
int prompt_flags;
struct session *session;
struct session *last_session;
int wlmouse;
struct cmd_q *cmdq;
int references;
TAILQ_ENTRY(client) entry;
};
TAILQ_HEAD(clients, client);
/* Parsed arguments structures. */
struct args_entry;
RB_HEAD(args_tree, args_entry);
@ -1324,42 +1230,46 @@ enum cmd_retval {
CMD_RETURN_STOP
};
/* Command queue entry. */
struct cmd_q_item {
struct cmd_list *cmdlist;
struct mouse_event mouse;
TAILQ_ENTRY(cmd_q_item) qentry;
/* Command queue item type. */
enum cmd_q_type {
CMD_Q_COMMAND,
CMD_Q_CALLBACK,
};
TAILQ_HEAD(cmd_q_items, cmd_q_item);
/* Command queue. */
/* Command queue item. */
typedef enum cmd_retval (*cmd_q_cb) (struct cmd_q *, void *);
struct cmd_q {
int references;
int flags;
#define CMD_Q_DEAD 0x1
#define CMD_Q_NOHOOKS 0x2
struct cmd_q_list *queue;
struct cmd_q *next;
struct client *client;
int client_exit;
struct cmd_q_items queue;
struct cmd_q_item *item;
enum cmd_q_type type;
u_int group;
u_int number;
time_t time;
const char *hook;
int flags;
#define CMD_Q_FIRED 0x1
#define CMD_Q_WAITING 0x2
#define CMD_Q_NOHOOKS 0x4
struct cmd_list *cmdlist;
struct cmd *cmd;
struct cmd_q *parent;
cmd_q_cb cb;
void *data;
struct cmd_find_state current;
struct cmd_state state;
time_t time;
u_int number;
struct mouse_event mouse;
void (*emptyfn)(struct cmd_q *);
void *data;
TAILQ_ENTRY(cmd_q) waitentry;
TAILQ_ENTRY(cmd_q) entry;
};
TAILQ_HEAD(cmd_q_list, cmd_q);
/* Command -c, -t or -s flags. */
enum cmd_entry_flag {
@ -1409,6 +1319,101 @@ struct cmd_entry {
enum cmd_retval (*exec)(struct cmd *, struct cmd_q *);
};
/* Client connection. */
struct client {
struct tmuxpeer *peer;
struct cmd_q_list queue;
pid_t pid;
int fd;
struct event event;
int retval;
struct timeval creation_time;
struct timeval activity_time;
struct environ *environ;
char *title;
const char *cwd;
char *term;
char *ttyname;
struct tty tty;
void (*stdin_callback)(struct client *, int, void *);
void *stdin_callback_data;
struct evbuffer *stdin_data;
int stdin_closed;
struct evbuffer *stdout_data;
struct evbuffer *stderr_data;
struct event repeat_timer;
struct event click_timer;
u_int click_button;
struct event status_timer;
struct screen status;
#define CLIENT_TERMINAL 0x1
#define CLIENT_LOGIN 0x2
#define CLIENT_EXIT 0x4
#define CLIENT_REDRAW 0x8
#define CLIENT_STATUS 0x10
#define CLIENT_REPEAT 0x20
#define CLIENT_SUSPENDED 0x40
#define CLIENT_ATTACHED 0x80
#define CLIENT_IDENTIFY 0x100
#define CLIENT_DEAD 0x200
#define CLIENT_BORDERS 0x400
#define CLIENT_READONLY 0x800
#define CLIENT_REDRAWWINDOW 0x1000
#define CLIENT_CONTROL 0x2000
#define CLIENT_CONTROLCONTROL 0x4000
#define CLIENT_FOCUSED 0x8000
#define CLIENT_UTF8 0x10000
#define CLIENT_256COLOURS 0x20000
#define CLIENT_IDENTIFIED 0x40000
#define CLIENT_STATUSFORCE 0x80000
#define CLIENT_DOUBLECLICK 0x100000
#define CLIENT_TRIPLECLICK 0x200000
int flags;
struct key_table *keytable;
struct event identify_timer;
void (*identify_callback)(struct client *, struct window_pane *);
void *identify_callback_data;
char *message_string;
struct event message_timer;
u_int message_next;
TAILQ_HEAD(, message_entry) message_log;
char *prompt_string;
struct utf8_data *prompt_buffer;
size_t prompt_index;
int (*prompt_callbackfn)(void *, const char *);
void (*prompt_freefn)(void *);
void *prompt_data;
u_int prompt_hindex;
enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode;
#define PROMPT_SINGLE 0x1
#define PROMPT_NUMERIC 0x2
int prompt_flags;
struct session *session;
struct session *last_session;
int wlmouse;
int references;
TAILQ_ENTRY(client) entry;
};
TAILQ_HEAD(clients, client);
/* Key binding and key table. */
struct key_binding {
key_code key;
@ -1504,10 +1509,9 @@ void proc_kill_peer(struct tmuxpeer *);
/* cfg.c */
extern int cfg_finished;
extern int cfg_references;
extern struct client *cfg_client;
void start_cfg(void);
int load_cfg(const char *, struct cmd_q *, int);
int load_cfg(const char *, struct client *, struct cmd_q *, int);
void set_cfg_file(const char *);
void printflike(1, 2) cfg_add_cause(const char *, ...);
void cfg_print_causes(struct cmd_q *);
@ -1557,9 +1561,9 @@ void hooks_add(struct hooks *, const char *, struct cmd_list *);
void hooks_copy(struct hooks *, struct hooks *);
void hooks_remove(struct hooks *, const char *);
struct hook *hooks_find(struct hooks *, const char *);
int printflike(4, 5) hooks_run(struct hooks *, struct client *,
void printflike(4, 5) hooks_run(struct hooks *, struct client *,
struct cmd_find_state *, const char *, ...);
int printflike(4, 5) hooks_wait(struct hooks *, struct cmd_q *,
void printflike(4, 5) hooks_insert(struct hooks *, struct cmd_q *,
struct cmd_find_state *, const char *, ...);
/* mode-key.c */
@ -1755,8 +1759,7 @@ char **cmd_copy_argv(int, char **);
void cmd_free_argv(int, char **);
char *cmd_stringify_argv(int, char **);
struct cmd *cmd_parse(int, char **, const char *, u_int, char **);
int cmd_prepare_state(struct cmd *, struct cmd_q *,
struct cmd_q *);
int cmd_prepare_state(struct cmd *, struct cmd_q *);
char *cmd_print(struct cmd *);
int cmd_mouse_at(struct window_pane *, struct mouse_event *,
u_int *, u_int *, int);
@ -1776,16 +1779,15 @@ void cmd_list_free(struct cmd_list *);
char *cmd_list_print(struct cmd_list *);
/* cmd-queue.c */
struct cmd_q *cmdq_new(struct client *);
int cmdq_free(struct cmd_q *);
struct cmd_q *cmdq_get_command(struct cmd_list *, struct cmd_find_state *,
struct mouse_event *, int);
struct cmd_q *cmdq_get_callback(cmd_q_cb, void *);
void cmdq_insert_after(struct cmd_q *, struct cmd_q *);
void cmdq_append(struct client *, struct cmd_q *);
u_int cmdq_next(struct client *);
void cmdq_guard(struct cmd_q *, const char *, int);
void printflike(2, 3) cmdq_print(struct cmd_q *, const char *, ...);
void printflike(2, 3) cmdq_error(struct cmd_q *, const char *, ...);
void cmdq_guard(struct cmd_q *, const char *, int);
void cmdq_run(struct cmd_q *, struct cmd_list *,
struct mouse_event *);
void cmdq_append(struct cmd_q *, struct cmd_list *,
struct mouse_event *);
int cmdq_continue(struct cmd_q *);
/* cmd-string.c */
int cmd_string_parse(const char *, struct cmd_list **, const char *,

View File

@ -246,6 +246,7 @@ window_choose_data_run(struct window_choose_data *cdata)
{
struct cmd_list *cmdlist;
char *cause;
struct cmd_q *cmdq;
/*
* The command template will have already been replaced. But if it's
@ -263,7 +264,8 @@ window_choose_data_run(struct window_choose_data *cdata)
return;
}
cmdq_run(cdata->start_client->cmdq, cmdlist, NULL);
cmdq = cmdq_get_command(cmdlist, NULL, NULL, 0);
cmdq_append(cdata->start_client, cmdq);
cmd_list_free(cmdlist);
}