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