Bash-4.3 distribution sources and documentation
[platform/upstream/bash.git] / lib / readline / bind.c
index 15d0e4e..8acf4ac 100644 (file)
@@ -1,24 +1,23 @@
 /* bind.c -- key binding and startup file support for the readline library. */
 
-/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2012 Free Software Foundation, Inc.
 
-   This file is part of the GNU Readline Library, a library for
-   reading lines of text with interactive input and history editing.
+   This file is part of the GNU Readline Library (Readline), a library
+   for reading lines of text with interactive input and history editing.
 
-   The GNU Readline Library is free software; you can redistribute it
-   and/or modify it under the terms of the GNU General Public License
-   as published by the Free Software Foundation; either version 2, or
+   Readline is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
-   The GNU Readline Library is distributed in the hope that it will be
-   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   Readline is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
-   The GNU General Public License is often shipped with GNU software, and
-   is generally kept in a file called COPYING or LICENSE.  If you do not
-   have a copy of the license, write to the Free Software Foundation,
-   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+   You should have received a copy of the GNU General Public License
+   along with Readline.  If not, see <http://www.gnu.org/licenses/>.
+*/
 
 #define READLINE_LIBRARY
 
@@ -73,11 +72,18 @@ extern char *strchr (), *strrchr ();
 /* Variables exported by this file. */
 Keymap rl_binding_keymap;
 
+static int _rl_skip_to_delim PARAMS((char *, int, int));
+
 static char *_rl_read_file PARAMS((char *, size_t *));
 static void _rl_init_file_error PARAMS((const char *));
 static int _rl_read_init_file PARAMS((const char *, int));
 static int glean_key_from_name PARAMS((char *));
-static int substring_member_of_array PARAMS((char *, const char **));
+
+static int find_boolean_var PARAMS((const char *));
+static int find_string_var PARAMS((const char *));
+
+static char *_rl_get_string_variable_value PARAMS((const char *));
+static int substring_member_of_array PARAMS((const char *, const char * const *));
 
 static int currently_reading_init_file;
 
@@ -314,7 +320,7 @@ rl_macro_bind (keyseq, macro, map)
 
   if (rl_translate_keyseq (macro, macro_keys, &macro_keys_len))
     {
-      free (macro_keys);
+      xfree (macro_keys);
       return -1;
     }
   rl_generic_bind (ISMACR, keyseq, macro_keys, map);
@@ -341,10 +347,10 @@ rl_generic_bind (type, keyseq, data, map)
   k.function = 0;
 
   /* If no keys to bind to, exit right away. */
-  if (!keyseq || !*keyseq)
+  if (keyseq == 0 || *keyseq == 0)
     {
       if (type == ISMACR)
-       free (data);
+       xfree (data);
       return -1;
     }
 
@@ -355,7 +361,7 @@ rl_generic_bind (type, keyseq, data, map)
      KEYS into KEYS_LEN. */
   if (rl_translate_keyseq (keyseq, keys, &keys_len))
     {
-      free (keys);
+      xfree (keys);
       return -1;
     }
 
@@ -367,9 +373,12 @@ rl_generic_bind (type, keyseq, data, map)
 
       ic = uc;
       if (ic < 0 || ic >= KEYMAP_SIZE)
-       return -1;
+        {
+          xfree (keys);
+         return -1;
+        }
 
-      if (_rl_convert_meta_chars_to_ascii && META_CHAR (ic))
+      if (META_CHAR (ic) && _rl_convert_meta_chars_to_ascii)
        {
          ic = UNMETA (ic);
          if (map[ESC].type == ISKMAP)
@@ -408,11 +417,18 @@ rl_generic_bind (type, keyseq, data, map)
       else
        {
          if (map[ic].type == ISMACR)
-           free ((char *)map[ic].function);
+           xfree ((char *)map[ic].function);
          else if (map[ic].type == ISKMAP)
            {
              map = FUNCTION_TO_KEYMAP (map, ic);
              ic = ANYOTHERKEY;
+             /* If we're trying to override a keymap with a null function
+                (e.g., trying to unbind it), we can't use a null pointer
+                here because that's indistinguishable from having not been
+                overridden.  We use a special bindable function that does
+                nothing. */
+             if (type == ISFUNC && data == 0)
+               data = (char *)_rl_null_function;
            }
 
          map[ic].function = KEYMAP_TO_FUNCTION (data);
@@ -421,7 +437,7 @@ rl_generic_bind (type, keyseq, data, map)
 
       rl_binding_keymap = map;
     }
-  free (keys);
+  xfree (keys);
   return 0;
 }
 
@@ -459,8 +475,24 @@ rl_translate_keyseq (seq, array, len)
                }
              else if (c == 'M')
                {
-                 i++;
-                 array[l++] = ESC;     /* ESC is meta-prefix */
+                 i++;          /* seq[i] == '-' */
+                 /* XXX - obey convert-meta setting */
+                 if (_rl_convert_meta_chars_to_ascii && _rl_keymap[ESC].type == ISKMAP)
+                   array[l++] = ESC;   /* ESC is meta-prefix */
+                 else if (seq[i+1] == '\\' && seq[i+2] == 'C' && seq[i+3] == '-')
+                   {
+                     i += 4;
+                     temp = (seq[i] == '?') ? RUBOUT : CTRL (_rl_to_upper (seq[i]));
+                     array[l++] = META (temp);
+                   }
+                 else
+                   {
+                     /* This doesn't yet handle things like \M-\a, which may
+                        or may not have any reasonable meaning.  You're
+                        probably better off using straight octal or hex. */
+                     i++;
+                     array[l++] = META (seq[i]);
+                   }
                }
              else if (c == 'C')
                {
@@ -539,6 +571,40 @@ rl_translate_keyseq (seq, array, len)
   return (0);
 }
 
+static int
+_rl_isescape (c)
+     int c;
+{
+  switch (c)
+    {
+    case '\007':
+    case '\b':
+    case '\f':
+    case '\n':
+    case '\r':
+    case TAB:
+    case 0x0b:  return (1);
+    default: return (0);
+    }
+}
+
+static int
+_rl_escchar (c)
+     int c;
+{
+  switch (c)
+    {
+    case '\007':  return ('a');
+    case '\b':  return ('b');
+    case '\f':  return ('f');
+    case '\n':  return ('n');
+    case '\r':  return ('r');
+    case TAB:  return ('t');
+    case 0x0b:  return ('v');
+    default: return (c);
+    }
+}
+
 char *
 rl_untranslate_keyseq (seq)
      int seq;
@@ -555,6 +621,11 @@ rl_untranslate_keyseq (seq)
       kseq[i++] = '-';
       c = UNMETA (c);
     }
+  else if (c == ESC)
+    {
+      kseq[i++] = '\\';
+      c = 'e';
+    }
   else if (CTRL_CHAR (c))
     {
       kseq[i++] = '\\';
@@ -585,9 +656,10 @@ rl_untranslate_keyseq (seq)
   return kseq;
 }
 
-static char *
-_rl_untranslate_macro_value (seq)
+char *
+_rl_untranslate_macro_value (seq, use_escapes)
      char *seq;
+     int use_escapes;
 {
   char *ret, *r, *s;
   int c;
@@ -603,12 +675,22 @@ _rl_untranslate_macro_value (seq)
          *r++ = '-';
          c = UNMETA (c);
        }
-      else if (CTRL_CHAR (c) && c != ESC)
+      else if (c == ESC)
        {
          *r++ = '\\';
-         *r++ = 'C';
-         *r++ = '-';
-         c = _rl_to_lower (UNCTRL (c));
+         c = 'e';
+       }
+      else if (CTRL_CHAR (c))
+       {
+         *r++ = '\\';
+         if (use_escapes && _rl_isescape (c))
+           c = _rl_escchar (c);
+         else
+           {
+             *r++ = 'C';
+             *r++ = '-';
+             c = _rl_to_lower (UNCTRL (c));
+           }
        }
       else if (c == RUBOUT)
        {
@@ -662,7 +744,7 @@ rl_function_of_keyseq (keyseq, map, type)
 {
   register int i;
 
-  if (!map)
+  if (map == 0)
     map = _rl_keymap;
 
   for (i = 0; keyseq && keyseq[i]; i++)
@@ -671,25 +753,27 @@ rl_function_of_keyseq (keyseq, map, type)
 
       if (META_CHAR (ic) && _rl_convert_meta_chars_to_ascii)
        {
-         if (map[ESC].type != ISKMAP)
+         if (map[ESC].type == ISKMAP)
+           {
+             map = FUNCTION_TO_KEYMAP (map, ESC);
+             ic = UNMETA (ic);
+           }
+         /* XXX - should we just return NULL here, since this obviously
+            doesn't match? */
+         else
            {
              if (type)
                *type = map[ESC].type;
 
              return (map[ESC].function);
            }
-         else
-           {
-             map = FUNCTION_TO_KEYMAP (map, ESC);
-             ic = UNMETA (ic);
-           }
        }
 
       if (map[ic].type == ISKMAP)
        {
          /* If this is the last key in the key sequence, return the
             map. */
-         if (!keyseq[i + 1])
+         if (keyseq[i + 1] == '\0')
            {
              if (type)
                *type = ISKMAP;
@@ -699,7 +783,12 @@ rl_function_of_keyseq (keyseq, map, type)
          else
            map = FUNCTION_TO_KEYMAP (map, ic);
        }
-      else
+      /* If we're not at the end of the key sequence, and the current key
+        is bound to something other than a keymap, then the entire key
+        sequence is not bound. */
+      else if (map[ic].type != ISKMAP && keyseq[i+1])
+       return ((rl_command_func_t *)NULL);
+      else     /* map[ic].type != ISKMAP && keyseq[i+1] == 0 */
        {
          if (type)
            *type = map[ic].type;
@@ -754,10 +843,12 @@ _rl_read_file (filename, sizep)
 
   if (i < 0)
     {
-      free (buffer);
+      xfree (buffer);
       return ((char *)NULL);
     }
 
+  RL_CHECK_SIGNALS ();
+
   buffer[i] = '\0';
   if (sizep)
     *sizep = i;
@@ -781,6 +872,7 @@ rl_re_read_init_file (count, ignore)
      1. the filename used for the previous call
      2. the value of the shell variable `INPUTRC'
      3. ~/.inputrc
+     4. /etc/inputrc
    If the file existed and could be opened and read, 0 is returned,
    otherwise errno is returned. */
 int
@@ -789,17 +881,18 @@ rl_read_init_file (filename)
 {
   /* Default the filename. */
   if (filename == 0)
+    filename = last_readline_init_file;
+  if (filename == 0)
+    filename = sh_get_env_value ("INPUTRC");
+  if (filename == 0 || *filename == 0)
     {
-      filename = last_readline_init_file;
-      if (filename == 0)
-        filename = sh_get_env_value ("INPUTRC");
-      if (filename == 0)
-       filename = DEFAULT_INPUTRC;
+      filename = DEFAULT_INPUTRC;
+      /* Try to read DEFAULT_INPUTRC; fall back to SYS_INPUTRC on failure */
+      if (_rl_read_init_file (filename, 0) == 0)
+       return 0;
+      filename = SYS_INPUTRC;
     }
 
-  if (*filename == 0)
-    filename = DEFAULT_INPUTRC;
-
 #if defined (__MSDOS__)
   if (_rl_read_init_file (filename, 0) == 0)
     return 0;
@@ -822,8 +915,9 @@ _rl_read_init_file (filename, include_level)
 
   openname = tilde_expand (filename);
   buffer = _rl_read_file (openname, &file_size);
-  free (openname);
+  xfree (openname);
 
+  RL_CHECK_SIGNALS ();
   if (buffer == 0)
     return (errno);
   
@@ -870,7 +964,7 @@ _rl_read_init_file (filename, include_level)
       current_readline_init_lineno++;
     }
 
-  free (buffer);
+  xfree (buffer);
   currently_reading_init_file = 0;
   return (0);
 }
@@ -880,10 +974,10 @@ _rl_init_file_error (msg)
      const char *msg;
 {
   if (currently_reading_init_file)
-    fprintf (stderr, "readline: %s: line %d: %s\n", current_readline_init_file,
+    _rl_errmsg ("%s: line %d: %s\n", current_readline_init_file,
                     current_readline_init_lineno, msg);
   else
-    fprintf (stderr, "readline: %s\n", msg);
+    _rl_errmsg ("%s", msg);
 }
 
 /* **************************************************************** */
@@ -895,11 +989,11 @@ _rl_init_file_error (msg)
 typedef int _rl_parser_func_t PARAMS((char *));
 
 /* Things that mean `Control'. */
-const char *_rl_possible_control_prefixes[] = {
+const char * const _rl_possible_control_prefixes[] = {
   "Control-", "C-", "CTRL-", (const char *)NULL
 };
 
-const char *_rl_possible_meta_prefixes[] = {
+const char * const _rl_possible_meta_prefixes[] = {
   "Meta", "M-", (const char *)NULL
 };
 
@@ -961,7 +1055,7 @@ parser_if (args)
         `$if term=sun-cmd' into their .inputrc. */
       _rl_parsing_conditionalized_out = _rl_stricmp (args + 5, tname) &&
                                        _rl_stricmp (args + 5, rl_terminal_name);
-      free (tname);
+      xfree (tname);
     }
 #if defined (VI_MODE)
   else if (_rl_strnicmp (args, "mode=", 5) == 0)
@@ -1058,8 +1152,8 @@ parser_include (args)
 }
   
 /* Associate textual names with actual functions. */
-static struct {
-  const char *name;
+static const struct {
+  const char * const name;
   _rl_parser_func_t *function;
 } parser_directives [] = {
   { "if", parser_if },
@@ -1107,6 +1201,38 @@ handle_parser_directive (statement)
   return (1);
 }
 
+/* Start at STRING[START] and look for DELIM.  Return I where STRING[I] ==
+   DELIM or STRING[I] == 0.  DELIM is usually a double quote. */
+static int
+_rl_skip_to_delim (string, start, delim)
+     char *string;
+     int start, delim;
+{
+  int i, c, passc;
+
+  for (i = start,passc = 0; c = string[i]; i++)
+    {
+      if (passc)
+       {
+         passc = 0;
+         if (c == 0)
+           break;
+         continue;
+       }
+
+      if (c == '\\')
+       {
+         passc = 1;
+         continue;
+       }
+
+      if (c == delim)
+       break;
+    }
+
+  return i;
+}
+
 /* Read the binding command from STRING and perform it.
    A key binding command looks like: Keyname: function-name\0,
    a variable binding command looks like: set variable value.
@@ -1122,7 +1248,7 @@ rl_parse_and_bind (string)
   while (string && whitespace (*string))
     string++;
 
-  if (!string || !*string || *string == '#')
+  if (string == 0 || *string == 0 || *string == '#')
     return 0;
 
   /* If this is a parser directive, act on it. */
@@ -1142,31 +1268,16 @@ rl_parse_and_bind (string)
      backslash to quote characters in the key expression. */
   if (*string == '"')
     {
-      int passc = 0;
-
-      for (i = 1; c = string[i]; i++)
-       {
-         if (passc)
-           {
-             passc = 0;
-             continue;
-           }
-
-         if (c == '\\')
-           {
-             passc++;
-             continue;
-           }
+      i = _rl_skip_to_delim (string, 1, '"');
 
-         if (c == '"')
-           break;
-       }
       /* If we didn't find a closing quote, abort the line. */
       if (string[i] == '\0')
         {
           _rl_init_file_error ("no closing `\"' in key binding");
           return 1;
         }
+      else
+        i++;   /* skip past closing double quote */
     }
 
   /* Advance to the colon (:) or whitespace which separates the two objects. */
@@ -1185,19 +1296,46 @@ rl_parse_and_bind (string)
   /* If this is a command to set a variable, then do that. */
   if (_rl_stricmp (string, "set") == 0)
     {
-      char *var = string + i;
-      char *value;
+      char *var, *value, *e;
+      int s;
 
+      var = string + i;
       /* Make VAR point to start of variable name. */
       while (*var && whitespace (*var)) var++;
 
       /* Make VALUE point to start of value string. */
       value = var;
-      while (*value && !whitespace (*value)) value++;
+      while (*value && whitespace (*value) == 0) value++;
       if (*value)
        *value++ = '\0';
       while (*value && whitespace (*value)) value++;
 
+      /* Strip trailing whitespace from values of boolean variables. */
+      if (find_boolean_var (var) >= 0)
+       {
+         /* remove trailing whitespace */
+remove_trailing:
+         e = value + strlen (value) - 1;
+         while (e >= value && whitespace (*e))
+           e--;
+         e++;          /* skip back to whitespace or EOS */
+         
+         if (*e && e >= value)
+           *e = '\0';
+       }
+      else if ((i = find_string_var (var)) >= 0)
+       {
+         /* Allow quoted strings in variable values */
+         if (*value == '"')
+           {
+             i = _rl_skip_to_delim (value, 1, *value);
+             value[i] = '\0';
+             value++;  /* skip past the quote */
+           }
+         else
+           goto remove_trailing;
+       }
+       
       rl_variable_bind (var, value);
       return 0;
     }
@@ -1218,31 +1356,13 @@ rl_parse_and_bind (string)
      the quoted string delimiter, like the shell. */
   if (*funname == '\'' || *funname == '"')
     {
-      int delimiter = string[i++], passc;
-
-      for (passc = 0; c = string[i]; i++)
-       {
-         if (passc)
-           {
-             passc = 0;
-             continue;
-           }
-
-         if (c == '\\')
-           {
-             passc = 1;
-             continue;
-           }
-
-         if (c == delimiter)
-           break;
-       }
-      if (c)
+      i = _rl_skip_to_delim (string, i+1, *funname);
+      if (string[i])
        i++;
     }
 
   /* Advance to the end of the string.  */
-  for (; string[i] && !whitespace (string[i]); i++);
+  for (; string[i] && whitespace (string[i]) == 0; i++);
 
   /* No extra whitespace at the end of the string. */
   string[i] = '\0';
@@ -1296,13 +1416,13 @@ rl_parse_and_bind (string)
       else
        rl_bind_keyseq (seq, rl_named_function (funname));
 
-      free (seq);
+      xfree (seq);
       return 0;
     }
 
   /* Get the actual character we want to deal with. */
   kname = strrchr (string, '-');
-  if (!kname)
+  if (kname == 0)
     kname = string;
   else
     kname++;
@@ -1350,17 +1470,24 @@ rl_parse_and_bind (string)
 
 #define V_SPECIAL      0x1
 
-static struct {
-  const char *name;
+static const struct {
+  const char * const name;
   int *value;
   int flags;
 } boolean_varlist [] = {
+  { "bind-tty-special-chars",  &_rl_bind_stty_chars,           0 },
   { "blink-matching-paren",    &rl_blink_matching_paren,       V_SPECIAL },
   { "byte-oriented",           &rl_byte_oriented,              0 },
+#if defined (COLOR_SUPPORT)
+  { "colored-stats",           &_rl_colored_stats,             0 },
+#endif
   { "completion-ignore-case",  &_rl_completion_case_fold,      0 },
+  { "completion-map-case",     &_rl_completion_case_map,       0 },
   { "convert-meta",            &_rl_convert_meta_chars_to_ascii, 0 },
   { "disable-completion",      &rl_inhibit_completion,         0 },
+  { "echo-control-characters", &_rl_echo_control_chars,        0 },
   { "enable-keypad",           &_rl_enable_keypad,             0 },
+  { "enable-meta-key",         &_rl_enable_meta,               0 },
   { "expand-tilde",            &rl_complete_with_tilde_expansion, 0 },
   { "history-preserve-point",  &_rl_history_preserve_point,    0 },
   { "horizontal-scroll-mode",  &_rl_horizontal_scroll_mode,    0 },
@@ -1369,17 +1496,21 @@ static struct {
   { "mark-modified-lines",     &_rl_mark_modified_lines,       0 },
   { "mark-symlinked-directories", &_rl_complete_mark_symlink_dirs, 0 },
   { "match-hidden-files",      &_rl_match_hidden_files,        0 },
+  { "menu-complete-display-prefix", &_rl_menu_complete_prefix_first, 0 },
   { "meta-flag",               &_rl_meta_flag,                 0 },
   { "output-meta",             &_rl_output_meta_chars,         0 },
   { "page-completions",                &_rl_page_completions,          0 },
   { "prefer-visible-bell",     &_rl_prefer_visible_bell,       V_SPECIAL },
   { "print-completions-horizontally", &_rl_print_completions_horizontally, 0 },
+  { "revert-all-at-newline",   &_rl_revert_all_at_newline,     0 },
   { "show-all-if-ambiguous",   &_rl_complete_show_all,         0 },
   { "show-all-if-unmodified",  &_rl_complete_show_unmodified,  0 },
+  { "show-mode-in-prompt",     &_rl_show_mode_in_prompt,       0 },
+  { "skip-completed-text",     &_rl_skip_completed_text,       0 },
 #if defined (VISIBLE_STATS)
   { "visible-stats",           &rl_visible_stats,              0 },
 #endif /* VISIBLE_STATS */
-  { (char *)NULL, (int *)NULL }
+  { (char *)NULL, (int *)NULL, 0 }
 };
 
 static int
@@ -1414,6 +1545,8 @@ hack_special_boolean_var (i)
       else
        _rl_bell_preference = AUDIBLE_BELL;
     }
+  else if (_rl_stricmp (name, "show-mode-in-prompt") == 0)
+    _rl_reset_prompt ();
 }
 
 typedef int _rl_sv_func_t PARAMS((const char *));
@@ -1432,23 +1565,31 @@ typedef int _rl_sv_func_t PARAMS((const char *));
 /* Forward declarations */
 static int sv_bell_style PARAMS((const char *));
 static int sv_combegin PARAMS((const char *));
+static int sv_dispprefix PARAMS((const char *));
 static int sv_compquery PARAMS((const char *));
+static int sv_compwidth PARAMS((const char *));
 static int sv_editmode PARAMS((const char *));
+static int sv_histsize PARAMS((const char *));
 static int sv_isrchterm PARAMS((const char *));
 static int sv_keymap PARAMS((const char *));
+static int sv_seqtimeout PARAMS((const char *));
 
-static struct {
-  const char *name;
+static const struct {
+  const char * const name;
   int flags;
   _rl_sv_func_t *set_func;
 } string_varlist[] = {
   { "bell-style",      V_STRING,       sv_bell_style },
   { "comment-begin",   V_STRING,       sv_combegin },
+  { "completion-display-width", V_INT, sv_compwidth },
+  { "completion-prefix-display-length", V_INT, sv_dispprefix },
   { "completion-query-items", V_INT,   sv_compquery },
   { "editing-mode",    V_STRING,       sv_editmode },
+  { "history-size",    V_INT,          sv_histsize },
   { "isearch-terminators", V_STRING,   sv_isrchterm },
   { "keymap",          V_STRING,       sv_keymap },
-  { (char *)NULL,      0 }
+  { "keyseq-timeout",  V_INT,          sv_seqtimeout },
+  { (char *)NULL,      0, (_rl_sv_func_t *)0 }
 };
 
 static int
@@ -1468,13 +1609,32 @@ find_string_var (name)
    values result in 0 (false). */
 static int
 bool_to_int (value)
-     char *value;
+     const char *value;
 {
   return (value == 0 || *value == '\0' ||
                (_rl_stricmp (value, "on") == 0) ||
                (value[0] == '1' && value[1] == '\0'));
 }
 
+char *
+rl_variable_value (name)
+     const char *name;
+{
+  register int i;
+
+  /* Check for simple variables first. */
+  i = find_boolean_var (name);
+  if (i >= 0)
+    return (*boolean_varlist[i].value ? "on" : "off");
+
+  i = find_string_var (name);
+  if (i >= 0)
+    return (_rl_get_string_variable_value (string_varlist[i].name));
+
+  /* Unknown variable names return NULL. */
+  return 0;
+}
+
 int
 rl_variable_bind (name, value)
      const char *name, *value;
@@ -1538,6 +1698,22 @@ sv_combegin (value)
 }
 
 static int
+sv_dispprefix (value)
+     const char *value;
+{
+  int nval = 0;
+
+  if (value && *value)
+    {
+      nval = atoi (value);
+      if (nval < 0)
+       nval = 0;
+    }
+  _rl_completion_prefix_display_length = nval;
+  return 0;
+}
+
+static int
 sv_compquery (value)
      const char *value;
 {
@@ -1554,6 +1730,39 @@ sv_compquery (value)
 }
 
 static int
+sv_compwidth (value)
+     const char *value;
+{
+  int nval = -1;
+
+  if (value && *value)
+    nval = atoi (value);
+
+  _rl_completion_columns = nval;
+  return 0;
+}
+
+static int
+sv_histsize (value)
+     const char *value;
+{
+  int nval;
+
+  nval = 500;
+  if (value && *value)
+    {
+      nval = atoi (value);
+      if (nval < 0)
+       {
+         unstifle_history ();
+         return 0;
+       }
+    }
+  stifle_history (nval);
+  return 0;
+}
+
+static int
 sv_keymap (value)
      const char *value;
 {
@@ -1569,6 +1778,23 @@ sv_keymap (value)
 }
 
 static int
+sv_seqtimeout (value)
+     const char *value;
+{
+  int nval;
+
+  nval = 0;
+  if (value && *value)
+    {
+      nval = atoi (value);
+      if (nval < 0)
+       nval = 0;
+    }
+  _rl_keyseq_timeout = nval;
+  return 0;
+}
+
+static int
 sv_bell_style (value)
      const char *value;
 {
@@ -1617,7 +1843,7 @@ sv_isrchterm (value)
   rl_translate_keyseq (v + beg, _rl_isearch_terminators, &end);
   _rl_isearch_terminators[end] = '\0';
 
-  free (v);
+  xfree (v);
   return 0;
 }
       
@@ -1625,11 +1851,11 @@ sv_isrchterm (value)
    For example, `Space' returns ' '. */
 
 typedef struct {
-  const char *name;
+  const char * const name;
   int value;
 } assoc_list;
 
-static assoc_list name_key_alist[] = {
+static const assoc_list name_key_alist[] = {
   { "DEL", 0x7f },
   { "ESC", '\033' },
   { "Escape", '\033' },
@@ -1658,8 +1884,8 @@ glean_key_from_name (name)
 }
 
 /* Auxiliary functions to manage keymaps. */
-static struct {
-  const char *name;
+static const struct {
+  const char * const name;
   Keymap map;
 } keymap_names[] = {
   { "emacs", emacs_standard_keymap },
@@ -1762,7 +1988,7 @@ rl_list_funmap_names ()
   for (i = 0; funmap_names[i]; i++)
     fprintf (rl_outstream, "%s\n", funmap_names[i]);
 
-  free (funmap_names);
+  xfree (funmap_names);
 }
 
 static char *
@@ -1901,12 +2127,16 @@ rl_invoking_keyseqs_in_map (function, map)
                char *keyname = (char *)xmalloc (6 + strlen (seqs[i]));
 
                if (key == ESC)
-#if 0
-                 sprintf (keyname, "\\e");
-#else
-               /* XXX - experimental */
-                 sprintf (keyname, "\\M-");
-#endif
+                 {
+                   /* If ESC is the meta prefix and we're converting chars
+                      with the eighth bit set to ESC-prefixed sequences, then
+                      we can use \M-.  Otherwise we need to use the sequence
+                      for ESC. */
+                   if (_rl_convert_meta_chars_to_ascii && map[ESC].type == ISKMAP)
+                     sprintf (keyname, "\\M-");
+                   else
+                     sprintf (keyname, "\\e");
+                 }
                else if (CTRL_CHAR (key))
                  sprintf (keyname, "\\C-%c", _rl_to_lower (UNCTRL (key)));
                else if (key == RUBOUT)
@@ -1924,7 +2154,7 @@ rl_invoking_keyseqs_in_map (function, map)
                  }
                
                strcat (keyname, seqs[i]);
-               free (seqs[i]);
+               xfree (seqs[i]);
 
                if (result_index + 2 > result_size)
                  {
@@ -1936,7 +2166,7 @@ rl_invoking_keyseqs_in_map (function, map)
                result[result_index] = (char *)NULL;
              }
 
-           free (seqs);
+           xfree (seqs);
          }
          break;
        }
@@ -1988,10 +2218,10 @@ rl_function_dumper (print_readably)
                {
                  fprintf (rl_outstream, "\"%s\": %s\n",
                           invokers[j], name);
-                 free (invokers[j]);
+                 xfree (invokers[j]);
                }
 
-             free (invokers);
+             xfree (invokers);
            }
        }
       else
@@ -2015,12 +2245,14 @@ rl_function_dumper (print_readably)
                fprintf (rl_outstream, "...\n");
 
              for (j = 0; invokers[j]; j++)
-               free (invokers[j]);
+               xfree (invokers[j]);
 
-             free (invokers);
+             xfree (invokers);
            }
        }
     }
+
+  xfree (names);
 }
 
 /* Print all of the current functions and their bindings to
@@ -2053,7 +2285,7 @@ _rl_macro_dumper_internal (print_readably, map, prefix)
        {
        case ISMACR:
          keyname = _rl_get_keyname (key);
-         out = _rl_untranslate_macro_value ((char *)map[key].function);
+         out = _rl_untranslate_macro_value ((char *)map[key].function, 0);
 
          if (print_readably)
            fprintf (rl_outstream, "\"%s%s\": \"%s\"\n", prefix ? prefix : "",
@@ -2063,8 +2295,8 @@ _rl_macro_dumper_internal (print_readably, map, prefix)
            fprintf (rl_outstream, "%s%s outputs %s\n", prefix ? prefix : "",
                                                        keyname,
                                                        out ? out : "");
-         free (keyname);
-         free (out);
+         xfree (keyname);
+         xfree (out);
          break;
        case ISFUNC:
          break;
@@ -2087,13 +2319,13 @@ _rl_macro_dumper_internal (print_readably, map, prefix)
                  out = (char *)xmalloc (strlen (keyname) + prefix_len + 1);
                  strcpy (out, prefix);
                  strcpy (out + prefix_len, keyname);
-                 free (keyname);
+                 xfree (keyname);
                  keyname = out;
                }
            }
 
          _rl_macro_dumper_internal (print_readably, FUNCTION_TO_KEYMAP (map, key), keyname);
-         free (keyname);
+         xfree (keyname);
          break;
        }
     }
@@ -2117,12 +2349,87 @@ rl_dump_macros (count, key)
   return (0);
 }
 
+static char *
+_rl_get_string_variable_value (name)
+     const char *name;
+{
+  static char numbuf[32];
+  char *ret;
+
+  if (_rl_stricmp (name, "bell-style") == 0)
+    {
+      switch (_rl_bell_preference)
+       {
+         case NO_BELL:
+           return "none";
+         case VISIBLE_BELL:
+           return "visible";
+         case AUDIBLE_BELL:
+         default:
+           return "audible";
+       }
+    }
+  else if (_rl_stricmp (name, "comment-begin") == 0)
+    return (_rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT);
+  else if (_rl_stricmp (name, "completion-display-width") == 0)
+    {
+      sprintf (numbuf, "%d", _rl_completion_columns);
+      return (numbuf);
+    }
+  else if (_rl_stricmp (name, "completion-prefix-display-length") == 0)
+    {
+      sprintf (numbuf, "%d", _rl_completion_prefix_display_length);
+      return (numbuf);
+    }
+  else if (_rl_stricmp (name, "completion-query-items") == 0)
+    {
+      sprintf (numbuf, "%d", rl_completion_query_items);
+      return (numbuf);
+    }
+  else if (_rl_stricmp (name, "editing-mode") == 0)
+    return (rl_get_keymap_name_from_edit_mode ());
+  else if (_rl_stricmp (name, "history-size") == 0)
+    {
+      sprintf (numbuf, "%d", history_is_stifled() ? history_max_entries : 0);
+      return (numbuf);
+    }
+  else if (_rl_stricmp (name, "isearch-terminators") == 0)
+    {
+      if (_rl_isearch_terminators == 0)
+       return 0;
+      ret = _rl_untranslate_macro_value (_rl_isearch_terminators, 0);
+      if (ret)
+       {
+         strncpy (numbuf, ret, sizeof (numbuf) - 1);
+         xfree (ret);
+         numbuf[sizeof(numbuf) - 1] = '\0';
+       }
+      else
+       numbuf[0] = '\0';
+      return numbuf;
+    }
+  else if (_rl_stricmp (name, "keymap") == 0)
+    {
+      ret = rl_get_keymap_name (_rl_keymap);
+      if (ret == 0)
+       ret = rl_get_keymap_name_from_edit_mode ();
+      return (ret ? ret : "none");
+    }
+  else if (_rl_stricmp (name, "keyseq-timeout") == 0)
+    {
+      sprintf (numbuf, "%d", _rl_keyseq_timeout);    
+      return (numbuf);
+    }
+  else
+    return (0);
+}
+
 void
 rl_variable_dumper (print_readably)
      int print_readably;
 {
   int i;
-  const char *kname;
+  char *v;
 
   for (i = 0; boolean_varlist[i].name; i++)
     {
@@ -2134,63 +2441,16 @@ rl_variable_dumper (print_readably)
                               *boolean_varlist[i].value ? "on" : "off");
     }
 
-  /* bell-style */
-  switch (_rl_bell_preference)
-    {
-    case NO_BELL:
-      kname = "none"; break;
-    case VISIBLE_BELL:
-      kname = "visible"; break;
-    case AUDIBLE_BELL:
-    default:
-      kname = "audible"; break;
-    }
-  if (print_readably)
-    fprintf (rl_outstream, "set bell-style %s\n", kname);
-  else
-    fprintf (rl_outstream, "bell-style is set to `%s'\n", kname);
-
-  /* comment-begin */
-  if (print_readably)
-    fprintf (rl_outstream, "set comment-begin %s\n", _rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT);
-  else
-    fprintf (rl_outstream, "comment-begin is set to `%s'\n", _rl_comment_begin ? _rl_comment_begin : RL_COMMENT_BEGIN_DEFAULT);
-
-  /* completion-query-items */
-  if (print_readably)
-    fprintf (rl_outstream, "set completion-query-items %d\n", rl_completion_query_items);
-  else
-    fprintf (rl_outstream, "completion-query-items is set to `%d'\n", rl_completion_query_items);
-
-  /* editing-mode */
-  if (print_readably)
-    fprintf (rl_outstream, "set editing-mode %s\n", (rl_editing_mode == emacs_mode) ? "emacs" : "vi");
-  else
-    fprintf (rl_outstream, "editing-mode is set to `%s'\n", (rl_editing_mode == emacs_mode) ? "emacs" : "vi");
-
-  /* isearch-terminators */
-  if (_rl_isearch_terminators)
+  for (i = 0; string_varlist[i].name; i++)
     {
-      char *disp;
-
-      disp = _rl_untranslate_macro_value (_rl_isearch_terminators);
-
+      v = _rl_get_string_variable_value (string_varlist[i].name);
+      if (v == 0)      /* _rl_isearch_terminators can be NULL */
+       continue;
       if (print_readably)
-       fprintf (rl_outstream, "set isearch-terminators \"%s\"\n", disp);
+        fprintf (rl_outstream, "set %s %s\n", string_varlist[i].name, v);
       else
-       fprintf (rl_outstream, "isearch-terminators is set to \"%s\"\n", disp);
-
-      free (disp);
+        fprintf (rl_outstream, "%s is set to `%s'\n", string_varlist[i].name, v);
     }
-
-  /* keymap */
-  kname = rl_get_keymap_name (_rl_keymap);
-  if (kname == 0)
-    kname = rl_get_keymap_name_from_edit_mode ();
-  if (print_readably)
-    fprintf (rl_outstream, "set keymap %s\n", kname ? kname : "none");
-  else
-    fprintf (rl_outstream, "keymap is set to `%s'\n", kname ? kname : "none");
 }
 
 /* Print all of the current variables and their values to
@@ -2210,8 +2470,8 @@ rl_dump_variables (count, key)
 /* Return non-zero if any members of ARRAY are a substring in STRING. */
 static int
 substring_member_of_array (string, array)
-     char *string;
-     const char **array;
+     const char *string;
+     const char * const *array;
 {
   while (*array)
     {