File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / tmux / cmd-queue.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 14 12:22:44 2017 UTC (6 years, 11 months ago) by misho
Branches: tmux, MAIN
CVS tags: v2_4p0, v2_4, HEAD
tmux 2.4

    1: /* $OpenBSD$ */
    2: 
    3: /*
    4:  * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
    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: /* Global command queue. */
   29: static struct cmdq_list global_queue = TAILQ_HEAD_INITIALIZER(global_queue);
   30: 
   31: /* Get command queue name. */
   32: static const char *
   33: cmdq_name(struct client *c)
   34: {
   35: 	static char	s[32];
   36: 
   37: 	if (c == NULL)
   38: 		return ("<global>");
   39: 	xsnprintf(s, sizeof s, "<%p>", c);
   40: 	return (s);
   41: }
   42: 
   43: /* Get command queue from client. */
   44: static struct cmdq_list *
   45: cmdq_get(struct client *c)
   46: {
   47: 	if (c == NULL)
   48: 		return (&global_queue);
   49: 	return (&c->queue);
   50: }
   51: 
   52: /* Append an item. */
   53: void
   54: cmdq_append(struct client *c, struct cmdq_item *item)
   55: {
   56: 	struct cmdq_list	*queue = cmdq_get(c);
   57: 	struct cmdq_item	*next;
   58: 
   59: 	do {
   60: 		next = item->next;
   61: 		item->next = NULL;
   62: 
   63: 		if (c != NULL)
   64: 			c->references++;
   65: 		item->client = c;
   66: 
   67: 		item->queue = queue;
   68: 		TAILQ_INSERT_TAIL(queue, item, entry);
   69: 
   70: 		item = next;
   71: 	} while (item != NULL);
   72: }
   73: 
   74: /* Insert an item. */
   75: void
   76: cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
   77: {
   78: 	struct client		*c = after->client;
   79: 	struct cmdq_list	*queue = after->queue;
   80: 	struct cmdq_item	*next;
   81: 
   82: 	do {
   83: 		next = item->next;
   84: 		item->next = NULL;
   85: 
   86: 		if (c != NULL)
   87: 			c->references++;
   88: 		item->client = c;
   89: 
   90: 		item->queue = queue;
   91: 		if (after->next != NULL)
   92: 			TAILQ_INSERT_AFTER(queue, after->next, item, entry);
   93: 		else
   94: 			TAILQ_INSERT_AFTER(queue, after, item, entry);
   95: 		after->next = item;
   96: 
   97: 		item = next;
   98: 	} while (item != NULL);
   99: }
  100: 
  101: /* Remove an item. */
  102: static void
  103: cmdq_remove(struct cmdq_item *item)
  104: {
  105: 	if (item->formats != NULL)
  106: 		format_free(item->formats);
  107: 
  108: 	if (item->client != NULL)
  109: 		server_client_unref(item->client);
  110: 
  111: 	if (item->type == CMDQ_COMMAND)
  112: 		cmd_list_free(item->cmdlist);
  113: 
  114: 	TAILQ_REMOVE(item->queue, item, entry);
  115: 
  116: 	free((void *)item->name);
  117: 	free(item);
  118: }
  119: 
  120: /* Set command group. */
  121: static u_int
  122: cmdq_next_group(void)
  123: {
  124: 	static u_int	group;
  125: 
  126: 	return (++group);
  127: }
  128: 
  129: /* Remove all subsequent items that match this item's group. */
  130: static void
  131: cmdq_remove_group(struct cmdq_item *item)
  132: {
  133: 	struct cmdq_item	*this, *next;
  134: 
  135: 	this = TAILQ_NEXT(item, entry);
  136: 	while (this != NULL) {
  137: 		next = TAILQ_NEXT(this, entry);
  138: 		if (this->group == item->group)
  139: 			cmdq_remove(this);
  140: 		this = next;
  141: 	}
  142: }
  143: 
  144: /* Get a command for the command queue. */
  145: struct cmdq_item *
  146: cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
  147:     struct mouse_event *m, int flags)
  148: {
  149: 	struct cmdq_item	*item, *first = NULL, *last = NULL;
  150: 	struct cmd		*cmd;
  151: 	u_int			 group = cmdq_next_group();
  152: 	char			*tmp;
  153: 
  154: 	TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
  155: 		xasprintf(&tmp, "command[%s]", cmd->entry->name);
  156: 
  157: 		item = xcalloc(1, sizeof *item);
  158: 		item->name = tmp;
  159: 		item->type = CMDQ_COMMAND;
  160: 
  161: 		item->group = group;
  162: 		item->flags = flags;
  163: 
  164: 		item->cmdlist = cmdlist;
  165: 		item->cmd = cmd;
  166: 
  167: 		if (current != NULL)
  168: 			cmd_find_copy_state(&item->current, current);
  169: 		if (m != NULL)
  170: 			memcpy(&item->mouse, m, sizeof item->mouse);
  171: 		cmdlist->references++;
  172: 
  173: 		if (first == NULL)
  174: 			first = item;
  175: 		if (last != NULL)
  176: 			last->next = item;
  177: 		last = item;
  178: 	}
  179: 	return (first);
  180: }
  181: 
  182: /* Fire command on command queue. */
  183: static enum cmd_retval
  184: cmdq_fire_command(struct cmdq_item *item)
  185: {
  186: 	struct client		*c = item->client;
  187: 	struct cmd		*cmd = item->cmd;
  188: 	enum cmd_retval		 retval;
  189: 	const char		*name;
  190: 	struct cmd_find_state	*fsp, fs;
  191: 	int			 flags;
  192: 
  193: 	flags = !!(cmd->flags & CMD_CONTROL);
  194: 	cmdq_guard(item, "begin", flags);
  195: 
  196: 	if (cmd_prepare_state(cmd, item) != 0) {
  197: 		retval = CMD_RETURN_ERROR;
  198: 		goto out;
  199: 	}
  200: 	if (item->client == NULL)
  201: 		item->client = cmd_find_client(item, NULL, CMD_FIND_QUIET);
  202: 
  203: 	retval = cmd->entry->exec(cmd, item);
  204: 	if (retval == CMD_RETURN_ERROR)
  205: 		goto out;
  206: 
  207: 	if (cmd->entry->flags & CMD_AFTERHOOK) {
  208: 		name = cmd->entry->name;
  209: 		if (cmd_find_valid_state(&item->state.tflag))
  210: 			fsp = &item->state.tflag;
  211: 		else {
  212: 			if (cmd_find_current(&fs, item, CMD_FIND_QUIET) != 0)
  213: 				goto out;
  214: 			fsp = &fs;
  215: 		}
  216: 		hooks_insert(fsp->s->hooks, item, fsp, "after-%s", name);
  217: 	}
  218: 
  219: out:
  220: 	item->client = c;
  221: 	if (retval == CMD_RETURN_ERROR)
  222: 		cmdq_guard(item, "error", flags);
  223: 	else
  224: 		cmdq_guard(item, "end", flags);
  225: 	return (retval);
  226: }
  227: 
  228: /* Get a callback for the command queue. */
  229: struct cmdq_item *
  230: cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
  231: {
  232: 	struct cmdq_item	*item;
  233: 	char			*tmp;
  234: 
  235: 	xasprintf(&tmp, "callback[%s]", name);
  236: 
  237: 	item = xcalloc(1, sizeof *item);
  238: 	item->name = tmp;
  239: 	item->type = CMDQ_CALLBACK;
  240: 
  241: 	item->group = 0;
  242: 	item->flags = 0;
  243: 
  244: 	item->cb = cb;
  245: 	item->data = data;
  246: 
  247: 	return (item);
  248: }
  249: 
  250: /* Fire callback on callback queue. */
  251: static enum cmd_retval
  252: cmdq_fire_callback(struct cmdq_item *item)
  253: {
  254: 	return (item->cb(item, item->data));
  255: }
  256: 
  257: /* Add a format to command queue. */
  258: void
  259: cmdq_format(struct cmdq_item *item, const char *key, const char *fmt, ...)
  260: {
  261: 	va_list			 ap;
  262: 	struct cmdq_item	*loop;
  263: 	char			*value;
  264: 
  265: 	va_start(ap, fmt);
  266: 	xvasprintf(&value, fmt, ap);
  267: 	va_end(ap);
  268: 
  269: 	for (loop = item; loop != NULL; loop = item->next) {
  270: 		if (loop->formats == NULL)
  271: 			loop->formats = format_create(NULL, FORMAT_NONE, 0);
  272: 		format_add(loop->formats, key, "%s", value);
  273: 	}
  274: 
  275: 	free(value);
  276: }
  277: 
  278: /* Process next item on command queue. */
  279: u_int
  280: cmdq_next(struct client *c)
  281: {
  282: 	struct cmdq_list	*queue = cmdq_get(c);
  283: 	const char		*name = cmdq_name(c);
  284: 	struct cmdq_item	*item;
  285: 	enum cmd_retval		 retval;
  286: 	u_int			 items = 0;
  287: 	static u_int		 number;
  288: 
  289: 	if (TAILQ_EMPTY(queue)) {
  290: 		log_debug("%s %s: empty", __func__, name);
  291: 		return (0);
  292: 	}
  293: 	if (TAILQ_FIRST(queue)->flags & CMDQ_WAITING) {
  294: 		log_debug("%s %s: waiting", __func__, name);
  295: 		return (0);
  296: 	}
  297: 
  298: 	log_debug("%s %s: enter", __func__, name);
  299: 	for (;;) {
  300: 		item = TAILQ_FIRST(queue);
  301: 		if (item == NULL)
  302: 			break;
  303: 		log_debug("%s %s: %s (%d), flags %x", __func__, name,
  304: 		    item->name, item->type, item->flags);
  305: 
  306: 		/*
  307: 		 * Any item with the waiting flag set waits until an external
  308: 		 * event clears the flag (for example, a job - look at
  309: 		 * run-shell).
  310: 		 */
  311: 		if (item->flags & CMDQ_WAITING)
  312: 			goto waiting;
  313: 
  314: 		/*
  315: 		 * Items are only fired once, once the fired flag is set, a
  316: 		 * waiting flag can only be cleared by an external event.
  317: 		 */
  318: 		if (~item->flags & CMDQ_FIRED) {
  319: 			item->time = time(NULL);
  320: 			item->number = ++number;
  321: 
  322: 			switch (item->type)
  323: 			{
  324: 			case CMDQ_COMMAND:
  325: 				retval = cmdq_fire_command(item);
  326: 
  327: 				/*
  328: 				 * If a command returns an error, remove any
  329: 				 * subsequent commands in the same group.
  330: 				 */
  331: 				if (retval == CMD_RETURN_ERROR)
  332: 					cmdq_remove_group(item);
  333: 				break;
  334: 			case CMDQ_CALLBACK:
  335: 				retval = cmdq_fire_callback(item);
  336: 				break;
  337: 			default:
  338: 				retval = CMD_RETURN_ERROR;
  339: 				break;
  340: 			}
  341: 			item->flags |= CMDQ_FIRED;
  342: 
  343: 			if (retval == CMD_RETURN_WAIT) {
  344: 				item->flags |= CMDQ_WAITING;
  345: 				goto waiting;
  346: 			}
  347: 			items++;
  348: 		}
  349: 		cmdq_remove(item);
  350: 	}
  351: 
  352: 	log_debug("%s %s: exit (empty)", __func__, name);
  353: 	return (items);
  354: 
  355: waiting:
  356: 	log_debug("%s %s: exit (wait)", __func__, name);
  357: 	return (items);
  358: }
  359: 
  360: /* Print a guard line. */
  361: void
  362: cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
  363: {
  364: 	struct client	*c = item->client;
  365: 
  366: 	if (c == NULL || !(c->flags & CLIENT_CONTROL))
  367: 		return;
  368: 
  369: 	evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
  370: 	    (long)item->time, item->number, flags);
  371: 	server_client_push_stdout(c);
  372: }
  373: 
  374: /* Show message from command. */
  375: void
  376: cmdq_print(struct cmdq_item *item, const char *fmt, ...)
  377: {
  378: 	struct client	*c = item->client;
  379: 	struct window	*w;
  380: 	va_list		 ap;
  381: 	char		*tmp, *msg;
  382: 
  383: 	va_start(ap, fmt);
  384: 
  385: 	if (c == NULL)
  386: 		/* nothing */;
  387: 	else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
  388: 		if (~c->flags & CLIENT_UTF8) {
  389: 			xvasprintf(&tmp, fmt, ap);
  390: 			msg = utf8_sanitize(tmp);
  391: 			free(tmp);
  392: 			evbuffer_add(c->stdout_data, msg, strlen(msg));
  393: 			free(msg);
  394: 		} else
  395: 			evbuffer_add_vprintf(c->stdout_data, fmt, ap);
  396: 		evbuffer_add(c->stdout_data, "\n", 1);
  397: 		server_client_push_stdout(c);
  398: 	} else {
  399: 		w = c->session->curw->window;
  400: 		if (w->active->mode != &window_copy_mode) {
  401: 			window_pane_reset_mode(w->active);
  402: 			window_pane_set_mode(w->active, &window_copy_mode);
  403: 			window_copy_init_for_output(w->active);
  404: 		}
  405: 		window_copy_vadd(w->active, fmt, ap);
  406: 	}
  407: 
  408: 	va_end(ap);
  409: }
  410: 
  411: /* Show error from command. */
  412: void
  413: cmdq_error(struct cmdq_item *item, const char *fmt, ...)
  414: {
  415: 	struct client	*c = item->client;
  416: 	struct cmd	*cmd = item->cmd;
  417: 	va_list		 ap;
  418: 	char		*msg;
  419: 	size_t		 msglen;
  420: 	char		*tmp;
  421: 
  422: 	va_start(ap, fmt);
  423: 	msglen = xvasprintf(&msg, fmt, ap);
  424: 	va_end(ap);
  425: 
  426: 	if (c == NULL)
  427: 		cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg);
  428: 	else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
  429: 		if (~c->flags & CLIENT_UTF8) {
  430: 			tmp = msg;
  431: 			msg = utf8_sanitize(tmp);
  432: 			free(tmp);
  433: 			msglen = strlen(msg);
  434: 		}
  435: 		evbuffer_add(c->stderr_data, msg, msglen);
  436: 		evbuffer_add(c->stderr_data, "\n", 1);
  437: 		server_client_push_stderr(c);
  438: 		c->retval = 1;
  439: 	} else {
  440: 		*msg = toupper((u_char) *msg);
  441: 		status_message_set(c, "%s", msg);
  442: 	}
  443: 
  444: 	free(msg);
  445: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>