]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
new bindable readline command `execute-named-command', bound to M-x in emacs mode
authorChet Ramey <chet.ramey@case.edu>
Mon, 6 Nov 2023 14:44:01 +0000 (09:44 -0500)
committerChet Ramey <chet.ramey@case.edu>
Mon, 6 Nov 2023 14:44:01 +0000 (09:44 -0500)
CWRU/CWRU.chlog
doc/bashref.texi
general.h
lib/readline/doc/rluser.texi
lib/readline/doc/version.texi
lib/readline/emacs_keymap.c
lib/readline/funmap.c
lib/readline/readline.h
lib/readline/rlprivate.h
lib/readline/text.c

index d1e6fe86f08fde486da687a5de774f7e2890397c..808a061c5526f3e4a207222cef9ecf95eeb9147b 100644 (file)
@@ -7946,3 +7946,30 @@ subst.c
          single-element vector with a copy of the original word so we can
          add it to the result list unchanged.
          Inspired by https://savannah.gnu.org/support/?110948
+
+                                  11/3
+                                  ----
+lib/readline/text.c
+       - readstr: set of functions to read a string from the keyboard, using
+         rl_line_buffer for temporary storage, with minimal editing and an
+         optional caller-supplied completion function. Doesn't use the
+         callback framework yet since none of the functions are public
+       - rl_execute_named_command: new bindable function to read a bindable
+         command name (from the funmap_names array) and execute it, with
+         command name completion on SPACE and TAB
+
+lib/readline/readline.h
+       - rl_execute_named_command: new extern declaration
+
+lib/readline/rlprivate.h
+       - new extern declarations for the readstr function framework
+
+lib/readline/funmap.c
+       - execute-named-command: new bindable function name
+
+lib/readline/emacs_keymap.c
+       - rl_execute_named_function: bound to M-x by default
+
+lib/readline/doc/rluser.texi
+       - execute-named-command: document as bindable function name with its
+         default binding to M-x in emacs mode
index f79ad8d27e3af063ba4e2cebb3a7a66018ece874..41db2ed5d2467b40097025f3ce0af61555deaacf 100644 (file)
@@ -6135,8 +6135,8 @@ The Bash @sc{posix} mode is described in @ref{Bash POSIX Mode}.
 
 These are the @sc{posix} special builtins:
 @example
-@w{break : . continue eval exec exit export readonly return set}
-@w{shift trap unset}
+@w{break : . source continue eval exec exit export readonly return set}
+@w{shift times trap unset}
 @end example
 
 @node Shell Variables
index 2320c67a9cf6e4fbcbf65a284856da0210eb43e7..ecbc887761559d8a90f1deea05839bc603adda4c 100644 (file)
--- a/general.h
+++ b/general.h
@@ -159,7 +159,9 @@ STREQ(const char *a, const char *b)
 static inline int
 STREQN(const char *a, const char *b, size_t n)
 {
-  return ((n == 0) || ((a)[0] == (b)[0] && strncmp(a, b, n) == 0));
+  return ((n == 0) || 
+         (n == 1 && a[0] == b[0]) ||
+         ((a)[0] == (b)[0] && strncmp(a, b, n) == 0));
 }
 
 /* More convenience definitions that possibly save system or libc calls. */
index a0cf21c805ceb8e7785d66118d87767f6f62a205..72d951aa764cff35f6a8c426111bcfd6ad1f1d2e 100644 (file)
@@ -1912,6 +1912,13 @@ editing mode.
 
 @end ifclear
 
+@item execute-named-command (M-x)
+Read a bindable readline command name from the input and execute the
+function to which it's bound, as if the key sequence to which it was
+bound appeared in the input.
+If this function is supplied with a numeric argument, it passes that
+argument to the function it executes.
+
 @end ftable
 
 @node Readline vi Mode
index f1b82d410383425c224aa35c23e4ba23f97dfaf4..aa585cde726e3353e9d9e8df3c3e80031e4fda77 100644 (file)
@@ -5,7 +5,7 @@ Copyright (C) 1988-2023 Free Software Foundation, Inc.
 @set EDITION 8.3
 @set VERSION 8.3
 
-@set UPDATED 4 October 2023
-@set UPDATED-MONTH October 2023
+@set UPDATED 3 November 2023
+@set UPDATED-MONTH November 2023
 
-@set LASTCHANGE Wed Oct  4 10:57:26 EDT 2023
+@set LASTCHANGE Fri Nov  3 12:04:26 EDT 2023
index 02597dad31a4811bb55d44a30914b7b955eb2871..cf2adcc1a015d3b9865bed397c25cb210f9d6bed 100644 (file)
@@ -448,7 +448,7 @@ KEYMAP_ENTRY_ARRAY emacs_meta_keymap = {
   { ISFUNC, rl_upcase_word },          /* Meta-u */
   { ISFUNC, (rl_command_func_t *)0x0 },        /* Meta-v */
   { ISFUNC, (rl_command_func_t *)0x0 },        /* Meta-w */
-  { ISFUNC, (rl_command_func_t *)0x0 },        /* Meta-x */
+  { ISFUNC, rl_execute_named_command },        /* Meta-x */
   { ISFUNC, rl_yank_pop },             /* Meta-y */
   { ISFUNC, (rl_command_func_t *)0x0 },        /* Meta-z */
 
index affa0fdcf1a74d32fb68671afa8ef8b7e63c66bb..548cfb572396de58a392adc765930df86395de82 100644 (file)
@@ -89,6 +89,7 @@ static const FUNMAP default_funmap[] = {
   { "end-of-history", rl_end_of_history },
   { "end-of-line", rl_end_of_line },
   { "exchange-point-and-mark", rl_exchange_point_and_mark },
+  { "execute-named-command", rl_execute_named_command },
   { "fetch-history", rl_fetch_history },
   { "forward-backward-delete-char", rl_rubout_or_delete },
   { "forward-byte", rl_forward_byte },
index 82b223437e8667c905a9eb1653e0fb95fd133362..d300e6a0e57fa3138bdafa1639c78db19f05d909 100644 (file)
@@ -209,6 +209,8 @@ extern int rl_stop_output (int, int);
 extern int rl_abort (int, int);
 extern int rl_tty_status (int, int);
 
+extern int rl_execute_named_command (int, int);
+
 /* Bindable commands for incremental and non-incremental history searching. */
 extern int rl_history_search_forward (int, int);
 extern int rl_history_search_backward (int, int);
index c3e43e398ae1eabe5753978b55b9585ecc7ae042..461617c4b691362776db8d6e25bb7fd2d476c0e2 100644 (file)
@@ -110,6 +110,27 @@ typedef struct  __rl_search_context
   char  *search_terminators;
 } _rl_search_cxt;
 
+/* readstr flags */
+#define RL_READSTR_NOSPACE     0x01    /* don't insert space, use for completion */
+
+typedef struct  __rl_readstr_context
+{
+  int flags;
+
+  int prevc;
+  int lastc;
+#if defined (HANDLE_MULTIBYTE)
+  char mb[MB_LEN_MAX];
+  char pmb[MB_LEN_MAX];
+#endif
+
+  int save_point;
+  int save_mark;
+  int save_line;
+
+  int (*compfunc) (struct __rl_readstr_context *, int);
+} _rl_readstr_cxt;
+
 struct _rl_cmd {
   Keymap map;
   int count;
@@ -461,6 +482,16 @@ extern int _rl_char_search_internal (int, int, int);
 #endif
 extern int _rl_set_mark_at_pos (int);
 
+extern _rl_readstr_cxt *_rl_rscxt_alloc (int);
+extern void _rl_rscxt_dispose (_rl_readstr_cxt *, int);
+extern void _rl_free_saved_readstr_line (void);
+extern void _rl_unsave_saved_readstr_line (void);
+extern _rl_readstr_cxt *_rl_readstr_init (int, int);
+extern int _rl_readstr_cleanup (_rl_readstr_cxt *, int);
+extern void _rl_readstr_restore (_rl_readstr_cxt *);
+extern int _rl_readstr_getchar (_rl_readstr_cxt *);
+extern int _rl_readstr_dispatch (_rl_readstr_cxt *, int);
+
 /* undo.c */
 extern UNDO_LIST *_rl_copy_undo_entry (UNDO_LIST *);
 extern UNDO_LIST *_rl_copy_undo_list (UNDO_LIST *);
@@ -635,6 +666,8 @@ extern int _rl_term_autowrap;
 extern int _rl_optimize_typeahead;
 extern int _rl_keep_mark_active;
 
+extern _rl_readstr_cxt *_rl_rscxt;
+
 /* undo.c */
 extern int _rl_doing_an_undo;
 extern int _rl_undo_group_level;
index e3e5bb9eb0c82582af0b5c28a3ba5fe7993f3e27..933536add9f452451b63995277b18151361a9807 100644 (file)
@@ -1928,3 +1928,421 @@ rl_mark_active_p (void)
 {
   return (mark_active);
 }
+
+/* **************************************************************** */
+/*                                                                 */
+/*           Reading a string entered from the keyboard            */
+/*                                                                 */
+/* **************************************************************** */
+
+/* A very simple set of functions to read a string from the keyboard using
+   the line buffer as temporary storage. The caller can set a completion
+   function to perform completion on TAB and SPACE. */
+
+/* XXX - this is all very similar to the search stuff but with a different
+   CXT. */
+
+static HIST_ENTRY *_rl_saved_line_for_readstr;
+_rl_readstr_cxt *_rl_rscxt;
+
+_rl_readstr_cxt *
+_rl_rscxt_alloc (int flags)
+{
+  _rl_readstr_cxt *cxt;
+
+  cxt = (_rl_readstr_cxt *)xmalloc (sizeof (_rl_readstr_cxt));
+
+  cxt->flags = flags;
+
+  cxt->save_point = rl_point;
+  cxt->save_mark = rl_mark;
+  cxt->save_line = where_history ();
+
+  cxt->prevc = cxt->lastc = 0;
+
+  cxt->compfunc = NULL;
+
+  return cxt;
+}
+
+void
+_rl_rscxt_dispose (_rl_readstr_cxt *cxt, int flags)
+{
+  xfree (cxt);
+}
+
+void
+_rl_free_saved_readstr_line ()
+{
+  if (_rl_saved_line_for_readstr)
+    _rl_free_saved_line (_rl_saved_line_for_readstr);
+  _rl_saved_line_for_readstr = (HIST_ENTRY *)NULL;
+}
+
+void
+_rl_unsave_saved_readstr_line ()
+{
+  if (_rl_saved_line_for_readstr)
+    _rl_unsave_line (_rl_saved_line_for_readstr);
+  _rl_saved_line_for_readstr = (HIST_ENTRY *)NULL;
+}
+
+_rl_readstr_cxt *
+_rl_readstr_init (int pchar, int flags)
+{
+  _rl_readstr_cxt *cxt;
+  char *p;  
+
+  cxt = _rl_rscxt_alloc (flags);
+
+  rl_maybe_replace_line ();
+  _rl_saved_line_for_readstr = _rl_alloc_saved_line ();
+
+  rl_undo_list = 0;
+
+  rl_line_buffer[0] = 0;
+  rl_end = rl_point = 0;
+
+  p = _rl_make_prompt_for_search (pchar ? pchar : '@');
+  rl_message ("%s", p);
+  xfree (p);
+
+  _rl_rscxt = cxt;  
+
+  return cxt;
+}
+
+int
+_rl_readstr_cleanup (_rl_readstr_cxt *cxt, int r)
+{
+  _rl_rscxt_dispose (cxt, 0);
+  _rl_rscxt = 0;
+
+  return (r != 1);
+}
+
+void
+_rl_readstr_restore (_rl_readstr_cxt *cxt)
+{
+  _rl_unsave_saved_readstr_line ();    /* restores rl_undo_list */
+  rl_point = cxt->save_point;
+  rl_mark = cxt->save_mark;
+  rl_restore_prompt ();                /* _rl_make_prompt_for_search saved it */
+  rl_clear_message ();
+  _rl_fix_point (1);
+}
+
+int   
+_rl_readstr_getchar (_rl_readstr_cxt *cxt)
+{
+  int c;   
+
+  cxt->prevc = cxt->lastc;        
+
+  /* Read a key and decide how to proceed. */
+  RL_SETSTATE(RL_STATE_MOREINPUT);
+  c = cxt->lastc = rl_read_key ();
+  RL_UNSETSTATE(RL_STATE_MOREINPUT);
+                 
+#if defined (HANDLE_MULTIBYTE)
+  /* This ends up with C (and LASTC) being set to the last byte of the
+     multibyte character.  In most cases c == lastc == mb[0] */
+  if (c >= 0 && MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+    c = cxt->lastc = _rl_read_mbstring (cxt->lastc, cxt->mb, MB_LEN_MAX);
+#endif
+
+  RL_CHECK_SIGNALS ();
+  return c;
+}
+
+/* Process just-read character C according to readstr context CXT.  Return -1
+   if the caller should abort the read, 0 if we should break out of the
+   loop, and 1 if we should continue to read characters. This can perform
+   completion on the string read so far (stored in rl_line_buffer) if the
+   caller has set up a completion function. The completion function can
+   return -1 to indicate that we should abort the read. If we return -1
+   we will call _rl_readstr_restore to clean up the state, leaving the caller
+   to free the context. */
+int
+_rl_readstr_dispatch (_rl_readstr_cxt *cxt, int c)
+{
+  int n;
+
+  if (c < 0)
+    c = CTRL ('C');  
+
+  switch (c)
+    {
+    case CTRL('W'):
+      rl_unix_word_rubout (1, c);
+      break;
+
+    case CTRL('U'):
+      rl_unix_line_discard (1, c);
+      break;
+
+    case CTRL('Q'):
+    case CTRL('V'):
+      n = rl_quoted_insert (1, c);
+      if (n < 0)
+       {
+         _rl_readstr_restore (cxt);
+         return -1;
+       }
+      cxt->lastc = rl_line_buffer[rl_point - 1];       /* preserve prevc */
+      break;
+
+    case RETURN:
+    case NEWLINE:
+      return 0;
+
+    case CTRL('H'):
+    case RUBOUT:
+      if (rl_point == 0)
+       {
+         _rl_readstr_restore (cxt);
+         return -1;
+       }
+      _rl_rubout_char (1, c);
+      break;
+
+    case CTRL('C'):
+    case CTRL('G'):
+      rl_ding ();
+      _rl_readstr_restore (cxt);
+      return -1;
+
+    case ESC:
+      /* Allow users to bracketed-paste text into the string.
+        Similar code is in search.c:_rl_nsearch_dispatch(). */
+      if (_rl_enable_bracketed_paste && ((n = _rl_nchars_available ()) >= (BRACK_PASTE_SLEN-1)))
+       {
+         if (_rl_read_bracketed_paste_prefix (c) == 1)
+           rl_bracketed_paste_begin (1, c);
+         else
+           {
+             c = rl_read_key ();       /* get the ESC that got pushed back */
+             _rl_insert_char (1, c);
+           }
+        }
+      else
+        _rl_insert_char (1, c);
+      break;
+
+    case ' ':
+      if ((cxt->flags & RL_READSTR_NOSPACE) == 0)
+       {
+         _rl_insert_char (1, c);
+         break;
+       }
+    /* FALLTHROUGH */
+    case TAB:
+      /* Perform completion if the caller has set a completion function. */
+      n = (cxt->compfunc) ? (*cxt->compfunc) (cxt, c) : _rl_insert_char (1, c);
+      if (n < 0)
+       {
+         _rl_readstr_restore (cxt);
+         return -1;
+       }
+      break;
+
+    default:
+#if defined (HANDLE_MULTIBYTE)
+      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
+       rl_insert_text (cxt->mb);
+      else
+#endif
+       _rl_insert_char (1, c);
+      break;
+    }
+
+  (*rl_redisplay_function) ();
+  rl_deactivate_mark ();
+  return 1;
+}
+
+/* **************************************************************** */
+/*                                                                 */
+/*             Reading and Executing named commands                */
+/*                                                                 */
+/* **************************************************************** */
+
+/* A completion generator for bindable readline command names. */
+static char *
+readcmd_completion_function (const char *text, int state)
+{
+  static const char **cmdlist = NULL;
+  static size_t lind, nlen;
+  const char *cmdname;
+
+  if (state == 0)
+    {
+      if (cmdlist)
+       free (cmdlist);
+
+      cmdlist = rl_funmap_names ();
+      lind = 0;
+      nlen = RL_STRLEN (text);
+    }
+  if (cmdlist == 0 || cmdlist[lind] == 0)
+    return (char *)NULL;
+
+  while (cmdlist[lind])
+    {
+      cmdname = cmdlist[lind++];
+      if (STREQN (text, cmdname, nlen))
+       return (savestring (cmdname));
+    }
+  return ((char *)NULL);
+}
+
+static void
+_rl_display_cmdname_matches (char **matches)
+{
+  size_t len, max, i;
+  int old;
+
+  old = rl_filename_completion_desired;
+  rl_filename_completion_desired = 0;
+
+  /* There is more than one match. Find out how many there are,
+     and find the maximum printed length of a single entry. */
+  for (max = 0, i = 1; matches[i]; i++)
+    {
+      len = strlen (matches[i]);
+
+      if (len > max)
+       max = len;
+    }
+  len = i - 1;
+
+  rl_display_match_list (matches, len, max);
+  rl_filename_completion_desired = old;
+
+  rl_forced_update_display ();
+  rl_display_fixed = 1;
+}
+
+static int
+_rl_readcmd_complete (_rl_readstr_cxt *cxt, int c)
+{
+  char **matches;
+  char *prefix;
+  size_t plen;
+
+  matches = rl_completion_matches (rl_line_buffer, readcmd_completion_function);
+
+  if (RL_SIG_RECEIVED())
+    {
+      _rl_free_match_list (matches);
+      matches = 0;
+      RL_CHECK_SIGNALS ();
+      return -1;
+    }
+  else if (matches == 0)
+    rl_ding ();
+
+  /* Whether or not there are multiple matches, we just want to append the
+     new characters in matches[0]. We display possible matches if we didn't
+     append anything. */
+  if (matches)
+    {
+      prefix = matches[0];
+      plen = strlen (prefix);
+
+      if (plen > rl_end)
+        {
+          size_t n;
+          for (n = rl_end; n < plen && prefix[n]; n++)
+            _rl_insert_char (1, prefix[n]);
+        }
+      else if (matches[1])
+       _rl_display_cmdname_matches (matches);
+      _rl_free_match_list (matches);
+    }
+
+  return 0;
+}
+
+/* Use the readstr functions to read a bindable command name using the
+   line buffer, with completion. */
+static char *
+_rl_read_command_name ()
+{
+  _rl_readstr_cxt *cxt;
+  char *ret;
+  int c, r;
+
+  cxt = _rl_readstr_init ('!', RL_READSTR_NOSPACE);
+  cxt->compfunc = _rl_readcmd_complete;
+
+  /* skip callback stuff for now */
+  r = 0;
+  while (1)
+    {
+      c = _rl_readstr_getchar (cxt);
+
+      if (c < 0)
+       {
+         _rl_readstr_restore (cxt);
+         _rl_readstr_cleanup (cxt, r);
+         return NULL;
+       }
+
+      if (c == 0)
+       break;
+
+      r = _rl_readstr_dispatch (cxt, c);
+      if (r < 0)
+       {
+         _rl_readstr_cleanup (cxt, r);
+         return NULL;          /* dispatch function cleans up */
+       }
+      else if (r == 0)
+       break;
+    }
+
+  ret = savestring (rl_line_buffer);
+
+  /* Now restore the original line and perform one final redisplay. */
+  _rl_readstr_restore (cxt);
+  (*rl_redisplay_function) ();
+
+  /* And free up the context. */
+  _rl_readstr_cleanup (cxt, r);
+  return ret;
+}
+
+/* Read a command name from the keyboard and execute it as if the bound key
+   sequence had been entered. */
+int
+rl_execute_named_command (int count, int key)
+{
+  char *command;
+  rl_command_func_t *func;
+  int r;
+
+  command = _rl_read_command_name ();
+  if (command == 0 || *command == '\0')
+    return 1;
+  if (func = rl_named_function (command))
+    {
+      int prev, ostate;
+
+      prev = rl_dispatching;
+      ostate = RL_ISSTATE (RL_STATE_DISPATCHING);
+      rl_dispatching = 1;
+      RL_SETSTATE (RL_STATE_DISPATCHING);      /* make sure it's set */
+      r = (*func) (count, key);
+      if (ostate == 0)
+       RL_UNSETSTATE (RL_STATE_DISPATCHING);   /* unset it if it wasn't set */
+      rl_dispatching = prev;
+    }
+  else
+    {
+      rl_ding ();
+      r = 1;
+    }
+
+  return r;
+}