mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 09:26:05 +00:00 
			
		
		
		
	Extend command-prompt with a -p option which is a comma-separated list of one
or more prompts to present in order. The responses to the prompt are replaced in the template string: %% are replaced in order, so the first prompt replaces the first %%, the second replaces the second, and so on. In addition, %1 up to %9 are replaced with the responses to the first the ninth prompts The default template is "%1" so the response to the first prompt is processed as a command. Note that this changes the behaviour for %% so if there is only one prompt, only the first %% will be replaced. Templates such as "neww -n '%%' 'ssh %%'" should be changed to "neww -n '%1' 'ssh %1'". From Tiago Cunha.
This commit is contained in:
		@@ -28,55 +28,111 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
void	 cmd_command_prompt_init(struct cmd *, int);
 | 
			
		||||
int	 cmd_command_prompt_parse(struct cmd *, int, char **, char **);
 | 
			
		||||
int	 cmd_command_prompt_exec(struct cmd *, struct cmd_ctx *);
 | 
			
		||||
void	 cmd_command_prompt_free(struct cmd *);
 | 
			
		||||
size_t	 cmd_command_prompt_print(struct cmd *, char *, size_t);
 | 
			
		||||
 | 
			
		||||
int	 cmd_command_prompt_callback(void *, const char *);
 | 
			
		||||
void	cmd_command_prompt_free(void *);
 | 
			
		||||
void	 cmd_command_prompt_cfree(void *);
 | 
			
		||||
char	*cmd_command_prompt_replace(char *, const char *, int);
 | 
			
		||||
 | 
			
		||||
const struct cmd_entry cmd_command_prompt_entry = {
 | 
			
		||||
	"command-prompt", NULL,
 | 
			
		||||
	CMD_TARGET_CLIENT_USAGE " [template]",
 | 
			
		||||
	CMD_ARG01, 0,
 | 
			
		||||
	CMD_TARGET_CLIENT_USAGE " [-p prompts] [template]",
 | 
			
		||||
	0, 0,
 | 
			
		||||
	cmd_command_prompt_init,
 | 
			
		||||
	cmd_target_parse,
 | 
			
		||||
	cmd_command_prompt_parse,
 | 
			
		||||
	cmd_command_prompt_exec,
 | 
			
		||||
	cmd_target_free,
 | 
			
		||||
	cmd_target_print
 | 
			
		||||
	cmd_command_prompt_free,
 | 
			
		||||
	cmd_command_prompt_print
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct cmd_command_prompt_data {
 | 
			
		||||
	struct client	*c;
 | 
			
		||||
	char	*prompts;
 | 
			
		||||
	char	*target;
 | 
			
		||||
	char	*template;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct cmd_command_prompt_cdata {
 | 
			
		||||
	struct client	*c;
 | 
			
		||||
	char		*next_prompt;
 | 
			
		||||
	char		*prompts;
 | 
			
		||||
	char		*template;
 | 
			
		||||
	int		 idx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
cmd_command_prompt_init(struct cmd *self, int key)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_target_data	*data;
 | 
			
		||||
	struct cmd_command_prompt_data	*data;
 | 
			
		||||
 | 
			
		||||
	cmd_target_init(self, key);
 | 
			
		||||
	data = self->data;
 | 
			
		||||
	self->data = data = xmalloc(sizeof *data);
 | 
			
		||||
	data->prompts = NULL;
 | 
			
		||||
	data->target = NULL;
 | 
			
		||||
	data->template = NULL;
 | 
			
		||||
 | 
			
		||||
	switch (key) {
 | 
			
		||||
	case ',':
 | 
			
		||||
		data->arg = xstrdup("rename-window '%%'");
 | 
			
		||||
		data->template = xstrdup("rename-window '%%'");
 | 
			
		||||
		break;
 | 
			
		||||
	case '.':
 | 
			
		||||
		data->arg = xstrdup("move-window -t '%%'");
 | 
			
		||||
		data->template = xstrdup("move-window -t '%%'");
 | 
			
		||||
		break;
 | 
			
		||||
	case 'f':
 | 
			
		||||
		data->arg = xstrdup("find-window '%%'");
 | 
			
		||||
		data->template = xstrdup("find-window '%%'");
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
cmd_command_prompt_parse(struct cmd *self, int argc, char **argv, char **cause)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_command_prompt_data	*data;
 | 
			
		||||
	int				 opt;
 | 
			
		||||
 | 
			
		||||
	self->entry->init(self, 0);
 | 
			
		||||
	data = self->data;
 | 
			
		||||
 | 
			
		||||
	while ((opt = getopt(argc, argv, "p:t:")) != -1) {
 | 
			
		||||
		switch (opt) {
 | 
			
		||||
		case 'p':
 | 
			
		||||
			if (data->prompts == NULL)
 | 
			
		||||
				data->prompts = xstrdup(optarg);
 | 
			
		||||
			break;
 | 
			
		||||
		case 't':
 | 
			
		||||
			if (data->target == NULL)
 | 
			
		||||
				data->target = xstrdup(optarg);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			goto usage;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	argc -= optind;
 | 
			
		||||
	argv += optind;
 | 
			
		||||
	if (argc != 0 && argc != 1)
 | 
			
		||||
		goto usage;
 | 
			
		||||
 | 
			
		||||
	if (argc == 1)
 | 
			
		||||
		data->template = xstrdup(argv[0]);
 | 
			
		||||
 | 
			
		||||
	return (0);
 | 
			
		||||
 | 
			
		||||
usage:
 | 
			
		||||
	xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
 | 
			
		||||
 | 
			
		||||
	self->entry->free(self);
 | 
			
		||||
	return (-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
cmd_command_prompt_exec(struct cmd *self, struct cmd_ctx *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_target_data		*data = self->data;
 | 
			
		||||
	struct cmd_command_prompt_data	*cdata;
 | 
			
		||||
	struct cmd_command_prompt_data	*data = self->data;
 | 
			
		||||
	struct cmd_command_prompt_cdata	*cdata;
 | 
			
		||||
	struct client			*c;
 | 
			
		||||
	char				*hdr, *ptr;
 | 
			
		||||
	char				*prompt, *ptr;
 | 
			
		||||
	size_t				 n;
 | 
			
		||||
 | 
			
		||||
	if ((c = cmd_find_client(ctx, data->target)) == NULL)
 | 
			
		||||
		return (-1);
 | 
			
		||||
@@ -86,76 +142,100 @@ cmd_command_prompt_exec(struct cmd *self, struct cmd_ctx *ctx)
 | 
			
		||||
 | 
			
		||||
	cdata = xmalloc(sizeof *cdata);
 | 
			
		||||
	cdata->c = c;
 | 
			
		||||
	if (data->arg != NULL) {
 | 
			
		||||
		cdata->template = xstrdup(data->arg);
 | 
			
		||||
		if ((ptr = strchr(data->arg, ' ')) == NULL)
 | 
			
		||||
			ptr = strchr(data->arg, '\0');
 | 
			
		||||
		xasprintf(&hdr, "(%.*s) ", (int) (ptr - data->arg), data->arg);
 | 
			
		||||
	} else {
 | 
			
		||||
	cdata->idx = 1;
 | 
			
		||||
	cdata->next_prompt = NULL;
 | 
			
		||||
	cdata->prompts = NULL;
 | 
			
		||||
	cdata->template = NULL;
 | 
			
		||||
		hdr = xstrdup(":");
 | 
			
		||||
	}
 | 
			
		||||
	status_prompt_set(c, hdr,
 | 
			
		||||
	    cmd_command_prompt_callback, cmd_command_prompt_free, cdata, 0);
 | 
			
		||||
	xfree(hdr);
 | 
			
		||||
 | 
			
		||||
	if (data->template != NULL)
 | 
			
		||||
		cdata->template = xstrdup(data->template);
 | 
			
		||||
	else
 | 
			
		||||
		cdata->template = xstrdup("%1");
 | 
			
		||||
	if (data->prompts != NULL)
 | 
			
		||||
		cdata->prompts = xstrdup(data->prompts);
 | 
			
		||||
	else if (data->template != NULL) {
 | 
			
		||||
		n = strcspn(data->template, " ,");
 | 
			
		||||
		xasprintf(&cdata->prompts, "(%.*s) ", (int) n, data->template);
 | 
			
		||||
	} else
 | 
			
		||||
		cdata->prompts = xstrdup(":");
 | 
			
		||||
 | 
			
		||||
	cdata->next_prompt = cdata->prompts;
 | 
			
		||||
	ptr = strsep(&cdata->next_prompt, ",");
 | 
			
		||||
	if (data->prompts == NULL)
 | 
			
		||||
		prompt = xstrdup(ptr);
 | 
			
		||||
	else
 | 
			
		||||
		xasprintf(&prompt, "%s ", ptr);
 | 
			
		||||
	status_prompt_set(c, prompt, cmd_command_prompt_callback,
 | 
			
		||||
	    cmd_command_prompt_cfree, cdata, 0);
 | 
			
		||||
	xfree(prompt);
 | 
			
		||||
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
cmd_command_prompt_free(struct cmd *self)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_command_prompt_data	*data = self->data;
 | 
			
		||||
 | 
			
		||||
	if (data->prompts != NULL)
 | 
			
		||||
		xfree(data->prompts);
 | 
			
		||||
	if (data->target != NULL)
 | 
			
		||||
		xfree(data->target);
 | 
			
		||||
	if (data->template != NULL)
 | 
			
		||||
		xfree(data->template);
 | 
			
		||||
	xfree(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t
 | 
			
		||||
cmd_command_prompt_print(struct cmd *self, char *buf, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_command_prompt_data	*data = self->data;
 | 
			
		||||
	size_t				 off = 0;
 | 
			
		||||
 | 
			
		||||
	off += xsnprintf(buf, len, "%s", self->entry->name);
 | 
			
		||||
	if (data == NULL)
 | 
			
		||||
		return (off);
 | 
			
		||||
	if (off < len && data->prompts != NULL)
 | 
			
		||||
		off += cmd_prarg(buf + off, len - off, " -p ", data->prompts);
 | 
			
		||||
	if (off < len && data->target != NULL)
 | 
			
		||||
		off += cmd_prarg(buf + off, len - off, " -t ", data->target);
 | 
			
		||||
	if (off < len && data->template != NULL)
 | 
			
		||||
		off += cmd_prarg(buf + off, len - off, " ", data->template);
 | 
			
		||||
	return (off);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
cmd_command_prompt_callback(void *data, const char *s)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_command_prompt_data	*cdata = data;
 | 
			
		||||
	struct cmd_command_prompt_cdata	*cdata = data;
 | 
			
		||||
	struct client			*c = cdata->c;
 | 
			
		||||
	struct cmd_list			*cmdlist;
 | 
			
		||||
	struct cmd_ctx			 ctx;
 | 
			
		||||
	char				*cause, *ptr, *buf, ch;
 | 
			
		||||
	size_t				 len, slen;
 | 
			
		||||
	char				*cause, *newtempl, *prompt, *ptr;
 | 
			
		||||
 | 
			
		||||
	if (s == NULL || *s == '\0')
 | 
			
		||||
	if (s == NULL)
 | 
			
		||||
		return (0);
 | 
			
		||||
	slen = strlen(s);
 | 
			
		||||
 | 
			
		||||
	len = 0;
 | 
			
		||||
	buf = NULL;
 | 
			
		||||
	if (cdata->template != NULL) {
 | 
			
		||||
		ptr = cdata->template;
 | 
			
		||||
		while (*ptr != '\0') {
 | 
			
		||||
			switch (ch = *ptr++) {
 | 
			
		||||
			case '%':
 | 
			
		||||
				if (*ptr != '%')
 | 
			
		||||
					break;
 | 
			
		||||
				ptr++;
 | 
			
		||||
	newtempl = cmd_command_prompt_replace(cdata->template, s, cdata->idx);
 | 
			
		||||
	xfree(cdata->template);
 | 
			
		||||
	cdata->template = newtempl;
 | 
			
		||||
 | 
			
		||||
				buf = xrealloc(buf, 1, len + slen + 1);
 | 
			
		||||
				memcpy(buf + len, s, slen);
 | 
			
		||||
				len += slen;
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				buf = xrealloc(buf, 1, len + 2);
 | 
			
		||||
				buf[len++] = ch;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
	if ((ptr = strsep(&cdata->next_prompt, ",")) != NULL) {
 | 
			
		||||
		xasprintf(&prompt, "%s ", ptr);
 | 
			
		||||
		status_prompt_update(c, prompt);
 | 
			
		||||
		xfree(prompt);
 | 
			
		||||
		cdata->idx++;
 | 
			
		||||
		return (1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		if (buf == NULL)
 | 
			
		||||
			return (0);
 | 
			
		||||
		buf[len] = '\0';
 | 
			
		||||
		s = buf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cmd_string_parse(s, &cmdlist, &cause) != 0) {
 | 
			
		||||
		if (cause == NULL)
 | 
			
		||||
			return (0);
 | 
			
		||||
	if (cmd_string_parse(newtempl, &cmdlist, &cause) != 0) {
 | 
			
		||||
		if (cause != NULL) {
 | 
			
		||||
			*cause = toupper((u_char) *cause);
 | 
			
		||||
			status_message_set(c, "%s", cause);
 | 
			
		||||
			xfree(cause);
 | 
			
		||||
		cmdlist = NULL;
 | 
			
		||||
		}
 | 
			
		||||
	if (buf != NULL)
 | 
			
		||||
		xfree(buf);
 | 
			
		||||
	if (cmdlist == NULL)
 | 
			
		||||
		return (0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.msgdata = NULL;
 | 
			
		||||
	ctx.cursession = c->session;
 | 
			
		||||
@@ -176,11 +256,53 @@ cmd_command_prompt_callback(void *data, const char *s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
cmd_command_prompt_free(void *data)
 | 
			
		||||
cmd_command_prompt_cfree(void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct cmd_command_prompt_data	*cdata = data;
 | 
			
		||||
	struct cmd_command_prompt_cdata	*cdata = data;
 | 
			
		||||
 | 
			
		||||
	if (cdata->prompts != NULL)
 | 
			
		||||
		xfree(cdata->prompts);
 | 
			
		||||
	if (cdata->template != NULL)
 | 
			
		||||
		xfree(cdata->template);
 | 
			
		||||
	xfree(cdata);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *
 | 
			
		||||
cmd_command_prompt_replace(char *template, const char *s, int idx)
 | 
			
		||||
{
 | 
			
		||||
	char	 ch;
 | 
			
		||||
	char	*buf, *ptr;
 | 
			
		||||
	int	 replaced;
 | 
			
		||||
	size_t	 len;
 | 
			
		||||
 | 
			
		||||
	if (strstr(template, "%") == NULL)
 | 
			
		||||
		return (xstrdup(template));
 | 
			
		||||
 | 
			
		||||
	buf = xmalloc(1);
 | 
			
		||||
	*buf = '\0';
 | 
			
		||||
	len = 0;
 | 
			
		||||
	replaced = 0;
 | 
			
		||||
 | 
			
		||||
	ptr = template;
 | 
			
		||||
	while (*ptr != '\0') {
 | 
			
		||||
		switch (ch = *ptr++) {
 | 
			
		||||
		case '%':
 | 
			
		||||
			if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
 | 
			
		||||
				if (*ptr != '%' || replaced)
 | 
			
		||||
					break;
 | 
			
		||||
				replaced = 1;
 | 
			
		||||
			}
 | 
			
		||||
			ptr++;
 | 
			
		||||
 | 
			
		||||
			len += strlen(s);
 | 
			
		||||
			buf = xrealloc(buf, 1, len + 1);
 | 
			
		||||
			strlcat(buf, s, len + 1);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		buf = xrealloc(buf, 1, len + 2);
 | 
			
		||||
		buf[len++] = ch;
 | 
			
		||||
		buf[len] = '\0';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (buf);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								status.c
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								status.c
									
									
									
									
									
								
							@@ -668,6 +668,20 @@ status_prompt_clear(struct client *c)
 | 
			
		||||
	screen_reinit(&c->status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
status_prompt_update(struct client *c, const char *msg)
 | 
			
		||||
{
 | 
			
		||||
	xfree(c->prompt_string);
 | 
			
		||||
	c->prompt_string = xstrdup(msg);
 | 
			
		||||
 | 
			
		||||
	*c->prompt_buffer = '\0';
 | 
			
		||||
	c->prompt_index = 0;
 | 
			
		||||
 | 
			
		||||
	c->prompt_hindex = 0;
 | 
			
		||||
 | 
			
		||||
	c->flags |= CLIENT_STATUS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Draw client prompt on status line of present else on last line. */
 | 
			
		||||
int
 | 
			
		||||
status_prompt_redraw(struct client *c)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								tmux.1
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								tmux.1
									
									
									
									
									
								
							@@ -1662,6 +1662,7 @@ session option.
 | 
			
		||||
Commands related to the status line are as follows:
 | 
			
		||||
.Bl -tag -width Ds
 | 
			
		||||
.It Xo Ic command-prompt
 | 
			
		||||
.Op Fl p Ar prompts
 | 
			
		||||
.Op Fl t Ar target-client
 | 
			
		||||
.Op Ar template
 | 
			
		||||
.Xc
 | 
			
		||||
@@ -1671,8 +1672,30 @@ This may be used from inside
 | 
			
		||||
to execute commands interactively.
 | 
			
		||||
If
 | 
			
		||||
.Ar template
 | 
			
		||||
is specified, it is used as the command; any %% in the template will be
 | 
			
		||||
replaced by what is entered at the prompt.
 | 
			
		||||
is specified, it is used as the command.
 | 
			
		||||
If
 | 
			
		||||
.Fl p
 | 
			
		||||
is given,
 | 
			
		||||
.Ar prompts
 | 
			
		||||
is a comma-separated list of prompts which are displayed in order; otherwise
 | 
			
		||||
a single prompt is displayed, constructed from
 | 
			
		||||
.Ar template
 | 
			
		||||
if it is present, or
 | 
			
		||||
.Ql \&:
 | 
			
		||||
if not.
 | 
			
		||||
Before the command is executed, the first occurrence of the string
 | 
			
		||||
.Ql %%
 | 
			
		||||
and all occurences of
 | 
			
		||||
.Ql %1
 | 
			
		||||
are replaced by the response to the first prompt, the second
 | 
			
		||||
.Ql %%
 | 
			
		||||
and all
 | 
			
		||||
.Ql %2
 | 
			
		||||
are replaced with the response to the second prompt, and so on for further
 | 
			
		||||
prompts. Up to nine prompt responses may be replaced
 | 
			
		||||
.Ns ( Ql %1
 | 
			
		||||
to
 | 
			
		||||
.Ns Ql %9 ) .
 | 
			
		||||
.It Xo Ic confirm-before
 | 
			
		||||
.Op Fl t Ar target-client
 | 
			
		||||
.Ar command
 | 
			
		||||
@@ -1926,6 +1949,7 @@ Creating new key bindings:
 | 
			
		||||
.Bd -literal -offset indent
 | 
			
		||||
bind-key b set-option status
 | 
			
		||||
bind-key / command-prompt "split-window 'exec man %%'"
 | 
			
		||||
bind-key S command-prompt "new-window -n %1 'ssh %1'"
 | 
			
		||||
.Ed
 | 
			
		||||
.Sh SEE ALSO
 | 
			
		||||
.Xr pty 4
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								tmux.h
									
									
									
									
									
								
							@@ -1440,6 +1440,7 @@ void	 status_prompt_set(struct client *, const char *,
 | 
			
		||||
void	 status_prompt_clear(struct client *);
 | 
			
		||||
int	 status_prompt_redraw(struct client *);
 | 
			
		||||
void	 status_prompt_key(struct client *, int);
 | 
			
		||||
void	 status_prompt_update(struct client *, const char *);
 | 
			
		||||
 | 
			
		||||
/* resize.c */
 | 
			
		||||
void	 recalculate_sizes(void);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user