1 /* $OpenBSD$ */ 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 73 va_start(ap, fmt); 74 75 if (c == NULL) 76 /* nothing */; 77 else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { 78 evbuffer_add_vprintf(c->stdout_data, fmt, ap); 79 80 evbuffer_add(c->stdout_data, "\n", 1); 81 server_push_stdout(c); 82 } else { 83 w = c->session->curw->window; 84 if (w->active->mode != &window_copy_mode) { 85 window_pane_reset_mode(w->active); 86 window_pane_set_mode(w->active, &window_copy_mode); 87 window_copy_init_for_output(w->active); 88 } 89 window_copy_vadd(w->active, fmt, ap); 90 } 91 92 va_end(ap); 93 } 94 95 /* Show error from command. */ 96 void 97 cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) 98 { 99 struct client *c = cmdq->client; 100 struct cmd *cmd = cmdq->cmd; 101 va_list ap; 102 char *msg; 103 size_t msglen; 104 105 va_start(ap, fmt); 106 msglen = xvasprintf(&msg, fmt, ap); 107 va_end(ap); 108 109 if (c == NULL) 110 cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); 111 else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { 112 evbuffer_add(c->stderr_data, msg, msglen); 113 evbuffer_add(c->stderr_data, "\n", 1); 114 115 server_push_stderr(c); 116 c->retval = 1; 117 } else { 118 *msg = toupper((u_char) *msg); 119 status_message_set(c, "%s", msg); 120 } 121 122 free(msg); 123 } 124 125 /* Print a guard line. */ 126 void 127 cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) 128 { 129 struct client *c = cmdq->client; 130 131 if (c == NULL || !(c->flags & CLIENT_CONTROL)) 132 return; 133 134 evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, 135 (long) cmdq->time, cmdq->number, flags); 136 server_push_stdout(c); 137 } 138 139 /* Add command list to queue and begin processing if needed. */ 140 void 141 cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) 142 { 143 cmdq_append(cmdq, cmdlist, m); 144 145 if (cmdq->item == NULL) { 146 cmdq->cmd = NULL; 147 cmdq_continue(cmdq); 148 } 149 } 150 151 /* Add command list to queue. */ 152 void 153 cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) 154 { 155 struct cmd_q_item *item; 156 157 item = xcalloc(1, sizeof *item); 158 item->cmdlist = cmdlist; 159 TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry); 160 cmdlist->references++; 161 162 if (m != NULL) 163 memcpy(&item->mouse, m, sizeof item->mouse); 164 else 165 item->mouse.valid = 0; 166 } 167 168 /* Process one command. */ 169 enum cmd_retval 170 cmdq_continue_one(struct cmd_q *cmdq) 171 { 172 struct cmd *cmd = cmdq->cmd; 173 enum cmd_retval retval; 174 char tmp[1024]; 175 int flags = !!(cmd->flags & CMD_CONTROL); 176 177 cmd_print(cmd, tmp, sizeof tmp); 178 log_debug("cmdq %p: %s", cmdq, tmp); 179 180 cmdq->time = time(NULL); 181 cmdq->number++; 182 183 cmdq_guard(cmdq, "begin", flags); 184 185 retval = cmd->entry->exec(cmd, cmdq); 186 187 if (retval == CMD_RETURN_ERROR) 188 cmdq_guard(cmdq, "error", flags); 189 else 190 cmdq_guard(cmdq, "end", flags); 191 return (retval); 192 } 193 194 /* Continue processing command queue. Returns 1 if finishes empty. */ 195 int 196 cmdq_continue(struct cmd_q *cmdq) 197 { 198 struct client *c = cmdq->client; 199 struct cmd_q_item *next; 200 enum cmd_retval retval; 201 int empty; 202 203 cmdq->references++; 204 notify_disable(); 205 206 log_debug("continuing cmdq %p: flags=%#x, client=%d", cmdq, cmdq->flags, 207 c != NULL ? c->ibuf.fd : -1); 208 209 empty = TAILQ_EMPTY(&cmdq->queue); 210 if (empty) 211 goto empty; 212 213 if (cmdq->item == NULL) { 214 cmdq->item = TAILQ_FIRST(&cmdq->queue); 215 cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list); 216 } else 217 cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); 218 219 do { 220 while (cmdq->cmd != NULL) { 221 retval = cmdq_continue_one(cmdq); 222 if (retval == CMD_RETURN_ERROR) 223 break; 224 if (retval == CMD_RETURN_WAIT) 225 goto out; 226 if (retval == CMD_RETURN_STOP) { 227 cmdq_flush(cmdq); 228 goto empty; 229 } 230 cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); 231 } 232 next = TAILQ_NEXT(cmdq->item, qentry); 233 234 TAILQ_REMOVE(&cmdq->queue, cmdq->item, qentry); 235 cmd_list_free(cmdq->item->cmdlist); 236 free(cmdq->item); 237 238 cmdq->item = next; 239 if (cmdq->item != NULL) 240 cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list); 241 } while (cmdq->item != NULL); 242 243 empty: 244 if (cmdq->client_exit > 0) 245 cmdq->client->flags |= CLIENT_EXIT; 246 if (cmdq->emptyfn != NULL) 247 cmdq->emptyfn(cmdq); 248 empty = 1; 249 250 out: 251 notify_enable(); 252 cmdq_free(cmdq); 253 254 return (empty); 255 } 256 257 /* Flush command queue. */ 258 void 259 cmdq_flush(struct cmd_q *cmdq) 260 { 261 struct cmd_q_item *item, *item1; 262 263 TAILQ_FOREACH_SAFE(item, &cmdq->queue, qentry, item1) { 264 TAILQ_REMOVE(&cmdq->queue, item, qentry); 265 cmd_list_free(item->cmdlist); 266 free(item); 267 } 268 cmdq->item = NULL; 269 } 270