rl_trim_arg_from_keyseq fails to trim a negative argument entered with
universal-argument and fails to trim arguments if they have multiple
leading minus signs.
Also, when an argument is provided using universal-argument but without
typing any digits, rl_explicit_arg does not get set. Assuming that's
intentional, bash_execute_unix_command (and maybe other functions) can't
rely on tests like `if (count > 1 || rl_explicit_arg)' to check for an
argument since `count' is negative after `\C-u-'.
Example:
bind 'set bind-tty-special-chars off'
bind '"\C-u": universal-argument'
bind -x '"\eX": "declare -p READLINE_ARGUMENT"'
Followed any of: "\e--\eX" "\C-u-\eX" "\C-u--\eX", etc. all result in:
bash: bash_execute_unix_command: cannot find keymap for command
From 250618affbd145c120862118a59530e8ffd6dbec Mon Sep 17 00:00:00 2001
From: Grisha Levit <[email protected]>
Date: Tue, 21 Nov 2023 00:53:20 -0500
Subject: [PATCH] negative arg trimming
---
bashline.c | 4 ++--
lib/readline/bind.c | 25 ++++++++++++-------------
2 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/bashline.c b/bashline.c
index c767e1f5..774f813f 100644
--- a/bashline.c
+++ b/bashline.c
@@ -4484,7 +4484,7 @@ bash_execute_unix_command (int count, int key)
kslen = rl_key_sequence_length;
/* If we have a numeric argument, chop it off the front of the key sequence */
- if (count > 1 || rl_explicit_arg)
+ if (count != 1 || rl_explicit_arg)
{
i = rl_trim_arg_from_keyseq (rl_executing_keyseq, rl_key_sequence_length, rl_get_keymap ());
if (i > 0)
@@ -4536,7 +4536,7 @@ bash_execute_unix_command (int count, int key)
if (v)
VSETATTR (v, att_exported);
- if (count > 1 || rl_explicit_arg)
+ if (count != 1 || rl_explicit_arg)
{
value = inttostr (count, ibuf, sizeof (ibuf));
v = bind_int_variable ("READLINE_ARGUMENT", value, 0);
diff --git a/lib/readline/bind.c b/lib/readline/bind.c
index 3a44fefb..1c347fbe 100644
--- a/lib/readline/bind.c
+++ b/lib/readline/bind.c
@@ -881,14 +881,19 @@ rl_trim_arg_from_keyseq (const char *keyseq, size_t len, Keymap map)
map = _rl_keymap;
map0 = map;
- /* The digits following the initial one (e.g., the binding to digit-argument)
- or the optional `-' in a binding to digit-argument or universal-argument
- are not added to rl_executing_keyseq. This is basically everything read by
- rl_digit_loop. The parsing_digits logic is here in case they ever are. */
for (i = j = parsing_digits = 0; keyseq && i < len; i++)
{
ic = keyseq[i];
+ if (parsing_digits == 2)
+ {
+ if (ic == '-')
+ {
+ j = i + 1;
+ continue;
+ }
+ parsing_digits = 1;
+ }
if (parsing_digits)
{
if (_rl_digit_p (ic))
@@ -924,15 +929,9 @@ rl_trim_arg_from_keyseq (const char *keyseq, size_t len, Keymap map)
/* This logic should be identical to rl_digit_loop */
/* We accept M-- as equivalent to M--1, C-u- as equivalent to C-u-1
but set parsing_digits to 2 to note that we saw `-' */
- if (map[ic].function == rl_universal_argument && (i + 1 == '-'))
- {
- i++;
- parsing_digits = 2;
- }
- if (map[ic].function == rl_digit_argument && ic == '-')
- {
- parsing_digits = 2;
- }
+ if (map[ic].function == rl_universal_argument ||
+ (map[ic].function == rl_digit_argument && ic == '-'))
+ parsing_digits = 2;
map = map0;
j = i + 1;
--
2.42.1