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