Annotation of src/usr.bin/tmux/cmd-queue.c, Revision 1.42
1.42 ! nicm 1: /* $OpenBSD: cmd-queue.c,v 1.41 2016/10/14 18:41:53 nicm Exp $ */
1.1 nicm 2:
3: /*
1.34 nicm 4: * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: #include <sys/types.h>
20:
21: #include <ctype.h>
22: #include <stdlib.h>
1.23 nicm 23: #include <string.h>
1.7 nicm 24: #include <time.h>
1.1 nicm 25:
26: #include "tmux.h"
27:
1.31 nicm 28: static enum cmd_retval cmdq_continue_one(struct cmd_q *);
1.38 nicm 29: static void cmdq_flush(struct cmd_q *);
1.24 nicm 30:
1.1 nicm 31: /* Create new command queue. */
32: struct cmd_q *
33: cmdq_new(struct client *c)
34: {
35: struct cmd_q *cmdq;
36:
37: cmdq = xcalloc(1, sizeof *cmdq);
38: cmdq->references = 1;
1.25 nicm 39: cmdq->flags = 0;
1.1 nicm 40:
41: cmdq->client = c;
1.11 nicm 42: cmdq->client_exit = -1;
1.1 nicm 43:
44: TAILQ_INIT(&cmdq->queue);
45: cmdq->item = NULL;
46: cmdq->cmd = NULL;
47:
1.33 nicm 48: cmd_find_clear_state(&cmdq->current, NULL, 0);
49: cmdq->parent = NULL;
50:
1.1 nicm 51: return (cmdq);
52: }
53:
54: /* Free command queue */
55: int
56: cmdq_free(struct cmd_q *cmdq)
57: {
1.41 nicm 58: log_debug("cmdq %p free: %u references", cmdq, cmdq->references);
59:
1.25 nicm 60: if (--cmdq->references != 0) {
61: if (cmdq->flags & CMD_Q_DEAD)
62: return (1);
63: return (0);
64: }
1.1 nicm 65:
66: cmdq_flush(cmdq);
67: free(cmdq);
68: return (1);
69: }
70:
71: /* Show message from command. */
1.18 nicm 72: void
1.1 nicm 73: cmdq_print(struct cmd_q *cmdq, const char *fmt, ...)
74: {
75: struct client *c = cmdq->client;
76: struct window *w;
77: va_list ap;
1.28 nicm 78: char *tmp, *msg;
1.1 nicm 79:
80: va_start(ap, fmt);
81:
82: if (c == NULL)
83: /* nothing */;
84: else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
1.28 nicm 85: if (~c->flags & CLIENT_UTF8) {
1.37 nicm 86: xvasprintf(&tmp, fmt, ap);
1.28 nicm 87: msg = utf8_sanitize(tmp);
88: free(tmp);
89: evbuffer_add(c->stdout_data, msg, strlen(msg));
90: free(msg);
91: } else
92: evbuffer_add_vprintf(c->stdout_data, fmt, ap);
1.1 nicm 93: evbuffer_add(c->stdout_data, "\n", 1);
1.29 nicm 94: server_client_push_stdout(c);
1.1 nicm 95: } else {
96: w = c->session->curw->window;
97: if (w->active->mode != &window_copy_mode) {
98: window_pane_reset_mode(w->active);
99: window_pane_set_mode(w->active, &window_copy_mode);
100: window_copy_init_for_output(w->active);
101: }
102: window_copy_vadd(w->active, fmt, ap);
103: }
104:
105: va_end(ap);
106: }
107:
108: /* Show error from command. */
1.18 nicm 109: void
1.1 nicm 110: cmdq_error(struct cmd_q *cmdq, const char *fmt, ...)
111: {
112: struct client *c = cmdq->client;
113: struct cmd *cmd = cmdq->cmd;
114: va_list ap;
1.20 nicm 115: char *msg;
1.1 nicm 116: size_t msglen;
1.28 nicm 117: char *tmp;
1.1 nicm 118:
119: va_start(ap, fmt);
120: msglen = xvasprintf(&msg, fmt, ap);
121: va_end(ap);
122:
1.20 nicm 123: if (c == NULL)
124: cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg);
125: else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
1.28 nicm 126: if (~c->flags & CLIENT_UTF8) {
127: tmp = msg;
128: msg = utf8_sanitize(tmp);
129: free(tmp);
130: msglen = strlen(msg);
131: }
1.1 nicm 132: evbuffer_add(c->stderr_data, msg, msglen);
133: evbuffer_add(c->stderr_data, "\n", 1);
1.29 nicm 134: server_client_push_stderr(c);
1.13 nicm 135: c->retval = 1;
1.1 nicm 136: } else {
137: *msg = toupper((u_char) *msg);
138: status_message_set(c, "%s", msg);
139: }
140:
141: free(msg);
142: }
143:
1.4 nicm 144: /* Print a guard line. */
1.21 nicm 145: void
1.10 nicm 146: cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags)
1.4 nicm 147: {
148: struct client *c = cmdq->client;
149:
1.21 nicm 150: if (c == NULL || !(c->flags & CLIENT_CONTROL))
151: return;
1.4 nicm 152:
1.9 nicm 153: evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
154: (long) cmdq->time, cmdq->number, flags);
1.29 nicm 155: server_client_push_stdout(c);
1.4 nicm 156: }
157:
1.1 nicm 158: /* Add command list to queue and begin processing if needed. */
159: void
1.23 nicm 160: cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m)
1.1 nicm 161: {
1.23 nicm 162: cmdq_append(cmdq, cmdlist, m);
1.1 nicm 163:
164: if (cmdq->item == NULL) {
165: cmdq->cmd = NULL;
166: cmdq_continue(cmdq);
167: }
168: }
169:
170: /* Add command list to queue. */
171: void
1.23 nicm 172: cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m)
1.1 nicm 173: {
174: struct cmd_q_item *item;
175:
176: item = xcalloc(1, sizeof *item);
177: item->cmdlist = cmdlist;
178: TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry);
179: cmdlist->references++;
1.23 nicm 180:
181: if (m != NULL)
182: memcpy(&item->mouse, m, sizeof item->mouse);
183: else
184: item->mouse.valid = 0;
1.1 nicm 185: }
186:
1.24 nicm 187: /* Process one command. */
1.31 nicm 188: static enum cmd_retval
1.24 nicm 189: cmdq_continue_one(struct cmd_q *cmdq)
190: {
1.41 nicm 191: struct cmd_list *cmdlist = cmdq->item->cmdlist;
1.40 nicm 192: struct cmd *cmd = cmdq->cmd;
193: enum cmd_retval retval;
194: char *tmp;
195: int flags = !!(cmd->flags & CMD_CONTROL);
196: const char *name;
197: struct cmd_find_state *fsp, fs;
1.24 nicm 198:
1.41 nicm 199: cmdlist->references++;
200:
1.31 nicm 201: tmp = cmd_print(cmd);
202: log_debug("cmdq %p: %s", cmdq, tmp);
203: free(tmp);
1.24 nicm 204:
205: cmdq->time = time(NULL);
206: cmdq->number++;
207:
1.40 nicm 208: cmdq_guard(cmdq, "begin", flags);
1.24 nicm 209:
1.36 nicm 210: if (cmd_prepare_state(cmd, cmdq, cmdq->parent) != 0)
1.31 nicm 211: goto error;
1.36 nicm 212:
1.24 nicm 213: retval = cmd->entry->exec(cmd, cmdq);
1.32 nicm 214: if (retval == CMD_RETURN_ERROR)
215: goto error;
1.24 nicm 216:
1.40 nicm 217: if (~cmd->entry->flags & CMD_AFTERHOOK)
218: goto end;
219:
220: if (cmd_find_valid_state(&cmdq->state.tflag))
221: fsp = &cmdq->state.tflag;
222: else {
223: if (cmd_find_current(&fs, cmdq, CMD_FIND_QUIET) != 0)
224: goto end;
225: fsp = &fs;
1.39 nicm 226: }
1.40 nicm 227: name = cmd->entry->name;
228: if (hooks_wait(fsp->s->hooks, cmdq, fsp, "after-%s", name) == 0)
229: retval = CMD_RETURN_WAIT;
230:
231: end:
1.31 nicm 232: cmdq_guard(cmdq, "end", flags);
1.41 nicm 233: cmd_list_free(cmdlist);
1.24 nicm 234: return (retval);
1.31 nicm 235:
236: error:
237: cmdq_guard(cmdq, "error", flags);
1.41 nicm 238: cmd_list_free(cmdlist);
1.31 nicm 239: return (CMD_RETURN_ERROR);
1.24 nicm 240: }
241:
1.1 nicm 242: /* Continue processing command queue. Returns 1 if finishes empty. */
243: int
244: cmdq_continue(struct cmd_q *cmdq)
245: {
1.31 nicm 246: struct client *c = cmdq->client;
1.1 nicm 247: struct cmd_q_item *next;
248: enum cmd_retval retval;
1.24 nicm 249: int empty;
1.1 nicm 250:
1.42 ! nicm 251: log_debug("continuing cmdq %p: flags %#x (%p)", cmdq, cmdq->flags, c);
1.22 nicm 252: cmdq->references++;
1.1 nicm 253:
254: empty = TAILQ_EMPTY(&cmdq->queue);
255: if (empty)
256: goto empty;
257:
1.40 nicm 258: if (cmdq->item == NULL) {
259: cmdq->item = TAILQ_FIRST(&cmdq->queue);
260: cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list);
261: } else
262: cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry);
1.1 nicm 263:
264: do {
265: while (cmdq->cmd != NULL) {
1.24 nicm 266: retval = cmdq_continue_one(cmdq);
1.1 nicm 267: if (retval == CMD_RETURN_ERROR)
268: break;
269: if (retval == CMD_RETURN_WAIT)
270: goto out;
271: if (retval == CMD_RETURN_STOP) {
272: cmdq_flush(cmdq);
273: goto empty;
274: }
275: cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry);
276: }
1.19 nicm 277: next = TAILQ_NEXT(cmdq->item, qentry);
1.1 nicm 278:
279: TAILQ_REMOVE(&cmdq->queue, cmdq->item, qentry);
280: cmd_list_free(cmdq->item->cmdlist);
281: free(cmdq->item);
282:
283: cmdq->item = next;
284: if (cmdq->item != NULL)
285: cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list);
286: } while (cmdq->item != NULL);
287:
288: empty:
1.41 nicm 289: log_debug("cmdq %p empty", cmdq);
1.11 nicm 290: if (cmdq->client_exit > 0)
1.1 nicm 291: cmdq->client->flags |= CLIENT_EXIT;
292: if (cmdq->emptyfn != NULL)
1.22 nicm 293: cmdq->emptyfn(cmdq);
1.1 nicm 294: empty = 1;
295:
296: out:
1.22 nicm 297: cmdq_free(cmdq);
1.1 nicm 298: return (empty);
299: }
300:
301: /* Flush command queue. */
1.38 nicm 302: static void
1.1 nicm 303: cmdq_flush(struct cmd_q *cmdq)
304: {
305: struct cmd_q_item *item, *item1;
306:
307: TAILQ_FOREACH_SAFE(item, &cmdq->queue, qentry, item1) {
308: TAILQ_REMOVE(&cmdq->queue, item, qentry);
309: cmd_list_free(item->cmdlist);
310: free(item);
311: }
312: cmdq->item = NULL;
313: }
1.33 nicm 314: