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