cmd-queue.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /* $OpenBSD$ */
  2. /*
  3. * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
  14. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  15. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include <sys/types.h>
  18. #include <ctype.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <time.h>
  22. #include "tmux.h"
  23. #include "tmate.h"
  24. static enum cmd_retval cmdq_continue_one(struct cmd_q *);
  25. /* Create new command queue. */
  26. struct cmd_q *
  27. cmdq_new(struct client *c)
  28. {
  29. struct cmd_q *cmdq;
  30. cmdq = xcalloc(1, sizeof *cmdq);
  31. cmdq->references = 1;
  32. cmdq->flags = 0;
  33. cmdq->client = c;
  34. cmdq->client_exit = -1;
  35. TAILQ_INIT(&cmdq->queue);
  36. cmdq->item = NULL;
  37. cmdq->cmd = NULL;
  38. cmd_find_clear_state(&cmdq->current, NULL, 0);
  39. cmdq->parent = NULL;
  40. return (cmdq);
  41. }
  42. /* Free command queue */
  43. int
  44. cmdq_free(struct cmd_q *cmdq)
  45. {
  46. if (--cmdq->references != 0) {
  47. if (cmdq->flags & CMD_Q_DEAD)
  48. return (1);
  49. return (0);
  50. }
  51. cmdq_flush(cmdq);
  52. free(cmdq);
  53. return (1);
  54. }
  55. /* Show message from command. */
  56. void
  57. cmdq_print(struct cmd_q *cmdq, const char *fmt, ...)
  58. {
  59. struct client *c = cmdq->client;
  60. struct window *w;
  61. va_list ap;
  62. char *tmp, *msg;
  63. va_start(ap, fmt);
  64. if (c == NULL)
  65. /* nothing */;
  66. else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
  67. if (~c->flags & CLIENT_UTF8) {
  68. vasprintf(&tmp, fmt, ap);
  69. msg = utf8_sanitize(tmp);
  70. free(tmp);
  71. evbuffer_add(c->stdout_data, msg, strlen(msg));
  72. free(msg);
  73. } else
  74. evbuffer_add_vprintf(c->stdout_data, fmt, ap);
  75. evbuffer_add(c->stdout_data, "\n", 1);
  76. server_client_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. #ifdef TMATE
  84. tmate_sync_copy_mode(w->active);
  85. #endif
  86. }
  87. window_copy_vadd(w->active, fmt, ap);
  88. }
  89. va_end(ap);
  90. }
  91. /* Show error from command. */
  92. void
  93. cmdq_error(struct cmd_q *cmdq, const char *fmt, ...)
  94. {
  95. struct client *c = cmdq->client;
  96. struct cmd *cmd = cmdq->cmd;
  97. va_list ap;
  98. char *msg;
  99. size_t msglen;
  100. char *tmp;
  101. va_start(ap, fmt);
  102. msglen = xvasprintf(&msg, fmt, ap);
  103. va_end(ap);
  104. if (c == NULL)
  105. #ifdef TMATE
  106. if (cmd->file && cmd->line)
  107. cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg);
  108. else
  109. cfg_add_cause("%s", msg);
  110. #else
  111. cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg);
  112. #endif
  113. else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
  114. if (~c->flags & CLIENT_UTF8) {
  115. tmp = msg;
  116. msg = utf8_sanitize(tmp);
  117. free(tmp);
  118. msglen = strlen(msg);
  119. }
  120. evbuffer_add(c->stderr_data, msg, msglen);
  121. evbuffer_add(c->stderr_data, "\n", 1);
  122. server_client_push_stderr(c);
  123. c->retval = 1;
  124. } else {
  125. *msg = toupper((u_char) *msg);
  126. status_message_set(c, "%s", msg);
  127. }
  128. free(msg);
  129. }
  130. /* Print a guard line. */
  131. void
  132. cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags)
  133. {
  134. struct client *c = cmdq->client;
  135. if (c == NULL || !(c->flags & CLIENT_CONTROL))
  136. return;
  137. evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
  138. (long) cmdq->time, cmdq->number, flags);
  139. server_client_push_stdout(c);
  140. }
  141. /* Add command list to queue and begin processing if needed. */
  142. void
  143. cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m)
  144. {
  145. cmdq_append(cmdq, cmdlist, m);
  146. if (cmdq->item == NULL) {
  147. cmdq->cmd = NULL;
  148. cmdq_continue(cmdq);
  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. item = xcalloc(1, sizeof *item);
  157. item->cmdlist = cmdlist;
  158. TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry);
  159. cmdlist->references++;
  160. if (m != NULL)
  161. memcpy(&item->mouse, m, sizeof item->mouse);
  162. else
  163. item->mouse.valid = 0;
  164. }
  165. /* Process one command. */
  166. static enum cmd_retval
  167. cmdq_continue_one(struct cmd_q *cmdq)
  168. {
  169. struct cmd *cmd = cmdq->cmd;
  170. enum cmd_retval retval;
  171. char *tmp;
  172. int flags = !!(cmd->flags & CMD_CONTROL);
  173. #ifdef TMATE
  174. if (tmate_should_replicate_cmd(cmd->entry))
  175. tmate_exec_cmd(cmd);
  176. #endif
  177. tmp = cmd_print(cmd);
  178. log_debug("cmdq %p: %s", cmdq, tmp);
  179. free(tmp);
  180. cmdq->time = time(NULL);
  181. cmdq->number++;
  182. cmdq_guard(cmdq, "begin", flags);
  183. if (cmd_prepare_state(cmd, cmdq, NULL) != 0)
  184. goto error;
  185. retval = cmd->entry->exec(cmd, cmdq);
  186. if (retval == CMD_RETURN_ERROR)
  187. goto error;
  188. cmdq_guard(cmdq, "end", flags);
  189. return (retval);
  190. error:
  191. cmdq_guard(cmdq, "error", flags);
  192. return (CMD_RETURN_ERROR);
  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. cmdq->references++;
  203. notify_disable();
  204. log_debug("continuing cmdq %p: flags %#x, client %p", cmdq, cmdq->flags,
  205. c);
  206. empty = TAILQ_EMPTY(&cmdq->queue);
  207. if (empty)
  208. goto empty;
  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. do {
  215. while (cmdq->cmd != NULL) {
  216. retval = cmdq_continue_one(cmdq);
  217. if (retval == CMD_RETURN_ERROR)
  218. break;
  219. if (retval == CMD_RETURN_WAIT)
  220. goto out;
  221. if (retval == CMD_RETURN_STOP) {
  222. cmdq_flush(cmdq);
  223. goto empty;
  224. }
  225. cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry);
  226. }
  227. next = TAILQ_NEXT(cmdq->item, qentry);
  228. TAILQ_REMOVE(&cmdq->queue, cmdq->item, qentry);
  229. cmd_list_free(cmdq->item->cmdlist);
  230. free(cmdq->item);
  231. cmdq->item = next;
  232. if (cmdq->item != NULL)
  233. cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list);
  234. } while (cmdq->item != NULL);
  235. empty:
  236. if (cmdq->client_exit > 0)
  237. cmdq->client->flags |= CLIENT_EXIT;
  238. if (cmdq->emptyfn != NULL)
  239. cmdq->emptyfn(cmdq);
  240. empty = 1;
  241. out:
  242. notify_enable();
  243. cmdq_free(cmdq);
  244. return (empty);
  245. }
  246. /* Flush command queue. */
  247. void
  248. cmdq_flush(struct cmd_q *cmdq)
  249. {
  250. struct cmd_q_item *item, *item1;
  251. TAILQ_FOREACH_SAFE(item, &cmdq->queue, qentry, item1) {
  252. TAILQ_REMOVE(&cmdq->queue, item, qentry);
  253. cmd_list_free(item->cmdlist);
  254. free(item);
  255. }
  256. cmdq->item = NULL;
  257. }