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.
pull/1/head
Nicholas Marriott 2009-08-19 10:39:50 +00:00
parent 036de0c5e4
commit 3f4418d84d
4 changed files with 238 additions and 77 deletions

View File

@ -27,56 +27,112 @@
* Prompt for command in client.
*/
void cmd_command_prompt_init(struct cmd *, int);
int cmd_command_prompt_exec(struct cmd *, struct cmd_ctx *);
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 *);
int cmd_command_prompt_callback(void *, const char *);
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 {
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->template = NULL;
hdr = xstrdup(":");
}
status_prompt_set(c, hdr,
cmd_command_prompt_callback, cmd_command_prompt_free, cdata, 0);
xfree(hdr);
cdata->idx = 1;
cdata->next_prompt = NULL;
cdata->prompts = NULL;
cdata->template = NULL;
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;
struct cmd_ctx ctx;
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 (cmd_string_parse(newtempl, &cmdlist, &cause) != 0) {
if (cause != NULL) {
*cause = toupper((u_char) *cause);
status_message_set(c, "%s", cause);
xfree(cause);
}
if (buf == NULL)
return (0);
buf[len] = '\0';
s = buf;
}
if (cmd_string_parse(s, &cmdlist, &cause) != 0) {
if (cause == NULL)
return (0);
*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);
}

View File

@ -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
View File

@ -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
View File

@ -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);