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;
|
||||
char **argv;
|
||||
int replaced;
|
||||
};
|
||||
|
||||
static void cmd_invoke_push(struct cmd_invoke_state *,
|
||||
@@ -182,6 +183,47 @@ cmd_invoke_tilde(const char *name)
|
||||
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. */
|
||||
static int
|
||||
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;
|
||||
char *buf = NULL, *new;
|
||||
size_t len = 0;
|
||||
int i;
|
||||
|
||||
child = cmd_parse_node_first_child(node);
|
||||
while (child != NULL) {
|
||||
@@ -214,8 +255,8 @@ cmd_invoke_expand_string(struct cmdq_item *item, struct cmd_invoke_state *is,
|
||||
}
|
||||
if (buf == NULL)
|
||||
buf = xstrdup("");
|
||||
for (i = 0; i < is->argc; i++) {
|
||||
new = cmd_template_replace(buf, is->argv[i], i + 1);
|
||||
if (is->argc != 0) {
|
||||
new = cmd_invoke_replace(is, buf);
|
||||
free(buf);
|
||||
buf = new;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Exercise cmd_template_replace through command-prompt, which passes prompt
|
||||
# input as argv to args_make_commands. This covers the quoting modes, indexed
|
||||
# replacements, and cases where replacements are intentionally not made.
|
||||
# Exercise invocation-time template replacement through command-prompt. String
|
||||
# and braced templates are parsed before prompting; responses are literal argv
|
||||
# values and cannot add arguments or commands to the stored tree.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
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)' \
|
||||
"set -g @plain no-template-markers" ||
|
||||
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 set -g status off || exit 1
|
||||
@@ -125,8 +145,7 @@ sleep 1
|
||||
|
||||
reset_options
|
||||
|
||||
# %% is for templates already inside single quotes. A single quote in the
|
||||
# replacement must stay data and must not close the surrounding quotes.
|
||||
# Quoting has already been parsed. Quotes in a response stay literal data.
|
||||
payload="can't ; set -g @marker changed ; done"
|
||||
accept_prompt M-s "$payload"
|
||||
wait_option @r "$payload"
|
||||
@@ -139,18 +158,19 @@ accept_prompt M-f "$payload"
|
||||
wait_option @first "$payload"
|
||||
wait_option @second %%
|
||||
|
||||
# %%% keeps the previous double-quote escaping behaviour.
|
||||
# %%% is accepted like %%, without adding quotation escapes to the value.
|
||||
reset_options
|
||||
payload='a"$;~\z'
|
||||
accept_prompt M-d "$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
|
||||
$IN set -g @raw_tail unchanged || exit 1
|
||||
accept_prompt M-r 'raw ; set -g @raw_tail yes'
|
||||
wait_option @r raw
|
||||
wait_option @raw_tail yes
|
||||
payload='raw ; set -g @raw_tail yes'
|
||||
accept_prompt M-r "$payload"
|
||||
wait_option @r "$payload"
|
||||
wait_option @raw_tail unchanged
|
||||
|
||||
# All instances of %1 are replaced.
|
||||
reset_options
|
||||
@@ -166,12 +186,56 @@ wait_option @one one
|
||||
wait_option @two 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
|
||||
payload='b"$;~\y'
|
||||
accept_two_prompts M-i ignored "$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.
|
||||
reset_options
|
||||
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
|
||||
.Ql %9
|
||||
.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 %%%
|
||||
is like
|
||||
is accepted like
|
||||
.Ql %%
|
||||
but any quotation marks are escaped.
|
||||
and
|
||||
.Ql %1%
|
||||
like
|
||||
.Ql %1 ,
|
||||
without adding escapes.
|
||||
.Pp
|
||||
.Fl 1
|
||||
makes the prompt only accept one key press, in this case the resulting input
|
||||
|
||||
Reference in New Issue
Block a user