mirror of
https://github.com/tmux/tmux.git
synced 2026-10-06 17:49:52 +00:00
Do not escape in %%.
This commit is contained in:
47
cmd-invoke.c
47
cmd-invoke.c
@@ -47,6 +47,7 @@ struct cmd_invoke_state {
|
|||||||
|
|
||||||
int argc;
|
int argc;
|
||||||
char **argv;
|
char **argv;
|
||||||
|
int replaced;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void cmd_invoke_push(struct cmd_invoke_state *,
|
static void cmd_invoke_push(struct cmd_invoke_state *,
|
||||||
@@ -182,6 +183,47 @@ cmd_invoke_tilde(const char *name)
|
|||||||
return (pw->pw_dir);
|
return (pw->pw_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Replace template markers in a parsed value without quoting or reparsing. */
|
||||||
|
static char *
|
||||||
|
cmd_invoke_replace(struct cmd_invoke_state *is, const char *s)
|
||||||
|
{
|
||||||
|
const char *cp = s, *value;
|
||||||
|
char *buf = NULL, ch[2] = { 0 };
|
||||||
|
size_t len = 0;
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
if (is->argc == 0 || strchr(s, '%') == NULL)
|
||||||
|
return (xstrdup(s));
|
||||||
|
|
||||||
|
while (*cp != '\0') {
|
||||||
|
value = NULL;
|
||||||
|
if (*cp == '%') {
|
||||||
|
idx = cp[1] - '1';
|
||||||
|
if (idx >= 0 && idx < 9 && idx < is->argc) {
|
||||||
|
value = is->argv[idx];
|
||||||
|
cp += 2;
|
||||||
|
if (*cp == '%')
|
||||||
|
cp++;
|
||||||
|
} else if (cp[1] == '%' && !is->replaced) {
|
||||||
|
is->replaced = 1;
|
||||||
|
value = is->argv[0];
|
||||||
|
cp += 2;
|
||||||
|
if (*cp == '%')
|
||||||
|
cp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value != NULL)
|
||||||
|
cmd_invoke_append(&buf, &len, value);
|
||||||
|
else {
|
||||||
|
ch[0] = *cp++;
|
||||||
|
cmd_invoke_append(&buf, &len, ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (buf == NULL)
|
||||||
|
buf = xstrdup("");
|
||||||
|
return (buf);
|
||||||
|
}
|
||||||
|
|
||||||
/* Expand a parsed string node into an argv string. */
|
/* Expand a parsed string node into an argv string. */
|
||||||
static int
|
static int
|
||||||
cmd_invoke_expand_string(struct cmdq_item *item, struct cmd_invoke_state *is,
|
cmd_invoke_expand_string(struct cmdq_item *item, struct cmd_invoke_state *is,
|
||||||
@@ -191,7 +233,6 @@ cmd_invoke_expand_string(struct cmdq_item *item, struct cmd_invoke_state *is,
|
|||||||
const char *s, *value;
|
const char *s, *value;
|
||||||
char *buf = NULL, *new;
|
char *buf = NULL, *new;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
int i;
|
|
||||||
|
|
||||||
child = cmd_parse_node_first_child(node);
|
child = cmd_parse_node_first_child(node);
|
||||||
while (child != NULL) {
|
while (child != NULL) {
|
||||||
@@ -214,8 +255,8 @@ cmd_invoke_expand_string(struct cmdq_item *item, struct cmd_invoke_state *is,
|
|||||||
}
|
}
|
||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
buf = xstrdup("");
|
buf = xstrdup("");
|
||||||
for (i = 0; i < is->argc; i++) {
|
if (is->argc != 0) {
|
||||||
new = cmd_template_replace(buf, is->argv[i], i + 1);
|
new = cmd_invoke_replace(is, buf);
|
||||||
free(buf);
|
free(buf);
|
||||||
buf = new;
|
buf = new;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# Exercise cmd_template_replace through command-prompt, which passes prompt
|
# Exercise invocation-time template replacement through command-prompt. String
|
||||||
# input as argv to args_make_commands. This covers the quoting modes, indexed
|
# and braced templates are parsed before prompting; responses are literal argv
|
||||||
# replacements, and cases where replacements are intentionally not made.
|
# values and cannot add arguments or commands to the stored tree.
|
||||||
|
|
||||||
PATH=/bin:/usr/bin
|
PATH=/bin:/usr/bin
|
||||||
TERM=screen
|
TERM=screen
|
||||||
@@ -115,6 +115,26 @@ $IN bind -n M-i command-prompt -p 'one,two' 'set -g @double_index "%2%"' ||
|
|||||||
$IN bind -n M-n command-prompt -p '(none)' \
|
$IN bind -n M-n command-prompt -p '(none)' \
|
||||||
"set -g @plain no-template-markers" ||
|
"set -g @plain no-template-markers" ||
|
||||||
exit 1
|
exit 1
|
||||||
|
$IN bind -n M-u command-prompt -p '(unmatched)' \
|
||||||
|
"set -g @plain '%9 %0 %'" || exit 1
|
||||||
|
|
||||||
|
# Command-valued bodies must have the same replacement behaviour as strings.
|
||||||
|
cat >"$TMP/bindings.conf" <<'EOF'
|
||||||
|
bind -n M-b command-prompt -p '(braced)' { set -g @r '%%' }
|
||||||
|
bind -n M-t command-prompt -p '(first)' {
|
||||||
|
set -g @first '%%'
|
||||||
|
set -g @second '%%'
|
||||||
|
}
|
||||||
|
bind -n M-v command-prompt -p 'one,two' {
|
||||||
|
set -g @one %1
|
||||||
|
set -g @two %2
|
||||||
|
set -g @two_again %2
|
||||||
|
set -g @r '%%'
|
||||||
|
}
|
||||||
|
bind -n M-q command-prompt -p '(indexed)' { set -g @r %1 }
|
||||||
|
bind -n M-w command-prompt -p '(within)' { set -g @r '%%/%%' }
|
||||||
|
EOF
|
||||||
|
$IN source-file "$TMP/bindings.conf" || exit 1
|
||||||
|
|
||||||
$OUT new -d -x80 -y24 || exit 1
|
$OUT new -d -x80 -y24 || exit 1
|
||||||
$OUT set -g status off || exit 1
|
$OUT set -g status off || exit 1
|
||||||
@@ -125,8 +145,7 @@ sleep 1
|
|||||||
|
|
||||||
reset_options
|
reset_options
|
||||||
|
|
||||||
# %% is for templates already inside single quotes. A single quote in the
|
# Quoting has already been parsed. Quotes in a response stay literal data.
|
||||||
# replacement must stay data and must not close the surrounding quotes.
|
|
||||||
payload="can't ; set -g @marker changed ; done"
|
payload="can't ; set -g @marker changed ; done"
|
||||||
accept_prompt M-s "$payload"
|
accept_prompt M-s "$payload"
|
||||||
wait_option @r "$payload"
|
wait_option @r "$payload"
|
||||||
@@ -139,18 +158,19 @@ accept_prompt M-f "$payload"
|
|||||||
wait_option @first "$payload"
|
wait_option @first "$payload"
|
||||||
wait_option @second %%
|
wait_option @second %%
|
||||||
|
|
||||||
# %%% keeps the previous double-quote escaping behaviour.
|
# %%% is accepted like %%, without adding quotation escapes to the value.
|
||||||
reset_options
|
reset_options
|
||||||
payload='a"$;~\z'
|
payload='a"$;~\z'
|
||||||
accept_prompt M-d "$payload"
|
accept_prompt M-d "$payload"
|
||||||
wait_option @r "$payload"
|
wait_option @r "$payload"
|
||||||
|
|
||||||
# %1 is intentionally left raw as an escape hatch.
|
# Indexed replacements are also data, so semicolons cannot add commands.
|
||||||
reset_options
|
reset_options
|
||||||
$IN set -g @raw_tail unchanged || exit 1
|
$IN set -g @raw_tail unchanged || exit 1
|
||||||
accept_prompt M-r 'raw ; set -g @raw_tail yes'
|
payload='raw ; set -g @raw_tail yes'
|
||||||
wait_option @r raw
|
accept_prompt M-r "$payload"
|
||||||
wait_option @raw_tail yes
|
wait_option @r "$payload"
|
||||||
|
wait_option @raw_tail unchanged
|
||||||
|
|
||||||
# All instances of %1 are replaced.
|
# All instances of %1 are replaced.
|
||||||
reset_options
|
reset_options
|
||||||
@@ -166,12 +186,56 @@ wait_option @one one
|
|||||||
wait_option @two two
|
wait_option @two two
|
||||||
wait_option @two_again two
|
wait_option @two_again two
|
||||||
|
|
||||||
# %idx% uses double-quote escaping for indexed replacements.
|
# %idx% is accepted like %idx, with no quotation escaping.
|
||||||
reset_options
|
reset_options
|
||||||
payload='b"$;~\y'
|
payload='b"$;~\y'
|
||||||
accept_two_prompts M-i ignored "$payload"
|
accept_two_prompts M-i ignored "$payload"
|
||||||
wait_option @double_index "$payload"
|
wait_option @double_index "$payload"
|
||||||
|
|
||||||
|
# Responses are not scanned again for markers from later prompt values.
|
||||||
|
reset_options
|
||||||
|
accept_two_prompts M-m '%2/%%' two
|
||||||
|
wait_option @one '%2/%%'
|
||||||
|
wait_option @two two
|
||||||
|
wait_option @two_again two
|
||||||
|
|
||||||
|
# A missing response and invalid or trailing percent markers stay literal.
|
||||||
|
reset_options
|
||||||
|
accept_prompt M-u unused
|
||||||
|
wait_option @plain '%9 %0 %'
|
||||||
|
|
||||||
|
# Reuse the stored string tree with a different response. Expansion must not
|
||||||
|
# mutate the tree or carry the first-%% state across separate invocations.
|
||||||
|
reset_options
|
||||||
|
payload="another'quote"
|
||||||
|
accept_prompt M-s "$payload"
|
||||||
|
wait_option @r "$payload"
|
||||||
|
|
||||||
|
# Braced bodies preserve the same first-%% rule across commands and within an
|
||||||
|
# argument. Indexed replacements do not consume this first-%% marker.
|
||||||
|
reset_options
|
||||||
|
payload="brace'quote ; set -g @marker changed"
|
||||||
|
accept_prompt M-b "$payload"
|
||||||
|
wait_option @r "$payload"
|
||||||
|
wait_option @marker unchanged
|
||||||
|
accept_prompt M-t "$payload"
|
||||||
|
wait_option @first "$payload"
|
||||||
|
wait_option @second %%
|
||||||
|
accept_prompt M-w value
|
||||||
|
wait_option @r 'value/%%'
|
||||||
|
|
||||||
|
reset_options
|
||||||
|
accept_two_prompts M-v '%2/%%' two
|
||||||
|
wait_option @one '%2/%%'
|
||||||
|
wait_option @two two
|
||||||
|
wait_option @two_again two
|
||||||
|
wait_option @r '%2/%%'
|
||||||
|
payload="can't ; set -g @marker changed"
|
||||||
|
payload="$payload"' ; "$HOME" ~ \ %2'
|
||||||
|
accept_prompt M-q "$payload"
|
||||||
|
wait_option @r "$payload"
|
||||||
|
wait_option @marker unchanged
|
||||||
|
|
||||||
# A template without replacement markers is left alone.
|
# A template without replacement markers is left alone.
|
||||||
reset_options
|
reset_options
|
||||||
accept_prompt M-n 'unused ; set -g @marker changed'
|
accept_prompt M-n 'unused ; set -g @marker changed'
|
||||||
|
|||||||
11
tmux.1
11
tmux.1
@@ -8089,10 +8089,17 @@ Up to nine prompt responses may be replaced
|
|||||||
to
|
to
|
||||||
.Ql %9
|
.Ql %9
|
||||||
.Pc .
|
.Pc .
|
||||||
|
The template is parsed before prompting, and responses are substituted literally
|
||||||
|
into its argument values without being parsed again.
|
||||||
|
Spaces, quotes and semicolons in a response do not add arguments or commands.
|
||||||
.Ql %%%
|
.Ql %%%
|
||||||
is like
|
is accepted like
|
||||||
.Ql %%
|
.Ql %%
|
||||||
but any quotation marks are escaped.
|
and
|
||||||
|
.Ql %1%
|
||||||
|
like
|
||||||
|
.Ql %1 ,
|
||||||
|
without adding escapes.
|
||||||
.Pp
|
.Pp
|
||||||
.Fl 1
|
.Fl 1
|
||||||
makes the prompt only accept one key press, in this case the resulting input
|
makes the prompt only accept one key press, in this case the resulting input
|
||||||
|
|||||||
Reference in New Issue
Block a user