mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 00:56:10 +00:00 
			
		
		
		
	Merge branch 'obsd-master'
This commit is contained in:
		@@ -94,6 +94,7 @@ dist_tmux_SOURCES = \
 | 
			
		||||
	cmd-move-window.c \
 | 
			
		||||
	cmd-new-session.c \
 | 
			
		||||
	cmd-new-window.c \
 | 
			
		||||
	cmd-parse.y \
 | 
			
		||||
	cmd-paste-buffer.c \
 | 
			
		||||
	cmd-pipe-pane.c \
 | 
			
		||||
	cmd-queue.c \
 | 
			
		||||
@@ -119,7 +120,6 @@ dist_tmux_SOURCES = \
 | 
			
		||||
	cmd-show-options.c \
 | 
			
		||||
	cmd-source-file.c \
 | 
			
		||||
	cmd-split-window.c \
 | 
			
		||||
	cmd-string.c \
 | 
			
		||||
	cmd-swap-pane.c \
 | 
			
		||||
	cmd-swap-window.c \
 | 
			
		||||
	cmd-switch-client.c \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										236
									
								
								cfg.c
									
									
									
									
									
								
							
							
						
						
									
										236
									
								
								cfg.c
									
									
									
									
									
								
							@@ -26,17 +26,6 @@
 | 
			
		||||
 | 
			
		||||
#include "tmux.h"
 | 
			
		||||
 | 
			
		||||
/* Condition for %if, %elif, %else and %endif. */
 | 
			
		||||
struct cfg_cond {
 | 
			
		||||
	size_t			line;		/* line number of %if */
 | 
			
		||||
	int			met;		/* condition was met */
 | 
			
		||||
	int			skip;		/* skip later %elif/%else */
 | 
			
		||||
	int			saw_else;	/* saw a %else */
 | 
			
		||||
 | 
			
		||||
	TAILQ_ENTRY(cfg_cond)	entry;
 | 
			
		||||
};
 | 
			
		||||
TAILQ_HEAD(cfg_conds, cfg_cond);
 | 
			
		||||
 | 
			
		||||
struct client		 *cfg_client;
 | 
			
		||||
static char		 *cfg_file;
 | 
			
		||||
int			  cfg_finished;
 | 
			
		||||
@@ -85,9 +74,8 @@ start_cfg(void)
 | 
			
		||||
	struct client	*c;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Configuration files are loaded without a client, so NULL is passed
 | 
			
		||||
	 * into load_cfg() and commands run in the global queue with
 | 
			
		||||
	 * item->client NULL.
 | 
			
		||||
	 * Configuration files are loaded without a client, so commands are run
 | 
			
		||||
	 * in the global queue with item->client NULL.
 | 
			
		||||
	 *
 | 
			
		||||
	 * However, we must block the initial client (but just the initial
 | 
			
		||||
	 * client) so that its command runs after the configuration is loaded.
 | 
			
		||||
@@ -102,11 +90,11 @@ start_cfg(void)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cfg_file == NULL)
 | 
			
		||||
		load_cfg(TMUX_CONF, NULL, NULL, CFG_QUIET, NULL);
 | 
			
		||||
		load_cfg(TMUX_CONF, NULL, NULL, CMD_PARSE_QUIET, NULL);
 | 
			
		||||
 | 
			
		||||
	if (cfg_file == NULL && (home = find_home()) != NULL) {
 | 
			
		||||
		xasprintf(&cfg_file, "%s/.tmux.conf", home);
 | 
			
		||||
		flags = CFG_QUIET;
 | 
			
		||||
		flags = CMD_PARSE_QUIET;
 | 
			
		||||
	}
 | 
			
		||||
	if (cfg_file != NULL)
 | 
			
		||||
		load_cfg(cfg_file, NULL, NULL, flags, NULL);
 | 
			
		||||
@@ -114,214 +102,54 @@ start_cfg(void)
 | 
			
		||||
	cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
cfg_check_cond(const char *path, size_t line, const char *p, int *skip,
 | 
			
		||||
    struct client *c, struct cmd_find_state *fs)
 | 
			
		||||
{
 | 
			
		||||
	struct format_tree	*ft;
 | 
			
		||||
	char			*s;
 | 
			
		||||
	int			 result;
 | 
			
		||||
 | 
			
		||||
	while (isspace((u_char)*p))
 | 
			
		||||
		p++;
 | 
			
		||||
	if (p[0] == '\0') {
 | 
			
		||||
		cfg_add_cause("%s:%zu: invalid condition", path, line);
 | 
			
		||||
		*skip = 1;
 | 
			
		||||
		return (0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ft = format_create(NULL, NULL, FORMAT_NONE, FORMAT_NOJOBS);
 | 
			
		||||
	if (fs != NULL)
 | 
			
		||||
		format_defaults(ft, c, fs->s, fs->wl, fs->wp);
 | 
			
		||||
	else
 | 
			
		||||
		format_defaults(ft, c, NULL, NULL, NULL);
 | 
			
		||||
	s = format_expand(ft, p);
 | 
			
		||||
	result = format_true(s);
 | 
			
		||||
	free(s);
 | 
			
		||||
	format_free(ft);
 | 
			
		||||
 | 
			
		||||
	*skip = result;
 | 
			
		||||
	return (result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds,
 | 
			
		||||
    const char *p, struct client *c, struct cmd_find_state *fs)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg_cond	*cond;
 | 
			
		||||
	struct cfg_cond	*parent = TAILQ_FIRST(conds);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Add a new condition. If a previous condition exists and isn't
 | 
			
		||||
	 * currently met, this new one also can't be met.
 | 
			
		||||
	 */
 | 
			
		||||
	cond = xcalloc(1, sizeof *cond);
 | 
			
		||||
	cond->line = line;
 | 
			
		||||
	if (parent == NULL || parent->met)
 | 
			
		||||
		cond->met = cfg_check_cond(path, line, p, &cond->skip, c, fs);
 | 
			
		||||
	else
 | 
			
		||||
		cond->skip = 1;
 | 
			
		||||
	cond->saw_else = 0;
 | 
			
		||||
	TAILQ_INSERT_HEAD(conds, cond, entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
cfg_handle_elif(const char *path, size_t line, struct cfg_conds *conds,
 | 
			
		||||
    const char *p, struct client *c, struct cmd_find_state *fs)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg_cond	*cond = TAILQ_FIRST(conds);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If a previous condition exists and wasn't met, check this
 | 
			
		||||
	 * one instead and change the state.
 | 
			
		||||
	 */
 | 
			
		||||
	if (cond == NULL || cond->saw_else)
 | 
			
		||||
		cfg_add_cause("%s:%zu: unexpected %%elif", path, line);
 | 
			
		||||
	else if (!cond->skip)
 | 
			
		||||
		cond->met = cfg_check_cond(path, line, p, &cond->skip, c, fs);
 | 
			
		||||
	else
 | 
			
		||||
		cond->met = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
cfg_handle_else(const char *path, size_t line, struct cfg_conds *conds)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg_cond	*cond = TAILQ_FIRST(conds);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If a previous condition exists and wasn't met and wasn't already
 | 
			
		||||
	 * %else, use this one instead.
 | 
			
		||||
	 */
 | 
			
		||||
	if (cond == NULL || cond->saw_else) {
 | 
			
		||||
		cfg_add_cause("%s:%zu: unexpected %%else", path, line);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	cond->saw_else = 1;
 | 
			
		||||
	cond->met = !cond->skip;
 | 
			
		||||
	cond->skip = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
cfg_handle_endif(const char *path, size_t line, struct cfg_conds *conds)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg_cond	*cond = TAILQ_FIRST(conds);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Remove previous condition if one exists.
 | 
			
		||||
	 */
 | 
			
		||||
	if (cond == NULL) {
 | 
			
		||||
		cfg_add_cause("%s:%zu: unexpected %%endif", path, line);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	TAILQ_REMOVE(conds, cond, entry);
 | 
			
		||||
	free(cond);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
cfg_handle_directive(const char *p, const char *path, size_t line,
 | 
			
		||||
    struct cfg_conds *conds, struct client *c, struct cmd_find_state *fs)
 | 
			
		||||
{
 | 
			
		||||
	int	n = 0;
 | 
			
		||||
 | 
			
		||||
	while (p[n] != '\0' && !isspace((u_char)p[n]))
 | 
			
		||||
		n++;
 | 
			
		||||
	if (strncmp(p, "%if", n) == 0)
 | 
			
		||||
		cfg_handle_if(path, line, conds, p + n, c, fs);
 | 
			
		||||
	else if (strncmp(p, "%elif", n) == 0)
 | 
			
		||||
		cfg_handle_elif(path, line, conds, p + n, c, fs);
 | 
			
		||||
	else if (strcmp(p, "%else") == 0)
 | 
			
		||||
		cfg_handle_else(path, line, conds);
 | 
			
		||||
	else if (strcmp(p, "%endif") == 0)
 | 
			
		||||
		cfg_handle_endif(path, line, conds);
 | 
			
		||||
	else
 | 
			
		||||
		cfg_add_cause("%s:%zu: invalid directive: %s", path, line, p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
 | 
			
		||||
    struct cmdq_item **new_item)
 | 
			
		||||
{
 | 
			
		||||
	FILE			*f;
 | 
			
		||||
	const char		 delim[3] = { '\\', '\\', '\0' };
 | 
			
		||||
	u_int			 found = 0;
 | 
			
		||||
	size_t			 line = 0;
 | 
			
		||||
	char			*buf, *cause1, *p, *q;
 | 
			
		||||
	struct cmd_list		*cmdlist;
 | 
			
		||||
	struct cmd_parse_input	 pi;
 | 
			
		||||
	struct cmd_parse_result	*pr;
 | 
			
		||||
	struct cmdq_item	*new_item0;
 | 
			
		||||
	struct cfg_cond		*cond, *cond1;
 | 
			
		||||
	struct cfg_conds	 conds;
 | 
			
		||||
	struct cmd_find_state	*fs = NULL;
 | 
			
		||||
	struct client		*fc = NULL;
 | 
			
		||||
 | 
			
		||||
	if (item != NULL) {
 | 
			
		||||
		fs = &item->target;
 | 
			
		||||
		fc = cmd_find_client(item, NULL, 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	TAILQ_INIT(&conds);
 | 
			
		||||
	if (new_item != NULL)
 | 
			
		||||
		*new_item = NULL;
 | 
			
		||||
 | 
			
		||||
	log_debug("loading %s", path);
 | 
			
		||||
	if ((f = fopen(path, "rb")) == NULL) {
 | 
			
		||||
		if (errno == ENOENT && (flags & CFG_QUIET))
 | 
			
		||||
		if (errno == ENOENT && (flags & CMD_PARSE_QUIET))
 | 
			
		||||
			return (0);
 | 
			
		||||
		cfg_add_cause("%s: %s", path, strerror(errno));
 | 
			
		||||
		return (-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while ((buf = fparseln(f, NULL, &line, delim, 0)) != NULL) {
 | 
			
		||||
		log_debug("%s: %s", path, buf);
 | 
			
		||||
	memset(&pi, 0, sizeof pi);
 | 
			
		||||
	pi.flags = flags;
 | 
			
		||||
	pi.file = path;
 | 
			
		||||
 | 
			
		||||
		p = buf;
 | 
			
		||||
		while (isspace((u_char)*p))
 | 
			
		||||
			p++;
 | 
			
		||||
		if (*p == '\0') {
 | 
			
		||||
			free(buf);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		q = p + strlen(p) - 1;
 | 
			
		||||
		while (q != p && isspace((u_char)*q))
 | 
			
		||||
			*q-- = '\0';
 | 
			
		||||
 | 
			
		||||
		if (*p == '%') {
 | 
			
		||||
			cfg_handle_directive(p, path, line, &conds, fc, fs);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		cond = TAILQ_FIRST(&conds);
 | 
			
		||||
		if (cond != NULL && !cond->met)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		cmdlist = cmd_string_parse(p, path, line, &cause1);
 | 
			
		||||
		if (cmdlist == NULL) {
 | 
			
		||||
			free(buf);
 | 
			
		||||
			if (cause1 == NULL)
 | 
			
		||||
				continue;
 | 
			
		||||
			cfg_add_cause("%s:%zu: %s", path, line, cause1);
 | 
			
		||||
			free(cause1);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		free(buf);
 | 
			
		||||
 | 
			
		||||
		new_item0 = cmdq_get_command(cmdlist, NULL, NULL, 0);
 | 
			
		||||
		if (item != NULL) {
 | 
			
		||||
			cmdq_insert_after(item, new_item0);
 | 
			
		||||
			item = new_item0;
 | 
			
		||||
		} else
 | 
			
		||||
			cmdq_append(c, new_item0);
 | 
			
		||||
		cmd_list_free(cmdlist);
 | 
			
		||||
 | 
			
		||||
		found++;
 | 
			
		||||
	}
 | 
			
		||||
	pr = cmd_parse_from_file(f, &pi);
 | 
			
		||||
	fclose(f);
 | 
			
		||||
 | 
			
		||||
	TAILQ_FOREACH_REVERSE_SAFE(cond, &conds, cfg_conds, entry, cond1) {
 | 
			
		||||
		cfg_add_cause("%s:%zu: unterminated %%if", path, cond->line);
 | 
			
		||||
		TAILQ_REMOVE(&conds, cond, entry);
 | 
			
		||||
		free(cond);
 | 
			
		||||
	if (pr->status == CMD_PARSE_EMPTY)
 | 
			
		||||
		return (0);
 | 
			
		||||
	if (pr->status == CMD_PARSE_ERROR) {
 | 
			
		||||
		cfg_add_cause("%s", pr->error);
 | 
			
		||||
		free(pr->error);
 | 
			
		||||
		return (-1);
 | 
			
		||||
	}
 | 
			
		||||
	if (flags & CMD_PARSE_PARSEONLY) {
 | 
			
		||||
		cmd_list_free(pr->cmdlist);
 | 
			
		||||
		return (0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
 | 
			
		||||
	if (item != NULL)
 | 
			
		||||
		cmdq_insert_after(item, new_item0);
 | 
			
		||||
	else
 | 
			
		||||
		cmdq_append(c, new_item0);
 | 
			
		||||
	cmd_list_free(pr->cmdlist);
 | 
			
		||||
 | 
			
		||||
	if (new_item != NULL)
 | 
			
		||||
		*new_item = item;
 | 
			
		||||
	return (found);
 | 
			
		||||
		*new_item = new_item0;
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
 
 | 
			
		||||
@@ -134,10 +134,10 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
 | 
			
		||||
    int done)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_command_prompt_cdata	*cdata = data;
 | 
			
		||||
	struct cmd_list			*cmdlist;
 | 
			
		||||
	struct cmdq_item		*new_item;
 | 
			
		||||
	char				*cause, *new_template, *prompt, *ptr;
 | 
			
		||||
	char				*new_template, *prompt, *ptr;
 | 
			
		||||
	char				*input = NULL;
 | 
			
		||||
	struct cmd_parse_result		*pr;
 | 
			
		||||
 | 
			
		||||
	if (s == NULL)
 | 
			
		||||
		return (0);
 | 
			
		||||
@@ -164,20 +164,22 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
 | 
			
		||||
		return (1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmdlist = cmd_string_parse(new_template, NULL, 0, &cause);
 | 
			
		||||
	if (cmdlist == NULL) {
 | 
			
		||||
		if (cause != NULL)
 | 
			
		||||
			new_item = cmdq_get_error(cause);
 | 
			
		||||
		else
 | 
			
		||||
	pr = cmd_parse_from_string(new_template, NULL);
 | 
			
		||||
	switch (pr->status) {
 | 
			
		||||
	case CMD_PARSE_EMPTY:
 | 
			
		||||
		new_item = NULL;
 | 
			
		||||
		free(cause);
 | 
			
		||||
	} else {
 | 
			
		||||
		new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
 | 
			
		||||
		cmd_list_free(cmdlist);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (new_item != NULL)
 | 
			
		||||
		break;
 | 
			
		||||
	case CMD_PARSE_ERROR:
 | 
			
		||||
		new_item = cmdq_get_error(pr->error);
 | 
			
		||||
		free(pr->error);
 | 
			
		||||
		cmdq_append(c, new_item);
 | 
			
		||||
		break;
 | 
			
		||||
	case CMD_PARSE_SUCCESS:
 | 
			
		||||
		new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
 | 
			
		||||
		cmd_list_free(pr->cmdlist);
 | 
			
		||||
		cmdq_append(c, new_item);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!done)
 | 
			
		||||
		free(new_template);
 | 
			
		||||
 
 | 
			
		||||
@@ -87,32 +87,33 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
 | 
			
		||||
    __unused int done)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_confirm_before_data	*cdata = data;
 | 
			
		||||
	struct cmd_list			*cmdlist;
 | 
			
		||||
	struct cmdq_item		*new_item;
 | 
			
		||||
	char				*cause;
 | 
			
		||||
	struct cmd_parse_result		*pr;
 | 
			
		||||
 | 
			
		||||
	if (c->flags & CLIENT_DEAD)
 | 
			
		||||
		return (0);
 | 
			
		||||
 | 
			
		||||
	if (s == NULL || *s == '\0')
 | 
			
		||||
		return (0);
 | 
			
		||||
	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
 | 
			
		||||
	if (tolower((u_char)s[0]) != 'y' || s[1] != '\0')
 | 
			
		||||
		return (0);
 | 
			
		||||
 | 
			
		||||
	cmdlist = cmd_string_parse(cdata->cmd, NULL, 0, &cause);
 | 
			
		||||
	if (cmdlist == NULL) {
 | 
			
		||||
		if (cause != NULL)
 | 
			
		||||
			new_item = cmdq_get_error(cause);
 | 
			
		||||
		else
 | 
			
		||||
	pr = cmd_parse_from_string(cdata->cmd, NULL);
 | 
			
		||||
	switch (pr->status) {
 | 
			
		||||
	case CMD_PARSE_EMPTY:
 | 
			
		||||
		new_item = NULL;
 | 
			
		||||
		free(cause);
 | 
			
		||||
	} else {
 | 
			
		||||
		new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
 | 
			
		||||
		cmd_list_free(cmdlist);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (new_item != NULL)
 | 
			
		||||
		break;
 | 
			
		||||
	case CMD_PARSE_ERROR:
 | 
			
		||||
		new_item = cmdq_get_error(pr->error);
 | 
			
		||||
		free(pr->error);
 | 
			
		||||
		cmdq_append(c, new_item);
 | 
			
		||||
		break;
 | 
			
		||||
	case CMD_PARSE_SUCCESS:
 | 
			
		||||
		new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
 | 
			
		||||
		cmd_list_free(pr->cmdlist);
 | 
			
		||||
		cmdq_append(c, new_item);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -197,11 +197,11 @@ static int
 | 
			
		||||
cmd_display_panes_key(struct client *c, struct key_event *event)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_display_panes_data	*cdata = c->overlay_data;
 | 
			
		||||
	struct cmd_list			*cmdlist;
 | 
			
		||||
	struct cmdq_item		*new_item;
 | 
			
		||||
	char				*cmd, *expanded, *cause;
 | 
			
		||||
	char				*cmd, *expanded;
 | 
			
		||||
	struct window			*w = c->session->curw->window;
 | 
			
		||||
	struct window_pane		*wp;
 | 
			
		||||
	struct cmd_parse_result		*pr;
 | 
			
		||||
 | 
			
		||||
	if (event->key < '0' || event->key > '9')
 | 
			
		||||
		return (1);
 | 
			
		||||
@@ -214,22 +214,21 @@ cmd_display_panes_key(struct client *c, struct key_event *event)
 | 
			
		||||
	xasprintf(&expanded, "%%%u", wp->id);
 | 
			
		||||
	cmd = cmd_template_replace(cdata->command, expanded, 1);
 | 
			
		||||
 | 
			
		||||
	cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);
 | 
			
		||||
	if (cmdlist == NULL) {
 | 
			
		||||
		if (cause != NULL)
 | 
			
		||||
			new_item = cmdq_get_error(cause);
 | 
			
		||||
		else
 | 
			
		||||
	pr = cmd_parse_from_string(cmd, NULL);
 | 
			
		||||
	switch (pr->status) {
 | 
			
		||||
	case CMD_PARSE_EMPTY:
 | 
			
		||||
		new_item = NULL;
 | 
			
		||||
		free(cause);
 | 
			
		||||
	} else {
 | 
			
		||||
		new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
 | 
			
		||||
		cmd_list_free(cmdlist);
 | 
			
		||||
	}
 | 
			
		||||
	if (new_item != NULL) {
 | 
			
		||||
		if (cdata->item != NULL)
 | 
			
		||||
			cmdq_insert_after(cdata->item, new_item);
 | 
			
		||||
		else
 | 
			
		||||
		break;
 | 
			
		||||
	case CMD_PARSE_ERROR:
 | 
			
		||||
		new_item = cmdq_get_error(pr->error);
 | 
			
		||||
		free(pr->error);
 | 
			
		||||
		cmdq_append(c, new_item);
 | 
			
		||||
		break;
 | 
			
		||||
	case CMD_PARSE_SUCCESS:
 | 
			
		||||
		new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
 | 
			
		||||
		cmd_list_free(pr->cmdlist);
 | 
			
		||||
		cmdq_append(c, new_item);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free(cmd);
 | 
			
		||||
 
 | 
			
		||||
@@ -49,8 +49,7 @@ const struct cmd_entry cmd_if_shell_entry = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct cmd_if_shell_data {
 | 
			
		||||
	char			*file;
 | 
			
		||||
	u_int			 line;
 | 
			
		||||
	struct cmd_parse_input	 input;
 | 
			
		||||
 | 
			
		||||
	char			*cmd_if;
 | 
			
		||||
	char			*cmd_else;
 | 
			
		||||
@@ -64,51 +63,62 @@ static enum cmd_retval
 | 
			
		||||
cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
{
 | 
			
		||||
	struct args			*args = self->args;
 | 
			
		||||
	struct cmdq_shared		*shared = item->shared;
 | 
			
		||||
	struct mouse_event		*m = &item->shared->mouse;
 | 
			
		||||
	struct cmd_if_shell_data	*cdata;
 | 
			
		||||
	char				*shellcmd, *cmd, *cause;
 | 
			
		||||
	struct cmd_list			*cmdlist;
 | 
			
		||||
	char				*shellcmd, *cmd;
 | 
			
		||||
	struct cmdq_item		*new_item;
 | 
			
		||||
	struct client			*c = cmd_find_client(item, NULL, 1);
 | 
			
		||||
	struct session			*s = item->target.s;
 | 
			
		||||
	struct winlink			*wl = item->target.wl;
 | 
			
		||||
	struct window_pane		*wp = item->target.wp;
 | 
			
		||||
	struct cmd_parse_input		 pi;
 | 
			
		||||
	struct cmd_parse_result		*pr;
 | 
			
		||||
 | 
			
		||||
	shellcmd = format_single(item, args->argv[0], c, s, wl, wp);
 | 
			
		||||
	if (args_has(args, 'F')) {
 | 
			
		||||
		cmd = NULL;
 | 
			
		||||
		if (*shellcmd != '0' && *shellcmd != '\0')
 | 
			
		||||
			cmd = args->argv[1];
 | 
			
		||||
		else if (args->argc == 3)
 | 
			
		||||
			cmd = args->argv[2];
 | 
			
		||||
		else
 | 
			
		||||
			cmd = NULL;
 | 
			
		||||
		free(shellcmd);
 | 
			
		||||
		if (cmd == NULL)
 | 
			
		||||
			return (CMD_RETURN_NORMAL);
 | 
			
		||||
		cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);
 | 
			
		||||
		if (cmdlist == NULL) {
 | 
			
		||||
			if (cause != NULL) {
 | 
			
		||||
				cmdq_error(item, "%s", cause);
 | 
			
		||||
				free(cause);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		memset(&pi, 0, sizeof pi);
 | 
			
		||||
		if (self->file != NULL)
 | 
			
		||||
			pi.file = self->file;
 | 
			
		||||
		pi.line = self->line;
 | 
			
		||||
		pi.item = item;
 | 
			
		||||
		pi.c = c;
 | 
			
		||||
		cmd_find_copy_state(&pi.fs, &item->target);
 | 
			
		||||
 | 
			
		||||
		pr = cmd_parse_from_string(cmd, &pi);
 | 
			
		||||
		switch (pr->status) {
 | 
			
		||||
		case CMD_PARSE_EMPTY:
 | 
			
		||||
			break;
 | 
			
		||||
		case CMD_PARSE_ERROR:
 | 
			
		||||
			cmdq_error(item, "%s", pr->error);
 | 
			
		||||
			free(pr->error);
 | 
			
		||||
			return (CMD_RETURN_ERROR);
 | 
			
		||||
		}
 | 
			
		||||
		new_item = cmdq_get_command(cmdlist, NULL, &shared->mouse, 0);
 | 
			
		||||
		case CMD_PARSE_SUCCESS:
 | 
			
		||||
			new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0);
 | 
			
		||||
			cmdq_insert_after(item, new_item);
 | 
			
		||||
		cmd_list_free(cmdlist);
 | 
			
		||||
			cmd_list_free(pr->cmdlist);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		return (CMD_RETURN_NORMAL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cdata = xcalloc(1, sizeof *cdata);
 | 
			
		||||
	if (self->file != NULL) {
 | 
			
		||||
		cdata->file = xstrdup(self->file);
 | 
			
		||||
		cdata->line = self->line;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cdata->cmd_if = xstrdup(args->argv[1]);
 | 
			
		||||
	if (args->argc == 3)
 | 
			
		||||
		cdata->cmd_else = xstrdup(args->argv[2]);
 | 
			
		||||
	else
 | 
			
		||||
		cdata->cmd_else = NULL;
 | 
			
		||||
	memcpy(&cdata->mouse, m, sizeof cdata->mouse);
 | 
			
		||||
 | 
			
		||||
	cdata->client = item->client;
 | 
			
		||||
	if (cdata->client != NULL)
 | 
			
		||||
@@ -118,7 +128,16 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
		cdata->item = item;
 | 
			
		||||
	else
 | 
			
		||||
		cdata->item = NULL;
 | 
			
		||||
	memcpy(&cdata->mouse, &shared->mouse, sizeof cdata->mouse);
 | 
			
		||||
 | 
			
		||||
	memset(&cdata->input, 0, sizeof cdata->input);
 | 
			
		||||
	if (self->file != NULL)
 | 
			
		||||
		cdata->input.file = xstrdup(self->file);
 | 
			
		||||
	cdata->input.line = self->line;
 | 
			
		||||
	cdata->input.item = cdata->item;
 | 
			
		||||
	cdata->input.c = c;
 | 
			
		||||
	if (cdata->input.c != NULL)
 | 
			
		||||
		cdata->input.c->references++;
 | 
			
		||||
	cmd_find_copy_state(&cdata->input.fs, &item->target);
 | 
			
		||||
 | 
			
		||||
	if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL,
 | 
			
		||||
	    cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) {
 | 
			
		||||
@@ -139,11 +158,11 @@ cmd_if_shell_callback(struct job *job)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_if_shell_data	*cdata = job_get_data(job);
 | 
			
		||||
	struct client			*c = cdata->client;
 | 
			
		||||
	struct cmd_list			*cmdlist;
 | 
			
		||||
	struct mouse_event		*m = &cdata->mouse;
 | 
			
		||||
	struct cmdq_item		*new_item;
 | 
			
		||||
	char				*cause, *cmd, *file = cdata->file;
 | 
			
		||||
	u_int				 line = cdata->line;
 | 
			
		||||
	char				*cmd;
 | 
			
		||||
	int				 status;
 | 
			
		||||
	struct cmd_parse_result		*pr;
 | 
			
		||||
 | 
			
		||||
	status = job_get_status(job);
 | 
			
		||||
	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
 | 
			
		||||
@@ -153,17 +172,20 @@ cmd_if_shell_callback(struct job *job)
 | 
			
		||||
	if (cmd == NULL)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	cmdlist = cmd_string_parse(cmd, file, line, &cause);
 | 
			
		||||
	if (cmdlist == NULL) {
 | 
			
		||||
		if (cause != NULL && cdata->item != NULL)
 | 
			
		||||
			cmdq_error(cdata->item, "%s", cause);
 | 
			
		||||
		free(cause);
 | 
			
		||||
	pr = cmd_parse_from_string(cmd, &cdata->input);
 | 
			
		||||
	switch (pr->status) {
 | 
			
		||||
	case CMD_PARSE_EMPTY:
 | 
			
		||||
		new_item = NULL;
 | 
			
		||||
	} else {
 | 
			
		||||
		new_item = cmdq_get_command(cmdlist, NULL, &cdata->mouse, 0);
 | 
			
		||||
		cmd_list_free(cmdlist);
 | 
			
		||||
		break;
 | 
			
		||||
	case CMD_PARSE_ERROR:
 | 
			
		||||
		new_item = cmdq_get_error(pr->error);
 | 
			
		||||
		free(pr->error);
 | 
			
		||||
		break;
 | 
			
		||||
	case CMD_PARSE_SUCCESS:
 | 
			
		||||
		new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0);
 | 
			
		||||
		cmd_list_free(pr->cmdlist);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (new_item != NULL) {
 | 
			
		||||
		if (cdata->item == NULL)
 | 
			
		||||
			cmdq_append(c, new_item);
 | 
			
		||||
@@ -187,6 +209,9 @@ cmd_if_shell_free(void *data)
 | 
			
		||||
	free(cdata->cmd_else);
 | 
			
		||||
	free(cdata->cmd_if);
 | 
			
		||||
 | 
			
		||||
	free(cdata->file);
 | 
			
		||||
	if (cdata->input.c != NULL)
 | 
			
		||||
		server_client_unref(cdata->input.c);
 | 
			
		||||
	free((void *)cdata->input.file);
 | 
			
		||||
 | 
			
		||||
	free(cdata);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								cmd-list.c
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								cmd-list.c
									
									
									
									
									
								
							@@ -23,17 +23,39 @@
 | 
			
		||||
 | 
			
		||||
#include "tmux.h"
 | 
			
		||||
 | 
			
		||||
static struct cmd_list *
 | 
			
		||||
static u_int cmd_list_next_group = 1;
 | 
			
		||||
 | 
			
		||||
struct cmd_list *
 | 
			
		||||
cmd_list_new(void)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_list	*cmdlist;
 | 
			
		||||
 | 
			
		||||
	cmdlist = xcalloc(1, sizeof *cmdlist);
 | 
			
		||||
	cmdlist->references = 1;
 | 
			
		||||
	cmdlist->group = cmd_list_next_group++;
 | 
			
		||||
	TAILQ_INIT(&cmdlist->list);
 | 
			
		||||
	return (cmdlist);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd)
 | 
			
		||||
{
 | 
			
		||||
	cmd->group = cmdlist->group;
 | 
			
		||||
	TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd	*cmd, *cmd1;
 | 
			
		||||
 | 
			
		||||
	TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) {
 | 
			
		||||
		TAILQ_REMOVE(&from->list, cmd, qentry);
 | 
			
		||||
		TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
 | 
			
		||||
	}
 | 
			
		||||
	cmdlist->group = cmd_list_next_group++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct cmd_list *
 | 
			
		||||
cmd_list_parse(int argc, char **argv, const char *file, u_int line,
 | 
			
		||||
    char **cause)
 | 
			
		||||
@@ -100,9 +122,7 @@ cmd_list_free(struct cmd_list *cmdlist)
 | 
			
		||||
 | 
			
		||||
	TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) {
 | 
			
		||||
		TAILQ_REMOVE(&cmdlist->list, cmd, qentry);
 | 
			
		||||
		args_free(cmd->args);
 | 
			
		||||
		free(cmd->file);
 | 
			
		||||
		free(cmd);
 | 
			
		||||
		cmd_free(cmd);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free(cmdlist);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1207
									
								
								cmd-parse.y
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1207
									
								
								cmd-parse.y
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										14
									
								
								cmd-queue.c
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								cmd-queue.c
									
									
									
									
									
								
							@@ -175,15 +175,6 @@ cmdq_remove(struct cmdq_item *item)
 | 
			
		||||
	free(item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Set command group. */
 | 
			
		||||
static u_int
 | 
			
		||||
cmdq_next_group(void)
 | 
			
		||||
{
 | 
			
		||||
	static u_int	group;
 | 
			
		||||
 | 
			
		||||
	return (++group);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Remove all subsequent items that match this item's group. */
 | 
			
		||||
static void
 | 
			
		||||
cmdq_remove_group(struct cmdq_item *item)
 | 
			
		||||
@@ -206,7 +197,6 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
 | 
			
		||||
{
 | 
			
		||||
	struct cmdq_item	*item, *first = NULL, *last = NULL;
 | 
			
		||||
	struct cmd		*cmd;
 | 
			
		||||
	u_int			 group = cmdq_next_group();
 | 
			
		||||
	struct cmdq_shared	*shared;
 | 
			
		||||
 | 
			
		||||
	shared = xcalloc(1, sizeof *shared);
 | 
			
		||||
@@ -222,13 +212,15 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
 | 
			
		||||
		xasprintf(&item->name, "[%s/%p]", cmd->entry->name, item);
 | 
			
		||||
		item->type = CMDQ_COMMAND;
 | 
			
		||||
 | 
			
		||||
		item->group = group;
 | 
			
		||||
		item->group = cmd->group;
 | 
			
		||||
		item->flags = flags;
 | 
			
		||||
 | 
			
		||||
		item->shared = shared;
 | 
			
		||||
		item->cmdlist = cmdlist;
 | 
			
		||||
		item->cmd = cmd;
 | 
			
		||||
 | 
			
		||||
		log_debug("%s: %s group %u", __func__, item->name, item->group);
 | 
			
		||||
 | 
			
		||||
		shared->references++;
 | 
			
		||||
		cmdlist->references++;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,8 +37,8 @@ const struct cmd_entry cmd_source_file_entry = {
 | 
			
		||||
	.name = "source-file",
 | 
			
		||||
	.alias = "source",
 | 
			
		||||
 | 
			
		||||
	.args = { "q", 1, 1 },
 | 
			
		||||
	.usage = "[-q] path",
 | 
			
		||||
	.args = { "nq", 1, 1 },
 | 
			
		||||
	.usage = "[-nq] path",
 | 
			
		||||
 | 
			
		||||
	.flags = 0,
 | 
			
		||||
	.exec = cmd_source_file_exec
 | 
			
		||||
@@ -58,7 +58,9 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
	u_int			 i;
 | 
			
		||||
 | 
			
		||||
	if (args_has(args, 'q'))
 | 
			
		||||
		flags |= CFG_QUIET;
 | 
			
		||||
		flags |= CMD_PARSE_QUIET;
 | 
			
		||||
	if (args_has(args, 'n'))
 | 
			
		||||
		flags |= CMD_PARSE_PARSEONLY;
 | 
			
		||||
 | 
			
		||||
	if (*path == '/')
 | 
			
		||||
		pattern = xstrdup(path);
 | 
			
		||||
@@ -71,7 +73,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
 | 
			
		||||
 | 
			
		||||
	retval = CMD_RETURN_NORMAL;
 | 
			
		||||
	if (glob(pattern, 0, NULL, &g) != 0) {
 | 
			
		||||
		if (errno != ENOENT || (~flags & CFG_QUIET)) {
 | 
			
		||||
		if (errno != ENOENT || (~flags & CMD_PARSE_QUIET)) {
 | 
			
		||||
			cmdq_error(item, "%s: %s", path, strerror(errno));
 | 
			
		||||
			retval = CMD_RETURN_ERROR;
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										393
									
								
								cmd-string.c
									
									
									
									
									
								
							
							
						
						
									
										393
									
								
								cmd-string.c
									
									
									
									
									
								
							@@ -1,393 +0,0 @@
 | 
			
		||||
/* $OpenBSD$ */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
 | 
			
		||||
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <pwd.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "tmux.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Parse a command from a string.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static int	 cmd_string_getc(const char *, size_t *);
 | 
			
		||||
static void	 cmd_string_ungetc(size_t *);
 | 
			
		||||
static void	 cmd_string_copy(char **, char *, size_t *);
 | 
			
		||||
static char	*cmd_string_string(const char *, size_t *, char, int);
 | 
			
		||||
static char	*cmd_string_variable(const char *, size_t *);
 | 
			
		||||
static char	*cmd_string_expand_tilde(const char *, size_t *);
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
cmd_string_getc(const char *s, size_t *p)
 | 
			
		||||
{
 | 
			
		||||
	const u_char	*ucs = s;
 | 
			
		||||
 | 
			
		||||
	if (ucs[*p] == '\0')
 | 
			
		||||
		return (EOF);
 | 
			
		||||
	return (ucs[(*p)++]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
cmd_string_ungetc(size_t *p)
 | 
			
		||||
{
 | 
			
		||||
	(*p)--;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
cmd_string_unicode(wchar_t *wc, const char *s, size_t *p, char ch)
 | 
			
		||||
{
 | 
			
		||||
	int	size = (ch == 'u') ? 4 : 8;
 | 
			
		||||
	u_int	tmp;
 | 
			
		||||
 | 
			
		||||
	if (size == 4 && sscanf(s + *p, "%4x", &tmp) != 1)
 | 
			
		||||
		return (-1);
 | 
			
		||||
	if (size == 8 && sscanf(s + *p, "%8x", &tmp) != 1)
 | 
			
		||||
		return (-1);
 | 
			
		||||
	*p += size;
 | 
			
		||||
 | 
			
		||||
	*wc = (wchar_t)tmp;
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
cmd_string_split(const char *s, int *rargc, char ***rargv)
 | 
			
		||||
{
 | 
			
		||||
	size_t		p = 0;
 | 
			
		||||
	int		ch, argc = 0, append = 0;
 | 
			
		||||
	char	      **argv = NULL, *buf = NULL, *t;
 | 
			
		||||
	const char     *whitespace, *equals;
 | 
			
		||||
	size_t		len = 0;
 | 
			
		||||
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		ch = cmd_string_getc(s, &p);
 | 
			
		||||
		switch (ch) {
 | 
			
		||||
		case '\'':
 | 
			
		||||
			if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL)
 | 
			
		||||
				goto error;
 | 
			
		||||
			cmd_string_copy(&buf, t, &len);
 | 
			
		||||
			break;
 | 
			
		||||
		case '"':
 | 
			
		||||
			if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
 | 
			
		||||
				goto error;
 | 
			
		||||
			cmd_string_copy(&buf, t, &len);
 | 
			
		||||
			break;
 | 
			
		||||
		case '$':
 | 
			
		||||
			if ((t = cmd_string_variable(s, &p)) == NULL)
 | 
			
		||||
				goto error;
 | 
			
		||||
			cmd_string_copy(&buf, t, &len);
 | 
			
		||||
			break;
 | 
			
		||||
		case '#':
 | 
			
		||||
			/* Comment: discard rest of line. */
 | 
			
		||||
			while ((ch = cmd_string_getc(s, &p)) != EOF)
 | 
			
		||||
				;
 | 
			
		||||
			/* FALLTHROUGH */
 | 
			
		||||
		case EOF:
 | 
			
		||||
		case ' ':
 | 
			
		||||
		case '\t':
 | 
			
		||||
			if (buf != NULL) {
 | 
			
		||||
				buf = xrealloc(buf, len + 1);
 | 
			
		||||
				buf[len] = '\0';
 | 
			
		||||
 | 
			
		||||
				argv = xreallocarray(argv, argc + 1,
 | 
			
		||||
				    sizeof *argv);
 | 
			
		||||
				argv[argc++] = buf;
 | 
			
		||||
 | 
			
		||||
				buf = NULL;
 | 
			
		||||
				len = 0;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (ch != EOF)
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			while (argc != 0) {
 | 
			
		||||
				equals = strchr(argv[0], '=');
 | 
			
		||||
				whitespace = argv[0] + strcspn(argv[0], " \t");
 | 
			
		||||
				if (equals == NULL || equals > whitespace)
 | 
			
		||||
					break;
 | 
			
		||||
				environ_put(global_environ, argv[0]);
 | 
			
		||||
				argc--;
 | 
			
		||||
				memmove(argv, argv + 1, argc * (sizeof *argv));
 | 
			
		||||
			}
 | 
			
		||||
			goto done;
 | 
			
		||||
		case '~':
 | 
			
		||||
			if (buf != NULL) {
 | 
			
		||||
				append = 1;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			t = cmd_string_expand_tilde(s, &p);
 | 
			
		||||
			if (t == NULL)
 | 
			
		||||
				goto error;
 | 
			
		||||
			cmd_string_copy(&buf, t, &len);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			append = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		if (append) {
 | 
			
		||||
			if (len >= SIZE_MAX - 2)
 | 
			
		||||
				goto error;
 | 
			
		||||
			buf = xrealloc(buf, len + 1);
 | 
			
		||||
			buf[len++] = ch;
 | 
			
		||||
		}
 | 
			
		||||
		append = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	*rargc = argc;
 | 
			
		||||
	*rargv = argv;
 | 
			
		||||
 | 
			
		||||
	free(buf);
 | 
			
		||||
	return (0);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	if (argv != NULL)
 | 
			
		||||
		cmd_free_argv(argc, argv);
 | 
			
		||||
	free(buf);
 | 
			
		||||
	return (-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct cmd_list *
 | 
			
		||||
cmd_string_parse(const char *s, const char *file, u_int line, char **cause)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_list	 *cmdlist = NULL;
 | 
			
		||||
	int		  argc;
 | 
			
		||||
	char		**argv;
 | 
			
		||||
 | 
			
		||||
	if (cause != NULL)
 | 
			
		||||
		*cause = NULL;
 | 
			
		||||
	log_debug ("%s: %s", __func__, s);
 | 
			
		||||
 | 
			
		||||
	if (cmd_string_split(s, &argc, &argv) != 0) {
 | 
			
		||||
		xasprintf(cause, "invalid or unknown command: %s", s);
 | 
			
		||||
		return (NULL);
 | 
			
		||||
	}
 | 
			
		||||
	if (argc != 0) {
 | 
			
		||||
		cmdlist = cmd_list_parse(argc, argv, file, line, cause);
 | 
			
		||||
		if (cmdlist == NULL) {
 | 
			
		||||
			cmd_free_argv(argc, argv);
 | 
			
		||||
			return (NULL);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	cmd_free_argv(argc, argv);
 | 
			
		||||
	return (cmdlist);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
cmd_string_copy(char **dst, char *src, size_t *len)
 | 
			
		||||
{
 | 
			
		||||
	size_t srclen;
 | 
			
		||||
 | 
			
		||||
	srclen = strlen(src);
 | 
			
		||||
 | 
			
		||||
	*dst = xrealloc(*dst, *len + srclen + 1);
 | 
			
		||||
	strlcpy(*dst + *len, src, srclen + 1);
 | 
			
		||||
 | 
			
		||||
	*len += srclen;
 | 
			
		||||
	free(src);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
cmd_string_string(const char *s, size_t *p, char endch, int esc)
 | 
			
		||||
{
 | 
			
		||||
	int			ch;
 | 
			
		||||
	wchar_t			wc;
 | 
			
		||||
	struct utf8_data	ud;
 | 
			
		||||
	char		       *buf = NULL, *t;
 | 
			
		||||
	size_t			len = 0;
 | 
			
		||||
 | 
			
		||||
	while ((ch = cmd_string_getc(s, p)) != endch) {
 | 
			
		||||
		switch (ch) {
 | 
			
		||||
		case EOF:
 | 
			
		||||
			goto error;
 | 
			
		||||
		case '\\':
 | 
			
		||||
			if (!esc)
 | 
			
		||||
				break;
 | 
			
		||||
			switch (ch = cmd_string_getc(s, p)) {
 | 
			
		||||
			case EOF:
 | 
			
		||||
				goto error;
 | 
			
		||||
			case 'e':
 | 
			
		||||
				ch = '\033';
 | 
			
		||||
				break;
 | 
			
		||||
			case 'r':
 | 
			
		||||
				ch = '\r';
 | 
			
		||||
				break;
 | 
			
		||||
			case 'n':
 | 
			
		||||
				ch = '\n';
 | 
			
		||||
				break;
 | 
			
		||||
			case 't':
 | 
			
		||||
				ch = '\t';
 | 
			
		||||
				break;
 | 
			
		||||
			case 'u':
 | 
			
		||||
			case 'U':
 | 
			
		||||
				if (cmd_string_unicode(&wc, s, p, ch) != 0)
 | 
			
		||||
					goto error;
 | 
			
		||||
				if (utf8_split(wc, &ud) != UTF8_DONE)
 | 
			
		||||
					goto error;
 | 
			
		||||
				if (len >= SIZE_MAX - ud.size - 1)
 | 
			
		||||
					goto error;
 | 
			
		||||
				buf = xrealloc(buf, len + ud.size);
 | 
			
		||||
				memcpy(buf + len, ud.data, ud.size);
 | 
			
		||||
				len += ud.size;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case '$':
 | 
			
		||||
			if (!esc)
 | 
			
		||||
				break;
 | 
			
		||||
			if ((t = cmd_string_variable(s, p)) == NULL)
 | 
			
		||||
				goto error;
 | 
			
		||||
			cmd_string_copy(&buf, t, &len);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (len >= SIZE_MAX - 2)
 | 
			
		||||
			goto error;
 | 
			
		||||
		buf = xrealloc(buf, len + 1);
 | 
			
		||||
		buf[len++] = ch;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf = xrealloc(buf, len + 1);
 | 
			
		||||
	buf[len] = '\0';
 | 
			
		||||
	return (buf);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	free(buf);
 | 
			
		||||
	return (NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
cmd_string_variable(const char *s, size_t *p)
 | 
			
		||||
{
 | 
			
		||||
	int			ch, fch;
 | 
			
		||||
	char		       *buf, *t;
 | 
			
		||||
	size_t			len;
 | 
			
		||||
	struct environ_entry   *envent;
 | 
			
		||||
 | 
			
		||||
#define cmd_string_first(ch) ((ch) == '_' || \
 | 
			
		||||
	((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
 | 
			
		||||
#define cmd_string_other(ch) ((ch) == '_' || \
 | 
			
		||||
	((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
 | 
			
		||||
	((ch) >= '0' && (ch) <= '9'))
 | 
			
		||||
 | 
			
		||||
	buf = NULL;
 | 
			
		||||
	len = 0;
 | 
			
		||||
 | 
			
		||||
	fch = EOF;
 | 
			
		||||
	switch (ch = cmd_string_getc(s, p)) {
 | 
			
		||||
	case EOF:
 | 
			
		||||
		goto error;
 | 
			
		||||
	case '{':
 | 
			
		||||
		fch = '{';
 | 
			
		||||
 | 
			
		||||
		ch = cmd_string_getc(s, p);
 | 
			
		||||
		if (!cmd_string_first(ch))
 | 
			
		||||
			goto error;
 | 
			
		||||
		/* FALLTHROUGH */
 | 
			
		||||
	default:
 | 
			
		||||
		if (!cmd_string_first(ch)) {
 | 
			
		||||
			xasprintf(&t, "$%c", ch);
 | 
			
		||||
			return (t);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		buf = xrealloc(buf, len + 1);
 | 
			
		||||
		buf[len++] = ch;
 | 
			
		||||
 | 
			
		||||
		for (;;) {
 | 
			
		||||
			ch = cmd_string_getc(s, p);
 | 
			
		||||
			if (ch == EOF || !cmd_string_other(ch))
 | 
			
		||||
				break;
 | 
			
		||||
			else {
 | 
			
		||||
				if (len >= SIZE_MAX - 3)
 | 
			
		||||
					goto error;
 | 
			
		||||
				buf = xrealloc(buf, len + 1);
 | 
			
		||||
				buf[len++] = ch;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (fch == '{' && ch != '}')
 | 
			
		||||
		goto error;
 | 
			
		||||
	if (ch != EOF && fch != '{')
 | 
			
		||||
		cmd_string_ungetc(p); /* ch */
 | 
			
		||||
 | 
			
		||||
	buf = xrealloc(buf, len + 1);
 | 
			
		||||
	buf[len] = '\0';
 | 
			
		||||
 | 
			
		||||
	envent = environ_find(global_environ, buf);
 | 
			
		||||
	free(buf);
 | 
			
		||||
	if (envent == NULL)
 | 
			
		||||
		return (xstrdup(""));
 | 
			
		||||
	return (xstrdup(envent->value));
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	free(buf);
 | 
			
		||||
	return (NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
cmd_string_expand_tilde(const char *s, size_t *p)
 | 
			
		||||
{
 | 
			
		||||
	struct passwd		*pw;
 | 
			
		||||
	struct environ_entry	*envent;
 | 
			
		||||
	char			*home, *path, *user, *cp;
 | 
			
		||||
	int			 last;
 | 
			
		||||
 | 
			
		||||
	home = NULL;
 | 
			
		||||
 | 
			
		||||
	last = cmd_string_getc(s, p);
 | 
			
		||||
	if (last == EOF || last == '/' || last == ' '|| last == '\t') {
 | 
			
		||||
		envent = environ_find(global_environ, "HOME");
 | 
			
		||||
		if (envent != NULL && *envent->value != '\0')
 | 
			
		||||
			home = envent->value;
 | 
			
		||||
		else if ((pw = getpwuid(getuid())) != NULL)
 | 
			
		||||
			home = pw->pw_dir;
 | 
			
		||||
	} else {
 | 
			
		||||
		cmd_string_ungetc(p);
 | 
			
		||||
 | 
			
		||||
		cp = user = xmalloc(strlen(s));
 | 
			
		||||
		for (;;) {
 | 
			
		||||
			last = cmd_string_getc(s, p);
 | 
			
		||||
			if (last == EOF ||
 | 
			
		||||
			    last == '/' ||
 | 
			
		||||
			    last == ' '||
 | 
			
		||||
			    last == '\t')
 | 
			
		||||
				break;
 | 
			
		||||
			*cp++ = last;
 | 
			
		||||
		}
 | 
			
		||||
		*cp = '\0';
 | 
			
		||||
 | 
			
		||||
		if ((pw = getpwnam(user)) != NULL)
 | 
			
		||||
			home = pw->pw_dir;
 | 
			
		||||
		free(user);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (home == NULL)
 | 
			
		||||
		return (NULL);
 | 
			
		||||
 | 
			
		||||
	if (last != EOF)
 | 
			
		||||
		xasprintf(&path, "%s%c", home, last);
 | 
			
		||||
	else
 | 
			
		||||
		xasprintf(&path, "%s", home);
 | 
			
		||||
	return (path);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										182
									
								
								cmd.c
									
									
									
									
									
								
							
							
						
						
									
										182
									
								
								cmd.c
									
									
									
									
									
								
							@@ -213,6 +213,29 @@ cmd_log_argv(int argc, char **argv, const char *prefix)
 | 
			
		||||
		log_debug("%s: argv[%d]=%s", prefix, i, argv[i]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
cmd_prepend_argv(int *argc, char ***argv, char *arg)
 | 
			
		||||
{
 | 
			
		||||
	char	**new_argv;
 | 
			
		||||
	int	  i;
 | 
			
		||||
 | 
			
		||||
	new_argv = xreallocarray(NULL, (*argc) + 1, sizeof *new_argv);
 | 
			
		||||
	new_argv[0] = xstrdup(arg);
 | 
			
		||||
	for (i = 0; i < *argc; i++)
 | 
			
		||||
		new_argv[1 + i] = (*argv)[i];
 | 
			
		||||
 | 
			
		||||
	free(*argv);
 | 
			
		||||
	*argv = new_argv;
 | 
			
		||||
	(*argc)++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
cmd_append_argv(int *argc, char ***argv, char *arg)
 | 
			
		||||
{
 | 
			
		||||
	*argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv);
 | 
			
		||||
	(*argv)[(*argc)++] = xstrdup(arg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
 | 
			
		||||
{
 | 
			
		||||
@@ -317,105 +340,102 @@ cmd_stringify_argv(int argc, char **argv)
 | 
			
		||||
	return (buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
cmd_try_alias(int *argc, char ***argv)
 | 
			
		||||
char *
 | 
			
		||||
cmd_get_alias(const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct options_entry		*o;
 | 
			
		||||
	struct options_array_item	*a;
 | 
			
		||||
	union options_value		*ov;
 | 
			
		||||
	int				  old_argc = *argc, new_argc, i;
 | 
			
		||||
	char				**old_argv = *argv, **new_argv;
 | 
			
		||||
	size_t				  wanted;
 | 
			
		||||
	const char			 *cp = NULL;
 | 
			
		||||
	size_t				 wanted, n;
 | 
			
		||||
	const char			*equals;
 | 
			
		||||
 | 
			
		||||
	o = options_get_only(global_options, "command-alias");
 | 
			
		||||
	if (o == NULL)
 | 
			
		||||
		return (-1);
 | 
			
		||||
	wanted = strlen(old_argv[0]);
 | 
			
		||||
		return (NULL);
 | 
			
		||||
	wanted = strlen(name);
 | 
			
		||||
 | 
			
		||||
	a = options_array_first(o);
 | 
			
		||||
	while (a != NULL) {
 | 
			
		||||
		ov = options_array_item_value(a);
 | 
			
		||||
		cp = strchr(ov->string, '=');
 | 
			
		||||
		if (cp != NULL &&
 | 
			
		||||
		    (size_t)(cp - ov->string) == wanted &&
 | 
			
		||||
		    strncmp(old_argv[0], ov->string, wanted) == 0)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		equals = strchr(ov->string, '=');
 | 
			
		||||
		if (equals != NULL) {
 | 
			
		||||
			n = equals - ov->string;
 | 
			
		||||
			if (n == wanted && strncmp(name, ov->string, n) == 0)
 | 
			
		||||
				return (xstrdup(equals + 1));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		a = options_array_next(a);
 | 
			
		||||
	}
 | 
			
		||||
	if (a == NULL)
 | 
			
		||||
		return (-1);
 | 
			
		||||
	return (NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	if (cmd_string_split(cp + 1, &new_argc, &new_argv) != 0)
 | 
			
		||||
		return (-1);
 | 
			
		||||
static const struct cmd_entry *
 | 
			
		||||
cmd_find(const char *name, char **cause)
 | 
			
		||||
{
 | 
			
		||||
	const struct cmd_entry **loop, *entry, *found = NULL;
 | 
			
		||||
	int			 ambiguous;
 | 
			
		||||
	char			 s[BUFSIZ];
 | 
			
		||||
 | 
			
		||||
	*argc = new_argc + old_argc - 1;
 | 
			
		||||
	*argv = xcalloc((*argc) + 1, sizeof **argv);
 | 
			
		||||
	ambiguous = 0;
 | 
			
		||||
	for (loop = cmd_table; *loop != NULL; loop++) {
 | 
			
		||||
		entry = *loop;
 | 
			
		||||
		if (entry->alias != NULL && strcmp(entry->alias, name) == 0) {
 | 
			
		||||
			ambiguous = 0;
 | 
			
		||||
			found = entry;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < new_argc; i++)
 | 
			
		||||
		(*argv)[i] = xstrdup(new_argv[i]);
 | 
			
		||||
	for (i = 1; i < old_argc; i++)
 | 
			
		||||
		(*argv)[new_argc + i - 1] = xstrdup(old_argv[i]);
 | 
			
		||||
		if (strncmp(entry->name, name, strlen(name)) != 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (found != NULL)
 | 
			
		||||
			ambiguous = 1;
 | 
			
		||||
		found = entry;
 | 
			
		||||
 | 
			
		||||
	log_debug("alias: %s=%s", old_argv[0], cp + 1);
 | 
			
		||||
	for (i = 0; i < *argc; i++)
 | 
			
		||||
		log_debug("alias: argv[%d] = %s", i, (*argv)[i]);
 | 
			
		||||
		if (strcmp(entry->name, name) == 0)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	if (ambiguous)
 | 
			
		||||
		goto ambiguous;
 | 
			
		||||
	if (found == NULL) {
 | 
			
		||||
		xasprintf(cause, "unknown command: %s", name);
 | 
			
		||||
		return (NULL);
 | 
			
		||||
	}
 | 
			
		||||
	return (found);
 | 
			
		||||
 | 
			
		||||
	cmd_free_argv(new_argc, new_argv);
 | 
			
		||||
	return (0);
 | 
			
		||||
ambiguous:
 | 
			
		||||
	*s = '\0';
 | 
			
		||||
	for (loop = cmd_table; *loop != NULL; loop++) {
 | 
			
		||||
		entry = *loop;
 | 
			
		||||
		if (strncmp(entry->name, name, strlen(name)) != 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (strlcat(s, entry->name, sizeof s) >= sizeof s)
 | 
			
		||||
			break;
 | 
			
		||||
		if (strlcat(s, ", ", sizeof s) >= sizeof s)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	s[strlen(s) - 2] = '\0';
 | 
			
		||||
	xasprintf(cause, "ambiguous command: %s, could be: %s", name, s);
 | 
			
		||||
	return (NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct cmd *
 | 
			
		||||
cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause)
 | 
			
		||||
{
 | 
			
		||||
	const struct cmd_entry	*entry;
 | 
			
		||||
	const char		*name;
 | 
			
		||||
	const struct cmd_entry **entryp, *entry;
 | 
			
		||||
	struct cmd		*cmd;
 | 
			
		||||
	struct args		*args;
 | 
			
		||||
	char			 s[BUFSIZ];
 | 
			
		||||
	int			 ambiguous, allocated = 0;
 | 
			
		||||
 | 
			
		||||
	*cause = NULL;
 | 
			
		||||
	if (argc == 0) {
 | 
			
		||||
		xasprintf(cause, "no command");
 | 
			
		||||
		return (NULL);
 | 
			
		||||
	}
 | 
			
		||||
	name = argv[0];
 | 
			
		||||
 | 
			
		||||
retry:
 | 
			
		||||
	ambiguous = 0;
 | 
			
		||||
	entry = NULL;
 | 
			
		||||
	for (entryp = cmd_table; *entryp != NULL; entryp++) {
 | 
			
		||||
		if ((*entryp)->alias != NULL &&
 | 
			
		||||
		    strcmp((*entryp)->alias, argv[0]) == 0) {
 | 
			
		||||
			ambiguous = 0;
 | 
			
		||||
			entry = *entryp;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (entry != NULL)
 | 
			
		||||
			ambiguous = 1;
 | 
			
		||||
		entry = *entryp;
 | 
			
		||||
 | 
			
		||||
		/* Bail now if an exact match. */
 | 
			
		||||
		if (strcmp(entry->name, argv[0]) == 0)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	if ((ambiguous || entry == NULL) &&
 | 
			
		||||
	    server_proc != NULL &&
 | 
			
		||||
	    !allocated &&
 | 
			
		||||
	    cmd_try_alias(&argc, &argv) == 0) {
 | 
			
		||||
		allocated = 1;
 | 
			
		||||
		goto retry;
 | 
			
		||||
	}
 | 
			
		||||
	if (ambiguous)
 | 
			
		||||
		goto ambiguous;
 | 
			
		||||
	if (entry == NULL) {
 | 
			
		||||
		xasprintf(cause, "unknown command: %s", name);
 | 
			
		||||
	entry = cmd_find(name, cause);
 | 
			
		||||
	if (entry == NULL)
 | 
			
		||||
		return (NULL);
 | 
			
		||||
	}
 | 
			
		||||
	cmd_log_argv(argc, argv, entry->name);
 | 
			
		||||
 | 
			
		||||
	args = args_parse(entry->args.template, argc, argv);
 | 
			
		||||
@@ -434,23 +454,11 @@ retry:
 | 
			
		||||
		cmd->file = xstrdup(file);
 | 
			
		||||
	cmd->line = line;
 | 
			
		||||
 | 
			
		||||
	if (allocated)
 | 
			
		||||
		cmd_free_argv(argc, argv);
 | 
			
		||||
	return (cmd);
 | 
			
		||||
	cmd->alias = NULL;
 | 
			
		||||
	cmd->argc = argc;
 | 
			
		||||
	cmd->argv = cmd_copy_argv(argc, argv);
 | 
			
		||||
 | 
			
		||||
ambiguous:
 | 
			
		||||
	*s = '\0';
 | 
			
		||||
	for (entryp = cmd_table; *entryp != NULL; entryp++) {
 | 
			
		||||
		if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s)
 | 
			
		||||
			break;
 | 
			
		||||
		if (strlcat(s, ", ", sizeof s) >= sizeof s)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	s[strlen(s) - 2] = '\0';
 | 
			
		||||
	xasprintf(cause, "ambiguous command: %s, could be: %s", name, s);
 | 
			
		||||
	return (NULL);
 | 
			
		||||
	return (cmd);
 | 
			
		||||
 | 
			
		||||
usage:
 | 
			
		||||
	if (args != NULL)
 | 
			
		||||
@@ -459,6 +467,18 @@ usage:
 | 
			
		||||
	return (NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
cmd_free(struct cmd *cmd)
 | 
			
		||||
{
 | 
			
		||||
	free(cmd->alias);
 | 
			
		||||
	cmd_free_argv(cmd->argc, cmd->argv);
 | 
			
		||||
 | 
			
		||||
	free(cmd->file);
 | 
			
		||||
 | 
			
		||||
	args_free(cmd->args);
 | 
			
		||||
	free(cmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *
 | 
			
		||||
cmd_print(struct cmd *cmd)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								control.c
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								control.c
									
									
									
									
									
								
							@@ -68,9 +68,9 @@ control_error(struct cmdq_item *item, void *data)
 | 
			
		||||
void
 | 
			
		||||
control_callback(struct client *c, int closed, __unused void *data)
 | 
			
		||||
{
 | 
			
		||||
	char			*line, *cause;
 | 
			
		||||
	struct cmd_list		*cmdlist;
 | 
			
		||||
	char			*line;
 | 
			
		||||
	struct cmdq_item	*item;
 | 
			
		||||
	struct cmd_parse_result	*pr;
 | 
			
		||||
 | 
			
		||||
	if (closed)
 | 
			
		||||
		c->flags |= CLIENT_EXIT;
 | 
			
		||||
@@ -84,15 +84,21 @@ control_callback(struct client *c, int closed, __unused void *data)
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cmdlist = cmd_string_parse(line, NULL, 0, &cause);
 | 
			
		||||
		if (cmdlist == NULL) {
 | 
			
		||||
			item = cmdq_get_callback(control_error, cause);
 | 
			
		||||
		pr = cmd_parse_from_string(line, NULL);
 | 
			
		||||
		switch (pr->status) {
 | 
			
		||||
		case CMD_PARSE_EMPTY:
 | 
			
		||||
			break;
 | 
			
		||||
		case CMD_PARSE_ERROR:
 | 
			
		||||
			item = cmdq_get_callback(control_error, pr->error);
 | 
			
		||||
			cmdq_append(c, item);
 | 
			
		||||
		} else {
 | 
			
		||||
			item = cmdq_get_command(cmdlist, NULL, NULL, 0);
 | 
			
		||||
			free(pr->error);
 | 
			
		||||
			break;
 | 
			
		||||
		case CMD_PARSE_SUCCESS:
 | 
			
		||||
			item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
 | 
			
		||||
			item->shared->flags |= CMDQ_SHARED_CONTROL;
 | 
			
		||||
			cmdq_append(c, item);
 | 
			
		||||
			cmd_list_free(cmdlist);
 | 
			
		||||
			cmd_list_free(pr->cmdlist);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		free(line);
 | 
			
		||||
 
 | 
			
		||||
@@ -434,15 +434,14 @@ key_bindings_init(void)
 | 
			
		||||
		"bind -Tcopy-mode-vi C-Down send -X scroll-down",
 | 
			
		||||
	};
 | 
			
		||||
	u_int			 i;
 | 
			
		||||
	struct cmd_list	*cmdlist;
 | 
			
		||||
	char		*cause;
 | 
			
		||||
	struct cmd_parse_result	*pr;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < nitems(defaults); i++) {
 | 
			
		||||
		cmdlist = cmd_string_parse(defaults[i], "<default>", i, &cause);
 | 
			
		||||
		if (cmdlist == NULL)
 | 
			
		||||
		pr = cmd_parse_from_string(defaults[i], NULL);
 | 
			
		||||
		if (pr->status != CMD_PARSE_SUCCESS)
 | 
			
		||||
			fatalx("bad default key: %s", defaults[i]);
 | 
			
		||||
		cmdq_append(NULL, cmdq_get_command(cmdlist, NULL, NULL, 0));
 | 
			
		||||
		cmd_list_free(cmdlist);
 | 
			
		||||
		cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL, NULL, 0));
 | 
			
		||||
		cmd_list_free(pr->cmdlist);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								menu.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								menu.c
									
									
									
									
									
								
							@@ -200,9 +200,8 @@ menu_key_cb(struct client *c, struct key_event *event)
 | 
			
		||||
	u_int				 i;
 | 
			
		||||
	int				 count = menu->count, old = md->choice;
 | 
			
		||||
	const struct menu_item		*item;
 | 
			
		||||
	struct cmd_list			*cmdlist;
 | 
			
		||||
	struct cmdq_item		*new_item;
 | 
			
		||||
	char				*cause;
 | 
			
		||||
	struct cmd_parse_result		*pr;
 | 
			
		||||
 | 
			
		||||
	if (KEYC_IS_MOUSE(event->key)) {
 | 
			
		||||
		if (md->flags & MENU_NOMOUSE)
 | 
			
		||||
@@ -272,22 +271,22 @@ chosen:
 | 
			
		||||
	    md->cb = NULL;
 | 
			
		||||
	    return (1);
 | 
			
		||||
	}
 | 
			
		||||
	cmdlist = cmd_string_parse(item->command, NULL, 0, &cause);
 | 
			
		||||
	if (cmdlist == NULL) {
 | 
			
		||||
		if (cause != NULL)
 | 
			
		||||
			new_item = cmdq_get_error(cause);
 | 
			
		||||
		else
 | 
			
		||||
 | 
			
		||||
	pr = cmd_parse_from_string(item->command, NULL);
 | 
			
		||||
	switch (pr->status) {
 | 
			
		||||
	case CMD_PARSE_EMPTY:
 | 
			
		||||
		new_item = NULL;
 | 
			
		||||
		free(cause);
 | 
			
		||||
	} else {
 | 
			
		||||
		new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
 | 
			
		||||
		cmd_list_free(cmdlist);
 | 
			
		||||
	}
 | 
			
		||||
	if (new_item != NULL) {
 | 
			
		||||
		if (md->item != NULL)
 | 
			
		||||
			cmdq_insert_after(md->item, new_item);
 | 
			
		||||
		else
 | 
			
		||||
		break;
 | 
			
		||||
	case CMD_PARSE_ERROR:
 | 
			
		||||
		new_item = cmdq_get_error(pr->error);
 | 
			
		||||
		free(pr->error);
 | 
			
		||||
		cmdq_append(c, new_item);
 | 
			
		||||
		break;
 | 
			
		||||
	case CMD_PARSE_SUCCESS:
 | 
			
		||||
		new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
 | 
			
		||||
		cmd_list_free(pr->cmdlist);
 | 
			
		||||
		cmdq_append(c, new_item);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return (1);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								mode-tree.c
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								mode-tree.c
									
									
									
									
									
								
							@@ -1045,8 +1045,8 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
 | 
			
		||||
    const char *template, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct cmdq_item	*new_item;
 | 
			
		||||
	struct cmd_list		*cmdlist;
 | 
			
		||||
	char			*command, *cause;
 | 
			
		||||
	char			*command;
 | 
			
		||||
	struct cmd_parse_result	*pr;
 | 
			
		||||
 | 
			
		||||
	command = cmd_template_replace(template, name, 1);
 | 
			
		||||
	if (command == NULL || *command == '\0') {
 | 
			
		||||
@@ -1054,17 +1054,22 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmdlist = cmd_string_parse(command, NULL, 0, &cause);
 | 
			
		||||
	if (cmdlist == NULL) {
 | 
			
		||||
		if (cause != NULL && c != NULL) {
 | 
			
		||||
			*cause = toupper((u_char)*cause);
 | 
			
		||||
			status_message_set(c, "%s", cause);
 | 
			
		||||
	pr = cmd_parse_from_string(command, NULL);
 | 
			
		||||
	switch (pr->status) {
 | 
			
		||||
	case CMD_PARSE_EMPTY:
 | 
			
		||||
		break;
 | 
			
		||||
	case CMD_PARSE_ERROR:
 | 
			
		||||
		if (c != NULL) {
 | 
			
		||||
			*pr->error = toupper((u_char)*pr->error);
 | 
			
		||||
			status_message_set(c, "%s", pr->error);
 | 
			
		||||
		}
 | 
			
		||||
		free(cause);
 | 
			
		||||
	} else {
 | 
			
		||||
		new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
 | 
			
		||||
		free(pr->error);
 | 
			
		||||
		break;
 | 
			
		||||
	case CMD_PARSE_SUCCESS:
 | 
			
		||||
		new_item = cmdq_get_command(pr->cmdlist, fs, NULL, 0);
 | 
			
		||||
		cmdq_append(c, new_item);
 | 
			
		||||
		cmd_list_free(cmdlist);
 | 
			
		||||
		cmd_list_free(pr->cmdlist);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free(command);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								options.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								options.c
									
									
									
									
									
								
							@@ -353,8 +353,7 @@ options_array_set(struct options_entry *o, u_int idx, const char *value,
 | 
			
		||||
{
 | 
			
		||||
	struct options_array_item	*a;
 | 
			
		||||
	char				*new;
 | 
			
		||||
	struct cmd_list			*cmdlist;
 | 
			
		||||
	char				*error;
 | 
			
		||||
	struct cmd_parse_result		*pr;
 | 
			
		||||
 | 
			
		||||
	if (!OPTIONS_IS_ARRAY(o)) {
 | 
			
		||||
		if (cause != NULL)
 | 
			
		||||
@@ -363,13 +362,19 @@ options_array_set(struct options_entry *o, u_int idx, const char *value,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (OPTIONS_IS_COMMAND(o)) {
 | 
			
		||||
		cmdlist = cmd_string_parse(value, NULL, 0, &error);
 | 
			
		||||
		if (cmdlist == NULL && error != NULL) {
 | 
			
		||||
			if (cause != NULL)
 | 
			
		||||
				*cause = error;
 | 
			
		||||
			else
 | 
			
		||||
				free(error);
 | 
			
		||||
		pr = cmd_parse_from_string(value, NULL);
 | 
			
		||||
		switch (pr->status) {
 | 
			
		||||
		case CMD_PARSE_EMPTY:
 | 
			
		||||
			*cause = xstrdup("empty command");
 | 
			
		||||
			return (-1);
 | 
			
		||||
		case CMD_PARSE_ERROR:
 | 
			
		||||
			if (cause != NULL)
 | 
			
		||||
				*cause = pr->error;
 | 
			
		||||
			else
 | 
			
		||||
				free(pr->error);
 | 
			
		||||
			return (-1);
 | 
			
		||||
		case CMD_PARSE_SUCCESS:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -397,7 +402,7 @@ options_array_set(struct options_entry *o, u_int idx, const char *value,
 | 
			
		||||
	if (OPTIONS_IS_STRING(o))
 | 
			
		||||
		a->value.string = new;
 | 
			
		||||
	else if (OPTIONS_IS_COMMAND(o))
 | 
			
		||||
		a->value.cmdlist = cmdlist;
 | 
			
		||||
		a->value.cmdlist = pr->cmdlist;
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										268
									
								
								tmux.1
									
									
									
									
									
								
							
							
						
						
									
										268
									
								
								tmux.1
									
									
									
									
									
								
							@@ -360,8 +360,217 @@ Key bindings may be changed with the
 | 
			
		||||
and
 | 
			
		||||
.Ic unbind-key
 | 
			
		||||
commands.
 | 
			
		||||
.Sh COMMAND PARSING AND EXECUTION
 | 
			
		||||
.Nm
 | 
			
		||||
supports a large number of commands which can be used to control its
 | 
			
		||||
behaviour.
 | 
			
		||||
Each command is named and can accept zero or more flags and arguments.
 | 
			
		||||
They may be bound to a key with the
 | 
			
		||||
.Ic bind-key
 | 
			
		||||
command or run from the shell prompt, a shell script, a configuration file or
 | 
			
		||||
the command prompt.
 | 
			
		||||
For example, the same
 | 
			
		||||
.Ic set-option
 | 
			
		||||
command run from the shell prompt, from
 | 
			
		||||
.Pa ~/.tmux.conf
 | 
			
		||||
and bound to a key may look like:
 | 
			
		||||
.Bd -literal -offset indent
 | 
			
		||||
$ tmux set-option -g status-style bg=cyan
 | 
			
		||||
 | 
			
		||||
set-option -g status-style bg=cyan
 | 
			
		||||
 | 
			
		||||
bind-key C set-option -g status-style bg=cyan
 | 
			
		||||
.Ed
 | 
			
		||||
.Pp
 | 
			
		||||
Here, the command name is
 | 
			
		||||
.Ql set-option ,
 | 
			
		||||
.Ql Fl g
 | 
			
		||||
is a flag and
 | 
			
		||||
.Ql status-style
 | 
			
		||||
and
 | 
			
		||||
.Ql bg=cyan
 | 
			
		||||
are arguments.
 | 
			
		||||
.Pp
 | 
			
		||||
.Nm
 | 
			
		||||
distinguishes between command parsing and execution.
 | 
			
		||||
In order to execute a command,
 | 
			
		||||
.Nm
 | 
			
		||||
needs it to be split up into its name and arguments.
 | 
			
		||||
This is command parsing.
 | 
			
		||||
If a command is run from the shell, the shell parses it; from inside
 | 
			
		||||
.Nm
 | 
			
		||||
or from a configuration file,
 | 
			
		||||
.Nm
 | 
			
		||||
does.
 | 
			
		||||
Examples of when
 | 
			
		||||
.Nm
 | 
			
		||||
parses commands are:
 | 
			
		||||
.Bl -dash -offset indent
 | 
			
		||||
.It
 | 
			
		||||
in a configuration file;
 | 
			
		||||
.It
 | 
			
		||||
typed at the command prompt (see
 | 
			
		||||
.Ic command-prompt ) ;
 | 
			
		||||
.It
 | 
			
		||||
given to
 | 
			
		||||
.Ic bind-key ;
 | 
			
		||||
.It
 | 
			
		||||
passed as arguments to
 | 
			
		||||
.Ic if-shell
 | 
			
		||||
or
 | 
			
		||||
.Ic confirm-before .
 | 
			
		||||
.El
 | 
			
		||||
.Pp
 | 
			
		||||
To execute commands, each client has a
 | 
			
		||||
.Ql command queue .
 | 
			
		||||
A global command queue not attached to any client is used on startup
 | 
			
		||||
for configuration files like
 | 
			
		||||
.Pa ~/.tmux.conf .
 | 
			
		||||
Parsed commands added to the queue are executed in order.
 | 
			
		||||
Some commands, like
 | 
			
		||||
.Ic if-shell
 | 
			
		||||
and
 | 
			
		||||
.Ic confirm-before ,
 | 
			
		||||
parse their argument to create a new command which is inserted immediately
 | 
			
		||||
after themselves.
 | 
			
		||||
This means that arguments can be parsed twice or more - once when the parent command (such as
 | 
			
		||||
.Ic if-shell )
 | 
			
		||||
is parsed and again when it parses and executes its command.
 | 
			
		||||
Commands like
 | 
			
		||||
.Ic if-shell ,
 | 
			
		||||
.Ic run-shell
 | 
			
		||||
and
 | 
			
		||||
.Ic display-panes
 | 
			
		||||
stop execution of subsequent commands on the queue until something happens -
 | 
			
		||||
.Ic if-shell
 | 
			
		||||
and
 | 
			
		||||
.Ic run-shell
 | 
			
		||||
until a shell command finishes and
 | 
			
		||||
.Ic display-panes
 | 
			
		||||
until a key is pressed.
 | 
			
		||||
For example, the following commands:
 | 
			
		||||
.Bd -literal -offset indent
 | 
			
		||||
new-session; new-window
 | 
			
		||||
if-shell "true" "split-window"
 | 
			
		||||
kill-session
 | 
			
		||||
.Ed
 | 
			
		||||
.Pp
 | 
			
		||||
Will execute
 | 
			
		||||
.Ic new-session ,
 | 
			
		||||
.Ic new-window ,
 | 
			
		||||
.Ic if-shell ,
 | 
			
		||||
the shell command
 | 
			
		||||
.Xr true 1 ,
 | 
			
		||||
.Ic new-window
 | 
			
		||||
and
 | 
			
		||||
.Ic kill-session
 | 
			
		||||
in that order.
 | 
			
		||||
.Pp
 | 
			
		||||
The
 | 
			
		||||
.Sx COMMANDS
 | 
			
		||||
section lists the
 | 
			
		||||
.Nm
 | 
			
		||||
commands and their arguments.
 | 
			
		||||
.Sh PARSING SYNTAX
 | 
			
		||||
This section describes the syntax of commands parsed by
 | 
			
		||||
.Nm ,
 | 
			
		||||
for example in a configuration file or at the command prompt.
 | 
			
		||||
Note the when commands are entered into the shell, they are parsed by the shell
 | 
			
		||||
- see for example
 | 
			
		||||
.Xr ksh 1
 | 
			
		||||
or
 | 
			
		||||
.Xr csh 1 .
 | 
			
		||||
.Pp
 | 
			
		||||
Each command is terminated by a newline or a semicolon (;).
 | 
			
		||||
Commands separated by semicolons together form a
 | 
			
		||||
.Ql command sequence
 | 
			
		||||
- if a command in the sequence encounters an error, no subsequent commands are
 | 
			
		||||
executed.
 | 
			
		||||
.Pp
 | 
			
		||||
Comments are marked by the unquoted # character - any remaining text after a
 | 
			
		||||
comment is ignored until the end of the line.
 | 
			
		||||
.Pp
 | 
			
		||||
If the last character of a line is \e, the line is joined with the following
 | 
			
		||||
line (the \e and the newline are completely removed).
 | 
			
		||||
This is called line continuation and applies both inside and outside quoted
 | 
			
		||||
strings and in comments.
 | 
			
		||||
.Pp
 | 
			
		||||
Command arguments may be specified as strings surrounded by either single (')
 | 
			
		||||
or double quotes (").
 | 
			
		||||
.\" "
 | 
			
		||||
This is required when the argument contains any special character.
 | 
			
		||||
Strings cannot span multiple lines except with line continuation.
 | 
			
		||||
.Pp
 | 
			
		||||
Outside of quotes and inside double quotes, these replacements are performed:
 | 
			
		||||
.Bl -dash -offset indent
 | 
			
		||||
.It
 | 
			
		||||
Environment variables preceded by $ are replaced with their value from the
 | 
			
		||||
global environment (see the
 | 
			
		||||
.Sx GLOBAL AND SESSION ENVIRONMENT
 | 
			
		||||
section).
 | 
			
		||||
.It
 | 
			
		||||
A leading ~ or ~user is expanded to the home directory of the current or
 | 
			
		||||
specified user.
 | 
			
		||||
.It
 | 
			
		||||
\euXXXX or \euXXXXXXXX is replaced by the Unicode codepoint corresponding to
 | 
			
		||||
the given four or eight digit hexadecimal number.
 | 
			
		||||
.It
 | 
			
		||||
When preceded (escaped) by a \e, the following characters are replaced: \ee by
 | 
			
		||||
the escape character; \er by a carriage return; \en by a newline; and \et by a
 | 
			
		||||
tab.
 | 
			
		||||
.Pp
 | 
			
		||||
Any other characters preceded by \e are replaced by themselves (that is, the \e
 | 
			
		||||
is removed) and are not treated as having any special meaning - so for example
 | 
			
		||||
\e; will not mark a command sequence and \e$ will not expand an environment
 | 
			
		||||
variable.
 | 
			
		||||
.El
 | 
			
		||||
.Pp
 | 
			
		||||
Environment variables may be set by using the syntax
 | 
			
		||||
.Ql name=value ,
 | 
			
		||||
for example
 | 
			
		||||
.Ql HOME=/home/user .
 | 
			
		||||
Variables set during parsing are added to the global environment.
 | 
			
		||||
.Pp
 | 
			
		||||
Commands may be parsed conditionally by surrounding them with
 | 
			
		||||
.Ql %if ,
 | 
			
		||||
.Ql %elif ,
 | 
			
		||||
.Ql %else
 | 
			
		||||
and
 | 
			
		||||
.Ql %endif .
 | 
			
		||||
The argument to
 | 
			
		||||
.Ql %if
 | 
			
		||||
and
 | 
			
		||||
.Ql %elif
 | 
			
		||||
is expanded as a format (see
 | 
			
		||||
.Sx FORMATS )
 | 
			
		||||
and if it evaluates to false (zero or empty), subsequent text is ignored until
 | 
			
		||||
the closing
 | 
			
		||||
.Ql %elif ,
 | 
			
		||||
.Ql %else
 | 
			
		||||
or
 | 
			
		||||
.Ql %endif .
 | 
			
		||||
For example:
 | 
			
		||||
.Bd -literal -offset indent
 | 
			
		||||
%if #{==:#{host},myhost}
 | 
			
		||||
set -g status-style bg=red
 | 
			
		||||
%elif #{==:#{host},myotherhost}
 | 
			
		||||
set -g status-style bg=green
 | 
			
		||||
%else
 | 
			
		||||
set -g status-style bg=blue
 | 
			
		||||
%endif
 | 
			
		||||
.Ed
 | 
			
		||||
.Pp
 | 
			
		||||
Will change the status line to red if running on
 | 
			
		||||
.Ql myhost ,
 | 
			
		||||
green if running on
 | 
			
		||||
.Ql myotherhost ,
 | 
			
		||||
or blue if running on another host.
 | 
			
		||||
Conditionals may be given on one line, for example:
 | 
			
		||||
.Bd -literal -offset indent
 | 
			
		||||
%if #{==:#{host},myhost} set -g status-style bg=red %endif
 | 
			
		||||
.Ed
 | 
			
		||||
.Sh COMMANDS
 | 
			
		||||
This section contains a list of the commands supported by
 | 
			
		||||
This section describes the commands supported by
 | 
			
		||||
.Nm .
 | 
			
		||||
Most commands accept the optional
 | 
			
		||||
.Fl t
 | 
			
		||||
@@ -627,16 +836,6 @@ Or if using
 | 
			
		||||
$ tmux bind-key F1 set-option status off
 | 
			
		||||
.Ed
 | 
			
		||||
.Pp
 | 
			
		||||
Multiple commands may be specified together as part of a
 | 
			
		||||
.Em command sequence .
 | 
			
		||||
Each command should be separated by spaces and a semicolon;
 | 
			
		||||
commands are executed sequentially from left to right and
 | 
			
		||||
lines ending with a backslash continue on to the next line,
 | 
			
		||||
except when escaped by another backslash.
 | 
			
		||||
A literal semicolon may be included by escaping it with a backslash (for
 | 
			
		||||
example, when specifying a command sequence to
 | 
			
		||||
.Ic bind-key ) .
 | 
			
		||||
.Pp
 | 
			
		||||
Example
 | 
			
		||||
.Nm
 | 
			
		||||
commands include:
 | 
			
		||||
@@ -1010,7 +1209,7 @@ and
 | 
			
		||||
.Fl T
 | 
			
		||||
show debugging information about jobs and terminals.
 | 
			
		||||
.It Xo Ic source-file
 | 
			
		||||
.Op Fl q
 | 
			
		||||
.Op Fl nq
 | 
			
		||||
.Ar path
 | 
			
		||||
.Xc
 | 
			
		||||
.D1 (alias: Ic source )
 | 
			
		||||
@@ -1024,44 +1223,9 @@ If
 | 
			
		||||
is given, no error will be returned if
 | 
			
		||||
.Ar path
 | 
			
		||||
does not exist.
 | 
			
		||||
.Pp
 | 
			
		||||
Within a configuration file, commands may be made conditional by surrounding
 | 
			
		||||
them with
 | 
			
		||||
.Em %if
 | 
			
		||||
and
 | 
			
		||||
.Em %endif
 | 
			
		||||
lines.
 | 
			
		||||
Additional
 | 
			
		||||
.Em %elif
 | 
			
		||||
and
 | 
			
		||||
.Em %else
 | 
			
		||||
lines may also be used.
 | 
			
		||||
The argument to
 | 
			
		||||
.Em %if
 | 
			
		||||
and
 | 
			
		||||
.Em %elif
 | 
			
		||||
is expanded as a format and if it evaluates to false (zero or empty),
 | 
			
		||||
subsequent lines are ignored until the next
 | 
			
		||||
.Em %elif ,
 | 
			
		||||
.Em %else
 | 
			
		||||
or
 | 
			
		||||
.Em %endif .
 | 
			
		||||
For example:
 | 
			
		||||
.Bd -literal -offset indent
 | 
			
		||||
%if #{==:#{host},myhost}
 | 
			
		||||
set -g status-style bg=red
 | 
			
		||||
%elif #{==:#{host},myotherhost}
 | 
			
		||||
set -g status-style bg=green
 | 
			
		||||
%else
 | 
			
		||||
set -g status-style bg=blue
 | 
			
		||||
%endif
 | 
			
		||||
.Ed
 | 
			
		||||
.Pp
 | 
			
		||||
Will change the status line to red if running on
 | 
			
		||||
.Ql myhost ,
 | 
			
		||||
green if running on
 | 
			
		||||
.Ql myotherhost ,
 | 
			
		||||
or blue if running on another host.
 | 
			
		||||
With
 | 
			
		||||
.Fl n ,
 | 
			
		||||
the file is parsed but no commands are executed.
 | 
			
		||||
.It Ic start-server
 | 
			
		||||
.D1 (alias: Ic start )
 | 
			
		||||
Start the
 | 
			
		||||
@@ -4141,7 +4305,7 @@ right of the list if there is not enough space.
 | 
			
		||||
.Ic norange
 | 
			
		||||
.Xc
 | 
			
		||||
Mark a range in the
 | 
			
		||||
. Ic status-format
 | 
			
		||||
.Ic status-format
 | 
			
		||||
option.
 | 
			
		||||
.Ic range=left
 | 
			
		||||
and
 | 
			
		||||
@@ -4457,7 +4621,7 @@ This command works only from inside
 | 
			
		||||
.Op Fl x Ar position
 | 
			
		||||
.Op Fl y Ar position
 | 
			
		||||
.Xc
 | 
			
		||||
.D1 (alias: Ic menu)
 | 
			
		||||
.D1 (alias: Ic menu )
 | 
			
		||||
Display a menu on
 | 
			
		||||
.Ar target-client .
 | 
			
		||||
.Ar target-pane
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										52
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								tmux.h
									
									
									
									
									
								
							@@ -1283,16 +1283,23 @@ struct cmd_find_state {
 | 
			
		||||
struct cmd {
 | 
			
		||||
	const struct cmd_entry	 *entry;
 | 
			
		||||
	struct args		 *args;
 | 
			
		||||
	u_int			  group;
 | 
			
		||||
 | 
			
		||||
	char			 *file;
 | 
			
		||||
	u_int			  line;
 | 
			
		||||
 | 
			
		||||
	char			 *alias;
 | 
			
		||||
	int			  argc;
 | 
			
		||||
	char			**argv;
 | 
			
		||||
 | 
			
		||||
	TAILQ_ENTRY(cmd)	  qentry;
 | 
			
		||||
};
 | 
			
		||||
TAILQ_HEAD(cmds, cmd);
 | 
			
		||||
 | 
			
		||||
struct cmd_list {
 | 
			
		||||
	int		references;
 | 
			
		||||
	TAILQ_HEAD(, cmd)	 list;
 | 
			
		||||
	u_int		group;
 | 
			
		||||
	struct cmds	list;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Command return values. */
 | 
			
		||||
@@ -1303,6 +1310,31 @@ enum cmd_retval {
 | 
			
		||||
	CMD_RETURN_STOP
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Command parse result. */
 | 
			
		||||
enum cmd_parse_status {
 | 
			
		||||
	CMD_PARSE_EMPTY,
 | 
			
		||||
	CMD_PARSE_ERROR,
 | 
			
		||||
	CMD_PARSE_SUCCESS
 | 
			
		||||
};
 | 
			
		||||
struct cmd_parse_result {
 | 
			
		||||
	enum cmd_parse_status	 status;
 | 
			
		||||
	struct cmd_list		*cmdlist;
 | 
			
		||||
	char			*error;
 | 
			
		||||
};
 | 
			
		||||
struct cmd_parse_input {
 | 
			
		||||
	int			 flags;
 | 
			
		||||
#define CMD_PARSE_QUIET 0x1
 | 
			
		||||
#define CMD_PARSE_PARSEONLY 0x2
 | 
			
		||||
#define CMD_PARSE_NOALIAS 0x4
 | 
			
		||||
 | 
			
		||||
	const char		*file;
 | 
			
		||||
	u_int			 line;
 | 
			
		||||
 | 
			
		||||
	struct cmdq_item	*item;
 | 
			
		||||
	struct client		*c;
 | 
			
		||||
	struct cmd_find_state	 fs;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Command queue item type. */
 | 
			
		||||
enum cmdq_type {
 | 
			
		||||
	CMDQ_COMMAND,
 | 
			
		||||
@@ -1673,7 +1705,6 @@ void	proc_toggle_log(struct tmuxproc *);
 | 
			
		||||
/* cfg.c */
 | 
			
		||||
extern int cfg_finished;
 | 
			
		||||
extern struct client *cfg_client;
 | 
			
		||||
#define CFG_QUIET 0x1
 | 
			
		||||
void	start_cfg(void);
 | 
			
		||||
int	load_cfg(const char *, struct client *, struct cmdq_item *, int,
 | 
			
		||||
	    struct cmdq_item **);
 | 
			
		||||
@@ -1958,12 +1989,16 @@ int		 cmd_find_from_nothing(struct cmd_find_state *, int);
 | 
			
		||||
 | 
			
		||||
/* cmd.c */
 | 
			
		||||
void		 cmd_log_argv(int, char **, const char *);
 | 
			
		||||
void		 cmd_prepend_argv(int *, char ***, char *);
 | 
			
		||||
void		 cmd_append_argv(int *, char ***, char *);
 | 
			
		||||
int		 cmd_pack_argv(int, char **, char *, size_t);
 | 
			
		||||
int		 cmd_unpack_argv(char *, size_t, int, char ***);
 | 
			
		||||
char	       **cmd_copy_argv(int, char **);
 | 
			
		||||
void		 cmd_free_argv(int, char **);
 | 
			
		||||
char		*cmd_stringify_argv(int, char **);
 | 
			
		||||
char		*cmd_get_alias(const char *);
 | 
			
		||||
struct cmd	*cmd_parse(int, char **, const char *, u_int, char **);
 | 
			
		||||
void		 cmd_free(struct cmd *);
 | 
			
		||||
char		*cmd_print(struct cmd *);
 | 
			
		||||
int		 cmd_mouse_at(struct window_pane *, struct mouse_event *,
 | 
			
		||||
		     u_int *, u_int *, int);
 | 
			
		||||
@@ -1977,7 +2012,16 @@ extern const struct cmd_entry *cmd_table[];
 | 
			
		||||
enum cmd_retval	 cmd_attach_session(struct cmdq_item *, const char *, int, int,
 | 
			
		||||
		     const char *, int);
 | 
			
		||||
 | 
			
		||||
/* cmd-parse.c */
 | 
			
		||||
void	    	 cmd_parse_empty(struct cmd_parse_input *);
 | 
			
		||||
struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *);
 | 
			
		||||
struct cmd_parse_result *cmd_parse_from_string(const char *,
 | 
			
		||||
		     struct cmd_parse_input *);
 | 
			
		||||
 | 
			
		||||
/* cmd-list.c */
 | 
			
		||||
struct cmd_list	*cmd_list_new(void);
 | 
			
		||||
void		 cmd_list_append(struct cmd_list *, struct cmd *);
 | 
			
		||||
void		 cmd_list_move(struct cmd_list *, struct cmd_list *);
 | 
			
		||||
struct cmd_list	*cmd_list_parse(int, char **, const char *, u_int, char **);
 | 
			
		||||
void		 cmd_list_free(struct cmd_list *);
 | 
			
		||||
char		*cmd_list_print(struct cmd_list *);
 | 
			
		||||
@@ -1999,10 +2043,6 @@ void		 cmdq_guard(struct cmdq_item *, const char *, int);
 | 
			
		||||
void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...);
 | 
			
		||||
void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...);
 | 
			
		||||
 | 
			
		||||
/* cmd-string.c */
 | 
			
		||||
int		 cmd_string_split(const char *, int *, char ***);
 | 
			
		||||
struct cmd_list	*cmd_string_parse(const char *, const char *, u_int, char **);
 | 
			
		||||
 | 
			
		||||
/* cmd-wait-for.c */
 | 
			
		||||
void	cmd_wait_for_flush(void);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user