1 /* $OpenBSD: cmd-queue.c,v 1.23 2015/04/19 21:34:21 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2013 Nicholas Marriott <nicm@users.sourceforge.net> 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> 23 #include <string.h> 24 #include <time.h> 25 26 #include "tmux.h" 27 28 /* Create new command queue. */ 29 struct cmd_q * 30 cmdq_new(struct client *c) 31 { 32 struct cmd_q *cmdq; 33 34 cmdq = xcalloc(1, sizeof *cmdq); 35 cmdq->references = 1; 36 cmdq->dead = 0; 37 38 cmdq->client = c; 39 cmdq->client_exit = -1; 40 41 TAILQ_INIT(&cmdq->queue); 42 cmdq->item = NULL; 43 cmdq->cmd = NULL; 44 45 return (cmdq); 46 } 47 48 /* Free command queue */ 49 int 50 cmdq_free(struct cmd_q *cmdq) 51 { 52 if (--cmdq->references != 0) 53 return (cmdq->dead); 54 55 cmdq_flush(cmdq); 56 free(cmdq); 57 return (1); 58 } 59 60 /* Show message from command. */ 61 void 62 cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) 63 { 64 struct client *c = cmdq->client; 65 struct window *w; 66 va_list ap; 67 68 va_start(ap, fmt); 69 70 if (c == NULL) 71 /* nothing */; 72 else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { 73 evbuffer_add_vprintf(c->stdout_data, fmt, ap); 74 75 evbuffer_add(c->stdout_data, "\n", 1); 76 server_push_stdout(c); 77 } else { 78 w = c->session->curw->window; 79 if (w->active->mode != &window_copy_mode) { 80 window_pane_reset_mode(w->active); 81 window_pane_set_mode(w->active, &window_copy_mode); 82 window_copy_init_for_output(w->active); 83 } 84 window_copy_vadd(w->active, fmt, ap); 85 } 86 87 va_end(ap); 88 } 89 90 /* Show error from command. */ 91 void 92 cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) 93 { 94 struct client *c = cmdq->client; 95 struct cmd *cmd = cmdq->cmd; 96 va_list ap; 97 char *msg; 98 size_t msglen; 99 100 va_start(ap, fmt); 101 msglen = xvasprintf(&msg, fmt, ap); 102 va_end(ap); 103 104 if (c == NULL) 105 cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); 106 else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { 107 evbuffer_add(c->stderr_data, msg, msglen); 108 evbuffer_add(c->stderr_data, "\n", 1); 109 110 server_push_stderr(c); 111 c->retval = 1; 112 } else { 113 *msg = toupper((u_char) *msg); 114 status_message_set(c, "%s", msg); 115 } 116 117 free(msg); 118 } 119 120 /* Print a guard line. */ 121 void 122 cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) 123 { 124 struct client *c = cmdq->client; 125 126 if (c == NULL || !(c->flags & CLIENT_CONTROL)) 127 return; 128 129 evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, 130 (long) cmdq->time, cmdq->number, flags); 131 server_push_stdout(c); 132 } 133 134 /* Add command list to queue and begin processing if needed. */ 135 void 136 cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) 137 { 138 cmdq_append(cmdq, cmdlist, m); 139 140 if (cmdq->item == NULL) { 141 cmdq->cmd = NULL; 142 cmdq_continue(cmdq); 143 } 144 } 145 146 /* Add command list to queue. */ 147 void 148 cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) 149 { 150 struct cmd_q_item *item; 151 152 item = xcalloc(1, sizeof *item); 153 item->cmdlist = cmdlist; 154 TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry); 155 cmdlist->references++; 156 157 if (m != NULL) 158 memcpy(&item->mouse, m, sizeof item->mouse); 159 else 160 item->mouse.valid = 0; 161 } 162 163 /* Continue processing command queue. Returns 1 if finishes empty. */ 164 int 165 cmdq_continue(struct cmd_q *cmdq) 166 { 167 struct cmd_q_item *next; 168 enum cmd_retval retval; 169 int empty, flags; 170 char s[1024]; 171 172 cmdq->references++; 173 notify_disable(); 174 175 empty = TAILQ_EMPTY(&cmdq->queue); 176 if (empty) 177 goto empty; 178 179 if (cmdq->item == NULL) { 180 cmdq->item = TAILQ_FIRST(&cmdq->queue); 181 cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list); 182 } else 183 cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); 184 185 do { 186 while (cmdq->cmd != NULL) { 187 cmd_print(cmdq->cmd, s, sizeof s); 188 log_debug("cmdq %p: %s (client %d)", cmdq, s, 189 cmdq->client != NULL ? cmdq->client->ibuf.fd : -1); 190 191 cmdq->time = time(NULL); 192 cmdq->number++; 193 194 flags = !!(cmdq->cmd->flags & CMD_CONTROL); 195 cmdq_guard(cmdq, "begin", flags); 196 197 retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq); 198 199 if (retval == CMD_RETURN_ERROR) 200 cmdq_guard(cmdq, "error", flags); 201 else 202 cmdq_guard(cmdq, "end", flags); 203 204 if (retval == CMD_RETURN_ERROR) 205 break; 206 if (retval == CMD_RETURN_WAIT) 207 goto out; 208 if (retval == CMD_RETURN_STOP) { 209 cmdq_flush(cmdq); 210 goto empty; 211 } 212 213 cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); 214 } 215 next = TAILQ_NEXT(cmdq->item, qentry); 216 217 TAILQ_REMOVE(&cmdq->queue, cmdq->item, qentry); 218 cmd_list_free(cmdq->item->cmdlist); 219 free(cmdq->item); 220 221 cmdq->item = next; 222 if (cmdq->item != NULL) 223 cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list); 224 } while (cmdq->item != NULL); 225 226 empty: 227 if (cmdq->client_exit > 0) 228 cmdq->client->flags |= CLIENT_EXIT; 229 if (cmdq->emptyfn != NULL) 230 cmdq->emptyfn(cmdq); 231 empty = 1; 232 233 out: 234 notify_enable(); 235 cmdq_free(cmdq); 236 237 return (empty); 238 } 239 240 /* Flush command queue. */ 241 void 242 cmdq_flush(struct cmd_q *cmdq) 243 { 244 struct cmd_q_item *item, *item1; 245 246 TAILQ_FOREACH_SAFE(item, &cmdq->queue, qentry, item1) { 247 TAILQ_REMOVE(&cmdq->queue, item, qentry); 248 cmd_list_free(item->cmdlist); 249 free(item); 250 } 251 cmdq->item = NULL; 252 } 253