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