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 <grishale...@gmail.com>
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

Reply via email to