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