1 /* $OpenBSD: cmd-queue.c,v 1.31 2015/12/13 14:32:38 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 203 cmdq_guard(cmdq, "end", flags); 204 return (retval); 205 206 error: 207 cmdq_guard(cmdq, "error", flags); 208 return (CMD_RETURN_ERROR); 209 } 210 211 /* Continue processing command queue. Returns 1 if finishes empty. */ 212 int 213 cmdq_continue(struct cmd_q *cmdq) 214 { 215 struct client *c = cmdq->client; 216 struct cmd_q_item *next; 217 enum cmd_retval retval; 218 int empty; 219 220 cmdq->references++; 221 notify_disable(); 222 223 log_debug("continuing cmdq %p: flags %#x, client %p", cmdq, cmdq->flags, 224 c); 225 226 empty = TAILQ_EMPTY(&cmdq->queue); 227 if (empty) 228 goto empty; 229 230 if (cmdq->item == NULL) { 231 cmdq->item = TAILQ_FIRST(&cmdq->queue); 232 cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list); 233 } else 234 cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); 235 236 do { 237 while (cmdq->cmd != NULL) { 238 retval = cmdq_continue_one(cmdq); 239 if (retval == CMD_RETURN_ERROR) 240 break; 241 if (retval == CMD_RETURN_WAIT) 242 goto out; 243 if (retval == CMD_RETURN_STOP) { 244 cmdq_flush(cmdq); 245 goto empty; 246 } 247 cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); 248 } 249 next = TAILQ_NEXT(cmdq->item, qentry); 250 251 TAILQ_REMOVE(&cmdq->queue, cmdq->item, qentry); 252 cmd_list_free(cmdq->item->cmdlist); 253 free(cmdq->item); 254 255 cmdq->item = next; 256 if (cmdq->item != NULL) 257 cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list); 258 } while (cmdq->item != NULL); 259 260 empty: 261 if (cmdq->client_exit > 0) 262 cmdq->client->flags |= CLIENT_EXIT; 263 if (cmdq->emptyfn != NULL) 264 cmdq->emptyfn(cmdq); 265 empty = 1; 266 267 out: 268 notify_enable(); 269 cmdq_free(cmdq); 270 271 return (empty); 272 } 273 274 /* Flush command queue. */ 275 void 276 cmdq_flush(struct cmd_q *cmdq) 277 { 278 struct cmd_q_item *item, *item1; 279 280 TAILQ_FOREACH_SAFE(item, &cmdq->queue, qentry, item1) { 281 TAILQ_REMOVE(&cmdq->queue, item, qentry); 282 cmd_list_free(item->cmdlist); 283 free(item); 284 } 285 cmdq->item = NULL; 286 } 287