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