diff options
-rw-r--r-- | cfg.c | 70 | ||||
-rw-r--r-- | cmd-attach-session.c | 2 | ||||
-rw-r--r-- | cmd-command-prompt.c | 30 | ||||
-rw-r--r-- | cmd-confirm-before.c | 27 | ||||
-rw-r--r-- | cmd-copy-mode.c | 4 | ||||
-rw-r--r-- | cmd-display-panes.c | 50 | ||||
-rw-r--r-- | cmd-find.c | 2 | ||||
-rw-r--r-- | cmd-if-shell.c | 112 | ||||
-rw-r--r-- | cmd-load-buffer.c | 45 | ||||
-rw-r--r-- | cmd-new-session.c | 6 | ||||
-rw-r--r-- | cmd-new-window.c | 4 | ||||
-rw-r--r-- | cmd-queue.c | 489 | ||||
-rw-r--r-- | cmd-resize-pane.c | 4 | ||||
-rw-r--r-- | cmd-run-shell.c | 28 | ||||
-rw-r--r-- | cmd-send-keys.c | 2 | ||||
-rw-r--r-- | cmd-source-file.c | 54 | ||||
-rw-r--r-- | cmd-split-window.c | 4 | ||||
-rw-r--r-- | cmd-wait-for.c | 69 | ||||
-rw-r--r-- | cmd.c | 51 | ||||
-rw-r--r-- | control.c | 29 | ||||
-rw-r--r-- | format.c | 9 | ||||
-rw-r--r-- | hooks.c | 64 | ||||
-rw-r--r-- | key-bindings.c | 22 | ||||
-rw-r--r-- | notify.c | 12 | ||||
-rw-r--r-- | server-client.c | 44 | ||||
-rw-r--r-- | server.c | 10 | ||||
-rw-r--r-- | tmux.1 | 4 | ||||
-rw-r--r-- | tmux.h | 268 | ||||
-rw-r--r-- | window-choose.c | 4 |
29 files changed, 817 insertions, 702 deletions
@@ -28,14 +28,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) @@ -50,30 +61,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' }; @@ -81,6 +86,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) { @@ -116,8 +122,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); @@ -125,37 +136,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, ...) { diff --git a/cmd-attach-session.c b/cmd-attach-session.c index daab428f..f0e860f9 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -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); diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 12ecb493..5e21b2bc 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -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); } diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 4241aefb..2dd52c81 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -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); } diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 4591a37d..dc880d56 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -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); diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 2edb2eb3..471bec02 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -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 (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 (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) { + 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; @@ -1005,7 +1005,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); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 2a8cbff2..7192b204 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -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; - - if (cmd_string_parse(cmd, &cmdlist, cdata->file, cdata->line, - &cause) != 0) { - if (cause != NULL) { - cmdq_error(cmdq, "%s", cause); - free(cause); - } - return; + goto out; + + 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; - - 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); + if (new_cmdq != NULL) { + if (cdata->cmdq == NULL) + cmdq_append(c, new_cmdq); + else + cmdq_insert_after(cdata->cmdq, new_cmdq); + } - 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); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index ca886d69..ae071968 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -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); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 5924b793..e587e5ee 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -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: diff --git a/cmd-new-window.c b/cmd-new-window.c index 4d0e0057..af476913 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -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: diff --git a/cmd-queue.c b/cmd-queue.c index 2012e871..fca3bf04 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -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; + 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); +} - cmdq = xcalloc(1, sizeof *cmdq); - cmdq->references = 1; - cmdq->flags = 0; +/* 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; - cmdq->client_exit = -1; + if (retval == CMD_RETURN_ERROR) + cmdq_guard(cmdq, "error", flags); + else + cmdq_guard(cmdq, "end", flags); + return (retval); +} - TAILQ_INIT(&cmdq->queue); - cmdq->item = NULL; - cmdq->cmd = NULL; +/* 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->type = CMD_Q_CALLBACK; + cmdq->group = 0; + cmdq->flags = 0; - 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)); +} + +/* 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 (--cmdq->references != 0) { - if (cmdq->flags & CMD_Q_DEAD) - return (1); + 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__, na |