]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - bashline.c
renamed several functions beginning with legal_; changed all callers
[thirdparty/bash.git] / bashline.c
index 362f0176a5284999098d5903e689bdcce40de863..c767e1f53b4827cac296666788031c6686ca2463 100644 (file)
@@ -1,6 +1,6 @@
 /* bashline.c -- Bash's interface to the readline library. */
 
-/* Copyright (C) 1987-2021 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2023 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
 #define RL_BOOLEAN_VARIABLE_VALUE(s)   ((s)[0] == 'o' && (s)[1] == 'n' && (s)[2] == '\0')
 
 #if defined (BRACE_COMPLETION)
-extern int bash_brace_completion PARAMS((int, int));
+extern int bash_brace_completion (int, int);
 #endif /* BRACE_COMPLETION */
 
 /* To avoid including curses.h/term.h/termcap.h and that whole mess. */
 #ifdef _MINIX
-extern int tputs PARAMS((const char *string, int nlines, void (*outx)(int)));
+extern int tputs (const char *string, int nlines, void (*outx)(int));
 #else
-extern int tputs PARAMS((const char *string, int nlines, int (*outx)(int)));
+extern int tputs (const char *string, int nlines, int (*outx)(int));
 #endif
 
 /* Forward declarations */
 
 /* Functions bound to keys in Readline for Bash users. */
-static int shell_expand_line PARAMS((int, int));
-static int display_shell_version PARAMS((int, int));
+static int shell_expand_line (int, int);
+static int display_shell_version (int, int);
 
-static int bash_ignore_filenames PARAMS((char **));
-static int bash_ignore_everything PARAMS((char **));
-static int bash_progcomp_ignore_filenames PARAMS((char **));
+static int bash_ignore_filenames (char **);
+static int bash_ignore_everything (char **);
+static int bash_progcomp_ignore_filenames (char **);
 
 #if defined (BANG_HISTORY)
-static char *history_expand_line_internal PARAMS((char *));
-static int history_expand_line PARAMS((int, int));
-static int tcsh_magic_space PARAMS((int, int));
+static char *history_expand_line_internal (char *);
+static int history_expand_line (int, int);
+static int tcsh_magic_space (int, int);
 #endif /* BANG_HISTORY */
 #ifdef ALIAS
-static int alias_expand_line PARAMS((int, int));
+static int alias_expand_line (int, int);
 #endif
 #if defined (BANG_HISTORY) && defined (ALIAS)
-static int history_and_alias_expand_line PARAMS((int, int));
+static int history_and_alias_expand_line (int, int);
 #endif
 
-static int bash_forward_shellword PARAMS((int, int));
-static int bash_backward_shellword PARAMS((int, int));
-static int bash_kill_shellword PARAMS((int, int));
-static int bash_backward_kill_shellword PARAMS((int, int));
-static int bash_transpose_shellwords PARAMS((int, int));
+static int bash_forward_shellword (int, int);
+static int bash_backward_shellword (int, int);
+static int bash_kill_shellword (int, int);
+static int bash_backward_kill_shellword (int, int);
+static int bash_transpose_shellwords (int, int);
 
-static int bash_spell_correct_shellword PARAMS((int, int));
+static int bash_spell_correct_shellword (int, int);
 
 /* Helper functions for Readline. */
-static char *restore_tilde PARAMS((char *, char *));
-static char *maybe_restore_tilde PARAMS((char *, char *));
-
-static char *bash_filename_rewrite_hook PARAMS((char *, int));
-
-static void bash_directory_expansion PARAMS((char **));
-static int bash_filename_stat_hook PARAMS((char **));
-static int bash_command_name_stat_hook PARAMS((char **));
-static int bash_directory_completion_hook PARAMS((char **));
-static int filename_completion_ignore PARAMS((char **));
-static int bash_push_line PARAMS((void));
-
-static int executable_completion PARAMS((const char *, int));
-
-static rl_icppfunc_t *save_directory_hook PARAMS((void));
-static void restore_directory_hook PARAMS((rl_icppfunc_t));
-
-static int directory_exists PARAMS((const char *, int));
-
-static void cleanup_expansion_error PARAMS((void));
-static void maybe_make_readline_line PARAMS((char *));
-static void set_up_new_line PARAMS((char *));
-
-static int check_redir PARAMS((int));
-static char **attempt_shell_completion PARAMS((const char *, int, int));
-static char *variable_completion_function PARAMS((const char *, int));
-static char *hostname_completion_function PARAMS((const char *, int));
-static char *command_subst_completion_function PARAMS((const char *, int));
-
-static void build_history_completion_array PARAMS((void));
-static char *history_completion_generator PARAMS((const char *, int));
-static int dynamic_complete_history PARAMS((int, int));
-static int bash_dabbrev_expand PARAMS((int, int));
-
-static void initialize_hostname_list PARAMS((void));
-static void add_host_name PARAMS((char *));
-static void snarf_hosts_from_file PARAMS((char *));
-static char **hostnames_matching PARAMS((char *));
-
-static void _ignore_completion_names PARAMS((char **, sh_ignore_func_t *));
-static int name_is_acceptable PARAMS((const char *));
-static int test_for_directory PARAMS((const char *));
-static int test_for_canon_directory PARAMS((const char *));
-static int return_zero PARAMS((const char *));
-
-static char *bash_dequote_filename PARAMS((char *, int));
-static char *quote_word_break_chars PARAMS((char *));
-static void set_filename_bstab PARAMS((const char *));
-static char *bash_quote_filename PARAMS((char *, int, char *));
+static char *restore_tilde (const char *, char *);
+static char *maybe_restore_tilde (char *, char *);
+
+static char *bash_filename_rewrite_hook (char *, int);
+
+static void bash_directory_expansion (char **);
+static char *bash_expand_filename (char *);
+static int bash_filename_stat_hook (char **);
+static int bash_command_name_stat_hook (char **);
+static int bash_directory_completion_hook (char **);
+static int filename_completion_ignore (char **);
+static int bash_push_line (void);
+
+static int executable_completion (const char *, int);
+
+static rl_icppfunc_t *save_directory_hook (void);
+static void restore_directory_hook (rl_icppfunc_t);
+
+static int directory_exists (const char *, int);
+
+static void cleanup_expansion_error (void);
+static void maybe_make_readline_line (const char *);
+static void set_up_new_line (char *);
+
+static int check_redir (int);
+static char **attempt_shell_completion (const char *, int, int);
+static char *variable_completion_function (const char *, int);
+static char *hostname_completion_function (const char *, int);
+static char *command_subst_completion_function (const char *, int);
+
+static void build_history_completion_array (void);
+static char *history_completion_generator (const char *, int);
+static int dynamic_complete_history (int, int);
+static int bash_dabbrev_expand (int, int);
+
+static void initialize_hostname_list (void);
+static void add_host_name (const char *);
+static void snarf_hosts_from_file (const char *);
+static char **hostnames_matching (const char *);
+
+static void _ignore_completion_names (char **, sh_ignore_func_t *);
+static int name_is_acceptable (const char *);
+static int test_for_directory (const char *);
+static int test_for_canon_directory (const char *);
+static int return_zero (const char *);
+
+static char *bash_dequote_filename (char *, int);
+static char *quote_word_break_chars (char *);
+static int bash_check_expchar (char *, int, int *, int *);
+static void set_filename_quote_chars (int, int, int);
+static void set_filename_bstab (const char *);
+static char *bash_quote_filename (char *, int, char *);
 
 #ifdef _MINIX
-static void putx PARAMS((int));
+static void putx (int);
 #else
-static int putx PARAMS((int));
+static int putx (int);
 #endif
-static int readline_get_char_offset PARAMS((int));
-static void readline_set_char_offset PARAMS((int, int *));
+static int readline_get_char_offset (int);
+static void readline_set_char_offset (int, int *);
 
-static Keymap get_cmd_xmap_from_edit_mode PARAMS((void));
-static Keymap get_cmd_xmap_from_keymap PARAMS((Keymap));
+static Keymap get_cmd_xmap_from_edit_mode (void);
+static Keymap get_cmd_xmap_from_keymap (Keymap);
 
-static void init_unix_command_map PARAMS((void));
-static int isolate_sequence PARAMS((char *, int, int, int *));
+static void init_unix_command_map (void);
+static int isolate_sequence (char *, int, int, int *);
 
-static int set_saved_history PARAMS((void));
+static int set_saved_history (void);
 
 #if defined (ALIAS)
-static int posix_edit_macros PARAMS((int, int));
+static int posix_edit_macros (int, int);
 #endif
 
-static int bash_event_hook PARAMS((void));
+static int bash_event_hook (void);
 
 #if defined (PROGRAMMABLE_COMPLETION)
-static int find_cmd_start PARAMS((int));
-static int find_cmd_end PARAMS((int));
-static char *find_cmd_name PARAMS((int, int *, int *));
-static char *prog_complete_return PARAMS((const char *, int));
+static int find_cmd_start (int);
+static int find_cmd_end (int);
+static char *find_cmd_name (int, int *, int *);
+static char *prog_complete_return (const char *, int);
 
 static char **prog_complete_matches;
 #endif
@@ -237,40 +240,40 @@ extern sh_timer *read_timeout;
 #define SPECIFIC_COMPLETION_FUNCTIONS
 
 #if defined (SPECIFIC_COMPLETION_FUNCTIONS)
-static int bash_specific_completion PARAMS((int, rl_compentry_func_t *));
-
-static int bash_complete_filename_internal PARAMS((int));
-static int bash_complete_username_internal PARAMS((int));
-static int bash_complete_hostname_internal PARAMS((int));
-static int bash_complete_variable_internal PARAMS((int));
-static int bash_complete_command_internal PARAMS((int));
-
-static int bash_complete_filename PARAMS((int, int));
-static int bash_possible_filename_completions PARAMS((int, int));
-static int bash_complete_username PARAMS((int, int));
-static int bash_possible_username_completions PARAMS((int, int));
-static int bash_complete_hostname PARAMS((int, int));
-static int bash_possible_hostname_completions PARAMS((int, int));
-static int bash_complete_variable PARAMS((int, int));
-static int bash_possible_variable_completions PARAMS((int, int));
-static int bash_complete_command PARAMS((int, int));
-static int bash_possible_command_completions PARAMS((int, int));
-
-static int completion_glob_pattern PARAMS((char *));
-static char *glob_complete_word PARAMS((const char *, int));
-static int bash_glob_completion_internal PARAMS((int));
-static int bash_glob_complete_word PARAMS((int, int));
-static int bash_glob_expand_word PARAMS((int, int));
-static int bash_glob_list_expansions PARAMS((int, int));
+static int bash_specific_completion (int, rl_compentry_func_t *);
+
+static int bash_complete_filename_internal (int);
+static int bash_complete_username_internal (int);
+static int bash_complete_hostname_internal (int);
+static int bash_complete_variable_internal (int);
+static int bash_complete_command_internal (int);
+
+static int bash_complete_filename (int, int);
+static int bash_possible_filename_completions (int, int);
+static int bash_complete_username (int, int);
+static int bash_possible_username_completions (int, int);
+static int bash_complete_hostname (int, int);
+static int bash_possible_hostname_completions (int, int);
+static int bash_complete_variable (int, int);
+static int bash_possible_variable_completions (int, int);
+static int bash_complete_command (int, int);
+static int bash_possible_command_completions (int, int);
+
+static int completion_glob_pattern (const char *);
+static char *glob_complete_word (const char *, int);
+static int bash_glob_completion_internal (int);
+static int bash_glob_complete_word (int, int);
+static int bash_glob_expand_word (int, int);
+static int bash_glob_list_expansions (int, int);
 
 #endif /* SPECIFIC_COMPLETION_FUNCTIONS */
 
-static int edit_and_execute_command PARAMS((int, int, int, char *));
+static int edit_and_execute_command (int, int, int, const char *);
 #if defined (VI_MODE)
-static int vi_edit_and_execute_command PARAMS((int, int));
-static int bash_vi_complete PARAMS((int, int));
+static int vi_edit_and_execute_command (int, int);
+static int bash_vi_complete (int, int);
 #endif
-static int emacs_edit_and_execute_command PARAMS((int, int));
+static int emacs_edit_and_execute_command (int, int);
 
 /* Non-zero once initialize_readline () has been called. */
 int bash_readline_initialized = 0;
@@ -326,10 +329,13 @@ static int dabbrev_expand_active = 0;
        COMPLETE_DQUOTE = double-quoting the filename
        COMPLETE_SQUOTE = single_quoting the filename
        COMPLETE_BSQUOTE = backslash-quoting special chars in the filename
+       COMPLETE_DQUOTE2 = double-quote filename, but leave $ and ` unquoted
 */
 #define COMPLETE_DQUOTE  1
 #define COMPLETE_SQUOTE  2
 #define COMPLETE_BSQUOTE 3
+#define COMPLETE_DQUOTE2 4
+
 static int completion_quoting_style = COMPLETE_BSQUOTE;
 
 /* Flag values for the final argument to bash_default_completion */
@@ -340,8 +346,7 @@ static rl_command_func_t *vi_tab_binding = rl_complete;
 /* Change the readline VI-mode keymaps into or out of Posix.2 compliance.
    Called when the shell is put into or out of `posix' mode. */
 void
-posix_readline_initialize (on_or_off)
-     int on_or_off;
+posix_readline_initialize (int on_or_off)
 {
   static char kseq[2] = { CTRL ('I'), 0 };             /* TAB */
 
@@ -362,7 +367,7 @@ posix_readline_initialize (on_or_off)
 }
 
 void
-reset_completer_word_break_chars ()
+reset_completer_word_break_chars (void)
 {
   rl_completer_word_break_characters = perform_hostname_completion ? savestring (bash_completer_word_break_characters) : savestring (bash_nohostname_word_break_characters);
 }
@@ -370,11 +375,11 @@ reset_completer_word_break_chars ()
 /* When this function returns, rl_completer_word_break_characters points to
    dynamically allocated memory. */
 int
-enable_hostname_completion (on_or_off)
-     int on_or_off;
+enable_hostname_completion (int on_or_off)
 {
   int old_value;
-  char *at, *nv, *nval;
+  char *nv, *nval;
+  const char *at;
 
   old_value = perform_hostname_completion;
 
@@ -434,7 +439,7 @@ enable_hostname_completion (on_or_off)
          strcpy (nval + 1, rl_completer_word_break_characters);
         }
 
-      free (rl_completer_word_break_characters);
+      free ((void *)rl_completer_word_break_characters);
       rl_completer_word_break_characters = nval;
     }
 
@@ -443,7 +448,7 @@ enable_hostname_completion (on_or_off)
 
 /* Called once from parse.y if we are going to use readline. */
 void
-initialize_readline ()
+initialize_readline (void)
 {
   rl_command_func_t *func;
   char kseq[2];
@@ -487,6 +492,9 @@ initialize_readline ()
 
   rl_add_defun ("display-shell-version", display_shell_version, -1);
   rl_add_defun ("edit-and-execute-command", emacs_edit_and_execute_command, -1);
+#if defined (VI_MODE)
+  rl_add_defun ("vi-edit-and-execute-command", vi_edit_and_execute_command, -1);
+#endif
 
 #if defined (BRACE_COMPLETION)
   rl_add_defun ("complete-into-braces", bash_brace_completion, -1);
@@ -585,13 +593,7 @@ initialize_readline ()
   /* Tell the completer that we want a crack first. */
   rl_attempted_completion_function = attempt_shell_completion;
 
-  /* Tell the completer that we might want to follow symbolic links or
-     do other expansion on directory names. */
-  set_directory_hook ();
-
-  rl_filename_rewrite_hook = bash_filename_rewrite_hook;
-
-  rl_filename_stat_hook = bash_filename_stat_hook;
+  bashline_set_filename_hooks ();
 
   /* Tell the filename completer we want a chance to ignore some names. */
   rl_ignore_some_completions_function = filename_completion_ignore;
@@ -644,19 +646,19 @@ initialize_readline ()
 }
 
 void
-bashline_reinitialize ()
+bashline_reinitialize (void)
 {
   bash_readline_initialized = 0;
 }
 
 void
-bashline_set_event_hook ()
+bashline_set_event_hook (void)
 {
   rl_signal_event_hook = bash_event_hook;
 }
 
 void
-bashline_reset_event_hook ()
+bashline_reset_event_hook (void)
 {
   rl_signal_event_hook = 0;
 }
@@ -667,17 +669,20 @@ bashline_reset_event_hook ()
    word.  This just resets all the completion functions to the right thing.
    It's called from throw_to_top_level(). */
 void
-bashline_reset ()
+bashline_reset (void)
 {
   tilde_initialize ();
   rl_attempted_completion_function = attempt_shell_completion;
   rl_completion_entry_function = NULL;
   rl_ignore_some_completions_function = filename_completion_ignore;
+
+  complete_fullquote = 1;
   rl_filename_quote_characters = default_filename_quote_characters;
   set_filename_bstab (rl_filename_quote_characters);
 
-  set_directory_hook ();
-  rl_filename_stat_hook = bash_filename_stat_hook;
+  rl_filename_quoting_function = bash_quote_filename;
+
+  bashline_set_filename_hooks ();
 
   bashline_reset_event_hook ();
 
@@ -690,7 +695,7 @@ static char *push_to_readline = (char *)NULL;
 /* Push the contents of push_to_readline into the
    readline buffer. */
 static int
-bash_push_line ()
+bash_push_line (void)
 {
   if (push_to_readline)
     {
@@ -705,8 +710,7 @@ bash_push_line ()
 /* Call this to set the initial text for the next line to read
    from readline. */
 int
-bash_re_edit (line)
-     char *line;
+bash_re_edit (const char *line)
 {
   FREE (push_to_readline);
 
@@ -718,8 +722,7 @@ bash_re_edit (line)
 }
 
 static int
-display_shell_version (count, c)
-     int count, c;
+display_shell_version (int count, int c)
 {
   rl_crlf ();
   show_shell_version (0);
@@ -745,17 +748,17 @@ display_shell_version (count, c)
 static char **hostname_list = (char **)NULL;
 
 /* The physical size of the above list. */
-static int hostname_list_size;
+static size_t hostname_list_size;
 
 /* The number of hostnames in the above list. */
-static int hostname_list_length;
+static size_t hostname_list_length;
 
 /* Whether or not HOSTNAME_LIST has been initialized. */
 int hostname_list_initialized = 0;
 
 /* Initialize the hostname completion table. */
 static void
-initialize_hostname_list ()
+initialize_hostname_list (void)
 {
   char *temp;
 
@@ -773,8 +776,7 @@ initialize_hostname_list ()
 
 /* Add NAME to the list of hosts. */
 static void
-add_host_name (name)
-     char *name;
+add_host_name (const char *name)
 {
   if (hostname_list_length + 2 > hostname_list_size)
     {
@@ -789,8 +791,7 @@ add_host_name (name)
 #define cr_whitespace(c) ((c) == '\r' || (c) == '\n' || whitespace(c))
 
 static void
-snarf_hosts_from_file (filename)
-     char *filename;
+snarf_hosts_from_file (const char *filename)
 {
   FILE *file;
   char *temp, buffer[256], name[256];
@@ -856,7 +857,7 @@ snarf_hosts_from_file (filename)
 
 /* Return the hostname list. */
 char **
-get_hostname_list ()
+get_hostname_list (void)
 {
   if (hostname_list_initialized == 0)
     initialize_hostname_list ();
@@ -864,7 +865,7 @@ get_hostname_list ()
 }
 
 void
-clear_hostname_list ()
+clear_hostname_list (void)
 {
   register int i;
 
@@ -879,11 +880,11 @@ clear_hostname_list ()
    Initialize the hostname list the first time if necessary.
    The array is malloc ()'ed, but not the individual strings. */
 static char **
-hostnames_matching (text)
-     char *text;
+hostnames_matching (const char *text)
 {
-  register int i, len, nmatch, rsize;
+  int i, nmatch;
   char **result;
+  size_t rsize, len;
 
   if (hostname_list_initialized == 0)
     initialize_hostname_list ();
@@ -911,7 +912,7 @@ hostnames_matching (text)
        continue;
 
       /* OK, it matches.  Add it to the list. */
-      if (nmatch >= (rsize - 1))
+      if (nmatch + 1 >= rsize)
        {
          rsize = (rsize + 16) - (rsize % 16);
          result = strvec_resize (result, rsize);
@@ -933,9 +934,7 @@ hostnames_matching (text)
 #define POSIX_VI_EDIT_COMMAND  "fc -e vi"
 
 static int
-edit_and_execute_command (count, c, editing_mode, edit_command)
-     int count, c, editing_mode;
-     char *edit_command;
+edit_and_execute_command (int count, int c, int editing_mode, const char *edit_command)
 {
   char *command, *metaval;
   int r, rrs, metaflag;
@@ -1009,27 +1008,24 @@ edit_and_execute_command (count, c, editing_mode, edit_command)
 
 #if defined (VI_MODE)
 static int
-vi_edit_and_execute_command (count, c)
-     int count, c;
+vi_edit_and_execute_command (int count, int key)
 {
   if (posixly_correct)
-    return (edit_and_execute_command (count, c, VI_EDITING_MODE, POSIX_VI_EDIT_COMMAND));
+    return (edit_and_execute_command (count, key, VI_EDITING_MODE, POSIX_VI_EDIT_COMMAND));
   else
-    return (edit_and_execute_command (count, c, VI_EDITING_MODE, VI_EDIT_COMMAND));
+    return (edit_and_execute_command (count, key, VI_EDITING_MODE, VI_EDIT_COMMAND));
 }
 #endif /* VI_MODE */
 
 static int
-emacs_edit_and_execute_command (count, c)
-     int count, c;
+emacs_edit_and_execute_command (int count, int key)
 {
-  return (edit_and_execute_command (count, c, EMACS_EDITING_MODE, EMACS_EDIT_COMMAND));
+  return (edit_and_execute_command (count, key, EMACS_EDITING_MODE, EMACS_EDIT_COMMAND));
 }
 
 #if defined (ALIAS)
 static int
-posix_edit_macros (count, key)
-     int count, key;
+posix_edit_macros (int count, int key)
 {
   int c;
   char alias_name[3], *alias_value, *macro;
@@ -1057,8 +1053,7 @@ posix_edit_macros (count, key)
 #define WORDDELIM(c)   (shellmeta(c) || shellblank(c))
 
 static int
-bash_forward_shellword (count, key)
-     int count, key;
+bash_forward_shellword (int count, int key)
 {
   size_t slen;
   int c, p;
@@ -1166,8 +1161,7 @@ bash_forward_shellword (count, key)
 }
 
 static int
-bash_backward_shellword (count, key)
-     int count, key;
+bash_backward_shellword (int count, int key)
 {
   size_t slen;
   int c, p, prev_p;
@@ -1228,8 +1222,7 @@ bash_backward_shellword (count, key)
 }
 
 static int
-bash_kill_shellword (count, key)
-     int count, key;
+bash_kill_shellword (int count, int key)
 {
   int p;
 
@@ -1250,8 +1243,7 @@ bash_kill_shellword (count, key)
 }
 
 static int
-bash_backward_kill_shellword (count, key)
-     int count, key;
+bash_backward_kill_shellword (int count, int key)
 {
   int p;
 
@@ -1271,8 +1263,7 @@ bash_backward_kill_shellword (count, key)
 }
 
 static int
-bash_transpose_shellwords (count, key)
-     int count, key;
+bash_transpose_shellwords (int count, int key)
 {
   char *word1, *word2;
   int w1_beg, w1_end, w2_beg, w2_end;
@@ -1332,13 +1323,11 @@ bash_transpose_shellwords (count, key)
 /* Directory name spelling correction on the current word (not shellword).
    COUNT > 1 is not exactly correct yet. */
 static int
-bash_spell_correct_shellword (count, key)
-     int count, key;
+bash_spell_correct_shellword (int count, int key)
 {
-  int opoint, wbeg, wend;
+  int wbeg, wend;
   char *text, *newdir;
 
-  opoint = rl_point;
   while (count)
     {
       bash_backward_shellword (1, key);
@@ -1350,6 +1339,8 @@ bash_spell_correct_shellword (count, key)
        break;
 
       text = rl_copy_text (wbeg, wend);
+      if (text == 0 || *text == 0)
+       break;
 
       newdir = dirspell (text);
       if (newdir)
@@ -1391,9 +1382,8 @@ bash_spell_correct_shellword (count, key)
 
 /* check for redirections and other character combinations that are not
    command separators */
-static int
-check_redir (ti)
-     int ti;
+static inline int
+check_redir (int ti)
 {
   register int this_char, prev_char;
 
@@ -1429,8 +1419,7 @@ check_redir (ti)
  * happen, but fix later after 2.05a-release.
  */
 static int
-find_cmd_start (start)
-     int start;
+find_cmd_start (int start)
 {
   register int s, os, ns;
 
@@ -1478,8 +1467,7 @@ find_cmd_start (start)
 }
 
 static int
-find_cmd_end (end)
-     int end;
+find_cmd_end (int end)
 {
   register int e;
 
@@ -1488,9 +1476,7 @@ find_cmd_end (end)
 }
 
 static char *
-find_cmd_name (start, sp, ep)
-     int start;
-     int *sp, *ep;
+find_cmd_name (int start, int *sp, int *ep)
 {
   char *name;
   register int s, e;
@@ -1512,9 +1498,7 @@ find_cmd_name (start, sp, ep)
 }
 
 static char *
-prog_complete_return (text, matchnum)
-     const char *text;
-     int matchnum;
+prog_complete_return (const char *text, int matchnum)
 {
   static int ind;
 
@@ -1531,9 +1515,7 @@ prog_complete_return (text, matchnum)
 /* Try and catch completion attempts that are syntax errors or otherwise
    invalid. */
 static int
-invalid_completion (text, ind)
-     const char *text;
-     int ind;
+invalid_completion (const char *text, int ind)
 {
   int pind;
 
@@ -1559,9 +1541,7 @@ invalid_completion (text, ind)
 /* Do some completion on TEXT.  The indices of TEXT in RL_LINE_BUFFER are
    at START and END.  Return an array of matches, or NULL if none. */
 static char **
-attempt_shell_completion (text, start, end)
-     const char *text;
-     int start, end;
+attempt_shell_completion (const char *text, int start, int end)
 {
   int in_command_position, ti, qc, dflags;
   char **matches, *command_separator_chars;
@@ -1574,10 +1554,10 @@ attempt_shell_completion (text, start, end)
   matches = (char **)NULL;
   rl_ignore_some_completions_function = filename_completion_ignore;
 
+  complete_fullquote = 1;              /* full filename quoting by default */
   rl_filename_quote_characters = default_filename_quote_characters;
   set_filename_bstab (rl_filename_quote_characters);
-  set_directory_hook ();
-  rl_filename_stat_hook = bash_filename_stat_hook;
+  bashline_set_filename_hooks ();
 
   rl_sort_completion_matches = 1;      /* sort by default */
 
@@ -1684,6 +1664,10 @@ attempt_shell_completion (text, start, end)
              s1 = s = e1;
              break;
            }
+         else if (s > e)
+           {
+             s = s1 = start; e = e1 = end;     /* reset */
+           }
          /* Skip over assignment statements preceding a command name.  If we
             don't find a command name at all, we can perform command name
             completion.  If we find a partial command name, we should perform
@@ -1703,10 +1687,12 @@ attempt_shell_completion (text, start, end)
         end == index of where word to be completed ends
         if (s == start) we are doing command word completion for sure
         if (e1 == end) we are at the end of the command name and completing it */
-      if (start == 0 && end == 0 && e != 0 && text[0] == '\0') /* beginning of non-empty line */
+      if (start == 0 && end == 0 && e != 0 && e1 < rl_end && text[0] == '\0')  /* beginning of non-empty line */
         foundcs = 0;
       else if (start == end && start == s1 && e != 0 && e1 > end)      /* beginning of command name, leading whitespace */
        foundcs = 0;
+      else if (start == 0 && start == end && start < s1 && e != 0 && e1 > end && text[0] == '\0' && have_progcomps)    /* no command name, leading whitespace only */
+        prog_complete_matches = programmable_completions (EMPTYCMD, text, s, e, &foundcs);
       else if (e == 0 && e == s && text[0] == '\0' && have_progcomps)  /* beginning of empty line */
         prog_complete_matches = programmable_completions (EMPTYCMD, text, s, e, &foundcs);
       else if (start == end && text[0] == '\0' && s1 > start && whitespace (rl_line_buffer[start]))
@@ -1750,7 +1736,14 @@ attempt_shell_completion (text, start, end)
       /* If we have defined a compspec for the initial (command) word, call
         it and process the results like any other programmable completion. */
       if (in_command_position && have_progcomps && foundcs == 0 && iw_compspec)
-       prog_complete_matches = programmable_completions (INITIALWORD, text, s, e, &foundcs);
+       {
+         /* Do some sanity checking on S and E. Room for more here. */
+         if (s > rl_point)
+           s = rl_point;
+         if (e > rl_end)
+           e = rl_end;
+         prog_complete_matches = programmable_completions (INITIALWORD, text, s, e, &foundcs);
+       }
 
       FREE (n);
       /* XXX - if we found a COMPSPEC for the command, just return whatever
@@ -1785,9 +1778,7 @@ attempt_shell_completion (text, start, end)
 }
 
 char **
-bash_default_completion (text, start, end, qc, compflags)
-     const char *text;
-     int start, end, qc, compflags;
+bash_default_completion (const char *text, int start, int end, int qc, int compflags)
 {
   char **matches, *t;
 
@@ -1864,7 +1855,12 @@ bash_default_completion (text, start, end, qc, compflags)
              rl_completion_suppress_append = 1;
              rl_filename_completion_desired = 0;
            }
+#if 1
+         else if (matches[0] && matches[1] && STREQ (matches[0], matches[1]) &&
+                  matches[2] && STREQ (matches[1], matches[2]) && CMD_IS_DIR (matches[0]))
+#else
          else if (matches[0] && matches[1] && STREQ (matches[0], matches[1]) && CMD_IS_DIR (matches[0]))
+#endif
            /* There are multiple instances of the same match (duplicate
               completions haven't yet been removed).  In this case, all of
               the matches will be the same, and the duplicate removal code
@@ -1882,7 +1878,7 @@ bash_default_completion (text, start, end, qc, compflags)
 
   /* This could be a globbing pattern, so try to expand it using pathname
      expansion. */
-  if (!matches && completion_glob_pattern ((char *)text))
+  if (!matches && completion_glob_pattern (text))
     {
       matches = rl_completion_matches (text, glob_complete_word);
       /* A glob expression that matches more than one filename is problematic.
@@ -1903,8 +1899,7 @@ bash_default_completion (text, start, end, qc, compflags)
 }
 
 static int
-bash_command_name_stat_hook (name)
-     char **name;
+bash_command_name_stat_hook (char **name)
 {
   char *cname, *result;
 
@@ -1919,6 +1914,7 @@ bash_command_name_stat_hook (name)
   result = search_for_command (cname, 0);
   if (result)
     {
+      FREE (*name);
       *name = result;
       return 1;
     }
@@ -1926,14 +1922,15 @@ bash_command_name_stat_hook (name)
 }
 
 static int
-executable_completion (filename, searching_path)
-     const char *filename;
-     int searching_path;
+executable_completion (const char *filename, int searching_path)
 {
-  char *f;
+  char *f, c;
   int r;
 
-  f = savestring (filename);
+  /* This gets an unquoted filename, so we need to quote special characters
+     in the filename before the completion hook gets it. */
+  c = 0;
+  f = bash_quote_filename ((char *)filename, SINGLE_MATCH, &c);
   bash_directory_completion_hook (&f);
   
   r = searching_path ? executable_file (f) : executable_or_directory (f);
@@ -1946,9 +1943,7 @@ executable_completion (filename, searching_path)
    that match.  It also scans aliases, function names, and the shell_builtin
    table. */
 char *
-command_word_completion_function (hint_text, state)
-     const char *hint_text;
-     int state;
+command_word_completion_function (const char *hint_text, int state)
 {
   static char *hint = (char *)NULL;
   static char *path = (char *)NULL;
@@ -1958,7 +1953,8 @@ command_word_completion_function (hint_text, state)
   static char *dequoted_hint = (char *)NULL;
   static char *directory_part = (char *)NULL;
   static char **glob_matches = (char **)NULL;
-  static int path_index, hint_len, istate, igncase;
+  static int path_index, istate, igncase;
+  static size_t hint_len;
   static int mapping_over, local_index, searching_path, hint_is_dir;
   static int old_glob_ignore_case, globpat;
   static SHELL_VAR **varlist = (SHELL_VAR **)NULL;
@@ -1977,6 +1973,7 @@ command_word_completion_function (hint_text, state)
        free (dequoted_hint);
       if (hint)
        free (hint);
+      dequoted_hint = hint = (char *)NULL;
 
       mapping_over = searching_path = 0;
       hint_is_dir = CMD_IS_DIR (hint_text);
@@ -1985,13 +1982,15 @@ command_word_completion_function (hint_text, state)
       temp = rl_variable_value ("completion-ignore-case");
       igncase = RL_BOOLEAN_VARIABLE_VALUE (temp);
 
+      old_glob_ignore_case = glob_ignore_case;
+
       if (glob_matches)
        {
          free (glob_matches);
          glob_matches = (char **)NULL;
        }
 
-      globpat = completion_glob_pattern ((char *)hint_text);
+      globpat = completion_glob_pattern (hint_text);
 
       /* If this is an absolute program name, do not check it against
         aliases, reserved words, functions or builtins.  We must check
@@ -2069,7 +2068,7 @@ command_word_completion_function (hint_text, state)
       if (rl_completion_found_quote && rl_completion_quote_character == 0)
        dequoted_hint = bash_dequote_filename (hint, 0);
       
-      path = get_string_value ("PATH");
+      path = path_value ("PATH", 0);
       path_index = dot_in_path = 0;
 
       /* Initialize the variables for each type of command word. */
@@ -2170,6 +2169,8 @@ globword:
     {
       if (state == 0)
        {
+         rl_filename_completion_desired = 1;
+
          glob_ignore_case = igncase;
          glob_matches = shell_glob_filename (hint, 0);
          glob_ignore_case = old_glob_ignore_case;
@@ -2183,7 +2184,11 @@ globword:
          local_index = 0;
                
          if (glob_matches[1] && rl_completion_type == TAB)     /* multiple matches are bad */
-           return ((char *)NULL);
+           {
+             strvec_dispose (glob_matches);
+             glob_matches = (char **)NULL;
+             return ((char *)NULL);
+           }
        }
 
       while (val = glob_matches[local_index++])
@@ -2255,6 +2260,7 @@ globword:
        free (fnhint);
       if (filename_hint)
        free (filename_hint);
+      fnhint = filename_hint = (char *)NULL;
 
       filename_hint = sh_makepath (current_path, hint, 0);
       /* Need a quoted version (though it doesn't matter much in most
@@ -2290,10 +2296,16 @@ globword:
 
       if (absolute_program (hint))
        {
+#if 0
          if (igncase == 0)
            match = strncmp (val, hint, hint_len) == 0;
          else
            match = strncasecmp (val, hint, hint_len) == 0;
+#else
+         /* Why duplicate the comparison rl_filename_completion_function
+            already performs? */
+         match = 1;
+#endif
 
          /* If we performed tilde expansion, restore the original
             filename. */
@@ -2344,6 +2356,7 @@ globword:
          t1 = make_absolute (val, t);
          free (t);
          cval = sh_canonpath (t1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
+         free (t1);
        }
       else
 #endif
@@ -2371,9 +2384,7 @@ globword:
 
 /* Completion inside an unterminated command substitution. */
 static char *
-command_subst_completion_function (text, state)
-     const char *text;
-     int state;
+command_subst_completion_function (const char *text, int state)
 {
   static char **matches = (char **)NULL;
   static const char *orig_start;
@@ -2396,7 +2407,10 @@ command_subst_completion_function (text, state)
       start_len = text - orig_start;
       filename_text = savestring (text);
       if (matches)
-       free (matches);
+       {
+         free (matches);
+         matches = (char **)NULL;
+       }
 
       /*
        * At this point we can entertain the idea of re-parsing
@@ -2454,9 +2468,7 @@ command_subst_completion_function (text, state)
 
 /* Okay, now we write the entry_function for variable completion. */
 static char *
-variable_completion_function (text, state)
-     const char *text;
-     int state;
+variable_completion_function (const char *text, int state)
 {
   static char **varlist = (char **)NULL;
   static int varlist_index;
@@ -2514,9 +2526,7 @@ variable_completion_function (text, state)
 
 /* How about a completion function for hostnames? */
 static char *
-hostname_completion_function (text, state)
-     const char *text;
-     int state;
+hostname_completion_function (const char *text, int state)
 {
   static char **list = (char **)NULL;
   static int list_index = 0;
@@ -2557,16 +2567,14 @@ hostname_completion_function (text, state)
  * A completion function for service names from /etc/services (or wherever).
  */
 char *
-bash_servicename_completion_function (text, state)
-     const char *text;
-     int state;
+bash_servicename_completion_function (const char *text, int state)
 {
 #if defined (__WIN32__) || defined (__OPENNT) || !defined (HAVE_GETSERVENT)
   return ((char *)NULL);
 #else
   static char *sname = (char *)NULL;
   static struct servent *srvent;
-  static int snamelen;
+  static size_t snamelen;
   char *value;
   char **alist, *aentry;
   int afound;
@@ -2615,16 +2623,14 @@ bash_servicename_completion_function (text, state)
  * A completion function for group names from /etc/group (or wherever).
  */
 char *
-bash_groupname_completion_function (text, state)
-     const char *text;
-     int state;
+bash_groupname_completion_function (const char *text, int state)
 {
 #if defined (__WIN32__) || defined (__OPENNT) || !defined (HAVE_GRP_H)
   return ((char *)NULL);
 #else
   static char *gname = (char *)NULL;
   static struct group *grent;
-  static int gnamelen;
+  static size_t gnamelen;
   char *value;
 
   if (state == 0)
@@ -2660,8 +2666,7 @@ bash_groupname_completion_function (text, state)
    is done, pre_process_line() returns what it was passed, so we need to
    allocate a new line here. */
 static char *
-history_expand_line_internal (line)
-     char *line;
+history_expand_line_internal (char *line)
 {
   char *new_line;
   int old_verify;
@@ -2678,7 +2683,7 @@ history_expand_line_internal (line)
 /* There was an error in expansion.  Let the preprocessor print
    the error here. */
 static void
-cleanup_expansion_error ()
+cleanup_expansion_error (void)
 {
   char *to_free;
 #if defined (BANG_HISTORY)
@@ -2703,8 +2708,7 @@ cleanup_expansion_error ()
    undo record to get from the readline line buffer contents to the new
    line and make NEW_LINE the current readline line. */
 static void
-maybe_make_readline_line (new_line)
-     char *new_line;
+maybe_make_readline_line (const char *new_line)
 {
   if (new_line && strcmp (new_line, rl_line_buffer) != 0)
     {
@@ -2718,15 +2722,27 @@ maybe_make_readline_line (new_line)
     }
 }
 
-/* Make NEW_LINE be the current readline line.  This frees NEW_LINE. */
+/* Make NEW_LINE be the current readline line.  This frees NEW_LINE. We use
+   several heuristics to decide where to put rl_point. */
+
 static void
-set_up_new_line (new_line)
-     char *new_line;
+set_up_new_line (char *new_line)
 {
-  int old_point, at_end;
+
+  int old_point, old_end, dist, nb;
+  char *s, *t;
+
+  /* If we didn't expand anything, don't change anything. */
+  if (STREQ (new_line, rl_line_buffer))
+    {
+      free (new_line);
+      return;
+    }
 
   old_point = rl_point;
-  at_end = rl_point == rl_end;
+  old_end = rl_end;
+  dist = rl_end - rl_point;
+  nb = rl_end - old_end;
 
   /* If the line was history and alias expanded, then make that
      be one thing to undo. */
@@ -2734,21 +2750,37 @@ set_up_new_line (new_line)
   free (new_line);
 
   /* Place rl_point where we think it should go. */
-  if (at_end)
+  if (dist == 0)
     rl_point = rl_end;
-  else if (old_point < rl_end)
+  else if (old_point == 0)
     {
+      /* this is what the old code did, but separate it out so we can treat
+        it differently */
+      rl_point = 0;
+      if (!whitespace (rl_line_buffer[rl_point]))
+       rl_forward_word (1, 0);
+    }
+  else if (rl_point < old_point+nb)
+    {
+      /* let's assume that this means the new point is within the changed region */
       rl_point = old_point;
       if (!whitespace (rl_line_buffer[rl_point]))
        rl_forward_word (1, 0);
     }
+  else
+    rl_point = rl_end - dist;  /* try to put point the same distance from end */
+
+  if (rl_point < 0)
+    rl_point = 0;
+  else if (rl_point > rl_end)
+    rl_point = rl_end;
+  rl_mark = 0;         /* XXX */
 }
 
 #if defined (ALIAS)
 /* Expand aliases in the current readline line. */
 static int
-alias_expand_line (count, ignore)
-     int count, ignore;
+alias_expand_line (int count, int ignore)
 {
   char *new_line;
 
@@ -2770,8 +2802,7 @@ alias_expand_line (count, ignore)
 #if defined (BANG_HISTORY)
 /* History expand the line. */
 static int
-history_expand_line (count, ignore)
-     int count, ignore;
+history_expand_line (int count, int ignore)
 {
   char *new_line;
 
@@ -2792,8 +2823,7 @@ history_expand_line (count, ignore)
 /* Expand history substitutions in the current line and then insert a
    space (hopefully close to where we were before). */
 static int
-tcsh_magic_space (count, ignore)
-     int count, ignore;
+tcsh_magic_space (int count, int ignore)
 {
   int dist_from_end, old_point;
 
@@ -2815,20 +2845,15 @@ tcsh_magic_space (count, ignore)
 
 /* History and alias expand the line. */
 static int
-history_and_alias_expand_line (count, ignore)
-     int count, ignore;
+history_and_alias_expand_line (int count, int ignore)
 {
-  char *new_line, *t;
+  char *new_line;
 
   new_line = 0;
 #if defined (BANG_HISTORY)
   new_line = history_expand_line_internal (rl_line_buffer);
 #endif
 
-  t = expand_string_dollar_quote (new_line ? new_line : rl_line_buffer, 0);
-  FREE (new_line);
-  new_line = t;
-
 #if defined (ALIAS)
   if (new_line)
     {
@@ -2857,8 +2882,7 @@ history_and_alias_expand_line (count, ignore)
    because we want the variable expansions as a separate undo'able
    set of operations. */
 static int
-shell_expand_line (count, ignore)
-     int count, ignore;
+shell_expand_line (int count, int ignore)
 {
   char *new_line, *t;
   WORD_LIST *expanded_string;
@@ -2959,12 +2983,10 @@ static struct ignorevar fignore =
 };
 
 static void
-_ignore_completion_names (names, name_func)
-     char **names;
-     sh_ignore_func_t *name_func;
+_ignore_completion_names (char **names, sh_ignore_func_t *name_func)
 {
   char **newnames;
-  int idx, nidx;
+  size_t idx, nidx;
   char **oldnames;
   int oidx;
 
@@ -3050,15 +3072,14 @@ _ignore_completion_names (names, name_func)
 }
 
 static int
-name_is_acceptable (name)
-     const char *name;
+name_is_acceptable (const char *name)
 {
   struct ign *p;
-  int nlen;
+  size_t nlen;
 
   for (nlen = strlen (name), p = fignore.ignores; p->val; p++)
     {
-      if (nlen > p->len && p->len > 0 && STREQ (p->val, &name[nlen - p->len]))
+      if (nlen >= p->len && p->len > 0 && STREQ (p->val, &name[nlen - p->len]))
        return (0);
     }
 
@@ -3067,16 +3088,14 @@ name_is_acceptable (name)
 
 #if 0
 static int
-ignore_dot_names (name)
-     char *name;
+ignore_dot_names (const char *name)
 {
   return (name[0] != '.');
 }
 #endif
 
 static int
-filename_completion_ignore (names)
-     char **names;
+filename_completion_ignore (char **names)
 {
 #if 0
   if (glob_dot_filenames == 0)
@@ -3095,8 +3114,7 @@ filename_completion_ignore (names)
 
 /* Return 1 if NAME is a directory.  NAME undergoes tilde expansion. */
 static int
-test_for_directory (name)
-     const char *name;
+test_for_directory (const char *name)
 {
   char *fn;
   int r;
@@ -3109,8 +3127,7 @@ test_for_directory (name)
 }
 
 static int
-test_for_canon_directory (name)
-     const char *name;
+test_for_canon_directory (const char *name)
 {
   char *fn;
   int r;
@@ -3125,31 +3142,27 @@ test_for_canon_directory (name)
 
 /* Remove files from NAMES, leaving directories. */
 static int
-bash_ignore_filenames (names)
-     char **names;
+bash_ignore_filenames (char **names)
 {
   _ignore_completion_names (names, test_for_directory);
   return 0;
 }
 
 static int
-bash_progcomp_ignore_filenames (names)
-     char **names;
+bash_progcomp_ignore_filenames (char **names)
 {
   _ignore_completion_names (names, test_for_canon_directory);
   return 0;
 }
 
 static int
-return_zero (name)
-     const char *name;
+return_zero (const char *name)
 {
   return 0;
 }
 
 static int
-bash_ignore_everything (names)
-     char **names;
+bash_ignore_everything (char **names)
 {
   _ignore_completion_names (names, return_zero);
   return 0;
@@ -3159,10 +3172,9 @@ bash_ignore_everything (names)
    is an expanded filename.  DIRECTORY_PART is the tilde-prefix portion
    of the un-tilde-expanded version of VAL (what the user typed). */
 static char *
-restore_tilde (val, directory_part)
-     char *val, *directory_part;
+restore_tilde (const char *val, char *directory_part)
 {
-  int l, vl, dl2, xl;
+  size_t l, vl, dl2, xl;
   char *dh2, *expdir, *ret, *v;
 
   vl = strlen (val);
@@ -3216,8 +3228,7 @@ restore_tilde (val, directory_part)
 }
 
 static char *
-maybe_restore_tilde (val, directory_part)
-     char *val, *directory_part;
+maybe_restore_tilde (char *val, char *directory_part)
 {
   rl_icppfunc_t *save;
   char *ret;
@@ -3233,8 +3244,7 @@ maybe_restore_tilde (val, directory_part)
    rl_filename_completion_function.  This must be called with the address of
    a pointer to malloc'd memory. */
 static void
-bash_directory_expansion (dirname)
-     char **dirname;
+bash_directory_expansion (char **dirname)
 {
   char *d, *nd;
 
@@ -3257,13 +3267,13 @@ bash_directory_expansion (dirname)
       free (d);
       *dirname = nd;
     }
+  else
+    free (d);
 }
 
 /* If necessary, rewrite directory entry */
 static char *
-bash_filename_rewrite_hook (fname, fnlen)
-     char *fname;
-     int fnlen;
+bash_filename_rewrite_hook (char *fname, int fnlen)
 {
   char *conv;
 
@@ -3276,7 +3286,7 @@ bash_filename_rewrite_hook (fname, fnlen)
 /* Functions to save and restore the appropriate directory hook */
 /* This is not static so the shopt code can call it */
 void
-set_directory_hook ()
+set_directory_hook (void)
 {
   if (dircomplete_expand)
     {
@@ -3291,7 +3301,7 @@ set_directory_hook ()
 }
 
 static rl_icppfunc_t *
-save_directory_hook ()
+save_directory_hook (void)
 {
   rl_icppfunc_t *ret;
 
@@ -3310,8 +3320,7 @@ save_directory_hook ()
 }
 
 static void
-restore_directory_hook (hookf)
-     rl_icppfunc_t *hookf;
+restore_directory_hook (rl_icppfunc_t *hookf)
 {
   if (dircomplete_expand)
     rl_directory_completion_hook = hookf;
@@ -3319,12 +3328,24 @@ restore_directory_hook (hookf)
     rl_directory_rewrite_hook = hookf;
 }
 
+/* Set the readline hooks that affect how directories and filenames are
+   converted. Extern so other parts of the shell can use. */
+void
+bashline_set_filename_hooks (void)
+{
+  /* Tell the completer that we might want to follow symbolic links or
+     do other expansion on directory names. */
+  set_directory_hook ();
+
+  rl_filename_rewrite_hook = bash_filename_rewrite_hook;
+  rl_completion_rewrite_hook = bash_filename_rewrite_hook;
+  rl_filename_stat_hook = bash_filename_stat_hook;
+}
+
 /* Check whether not DIRNAME, with any trailing slash removed, exists.  If
    SHOULD_DEQUOTE is non-zero, we dequote the directory name first. */
 static int
-directory_exists (dirname, should_dequote)
-     const char *dirname;
-     int should_dequote;
+directory_exists (const char *dirname, int should_dequote)
 {
   char *new_dirname;
   int dirlen, r;
@@ -3344,12 +3365,42 @@ directory_exists (dirname, should_dequote)
   free (new_dirname);
   return (r);
 }
+
+static char *
+bash_expand_filename (char *filename)
+{
+  char *newname;
+  int global_nounset;
+  WORD_LIST *wl;
+
+  newname = savestring (filename);
+  /* no error messages, and expand_prompt_string doesn't longjmp so we don't
+     have to worry about restoring this setting. */
+  global_nounset = unbound_vars_is_error;
+  unbound_vars_is_error = 0;
+  wl = expand_prompt_string (newname, 0, W_NOCOMSUB|W_NOPROCSUB|W_COMPLETE);   /* does the right thing */
+  unbound_vars_is_error = global_nounset;
+  free (newname);
+
+  if (wl == 0)
+    return filename;
+  else
+    {
+      newname = string_list (wl);
+      dispose_words (wl);
+      if (newname && *newname && STREQ (newname, filename))
+       {
+         free (newname);
+         return filename;
+       }
+      return newname;
+    }
+}
   
 /* Expand a filename before the readline completion code passes it to stat(2).
    The filename will already have had tilde expansion performed. */
 static int
-bash_filename_stat_hook (dirname)
-     char **dirname;
+bash_filename_stat_hook (char **dirname)
 {
   char *local_dirname, *new_dirname, *t;
   int should_expand_dirname, return_value;
@@ -3428,8 +3479,7 @@ bash_filename_stat_hook (dirname)
    the DIRNAME argument, 0 otherwise.  It should make sure not to modify
    DIRNAME if it returns 0. */
 static int
-bash_directory_completion_hook (dirname)
-     char **dirname;
+bash_directory_completion_hook (char **dirname)
 {
   char *local_dirname, *new_dirname, *t;
   int return_value, should_expand_dirname, nextch, closer;
@@ -3438,38 +3488,7 @@ bash_directory_completion_hook (dirname)
   return_value = should_expand_dirname = nextch = closer = 0;
   local_dirname = *dirname;
 
-  if (t = mbschr (local_dirname, '$'))
-    {
-      should_expand_dirname = '$';
-      nextch = t[1];
-      /* Deliberately does not handle the deprecated $[...] arithmetic
-        expansion syntax */
-      if (nextch == '(')
-       closer = ')';
-      else if (nextch == '{')
-       closer = '}';
-      else
-       nextch = 0;
-
-      if (closer)
-       {
-         int p;
-         char delims[2];
-
-         delims[0] = closer; delims[1] = 0;
-         p = skip_to_delim (t, 1, delims, SD_NOJMP|SD_COMPLETE);
-         if (t[p] != closer)
-           should_expand_dirname = 0;
-       }
-    }
-  else if (local_dirname[0] == '~')
-    should_expand_dirname = '~';
-  else
-    {
-      t = mbschr (local_dirname, '`');
-      if (t && unclosed_pair (local_dirname, strlen (local_dirname), "`") == 0)
-       should_expand_dirname = '`';
-    }
+  should_expand_dirname = bash_check_expchar (local_dirname, 1, &nextch, &closer);
 
   if (should_expand_dirname && directory_exists (local_dirname, 1))
     should_expand_dirname = 0;
@@ -3488,24 +3507,8 @@ bash_directory_completion_hook (dirname)
          free (new_dirname);
          dispose_words (wl);
          local_dirname = *dirname;
-         /* XXX - change rl_filename_quote_characters here based on
-            should_expand_dirname/nextch/closer.  This is the only place
-            custom_filename_quote_characters is modified. */
-         if (rl_filename_quote_characters && *rl_filename_quote_characters)
-           {
-             int i, j, c;
-             i = strlen (default_filename_quote_characters);
-             custom_filename_quote_characters = xrealloc (custom_filename_quote_characters, i+1);
-             for (i = j = 0; c = default_filename_quote_characters[i]; i++)
-               {
-                 if (c == should_expand_dirname || c == nextch || c == closer)
-                   continue;
-                 custom_filename_quote_characters[j++] = c;
-               }
-             custom_filename_quote_characters[j] = '\0';
-             rl_filename_quote_characters = custom_filename_quote_characters;
-             set_filename_bstab (rl_filename_quote_characters);
-           }
+
+         set_filename_quote_chars (should_expand_dirname, nextch, closer);
        }
       else
        {
@@ -3539,7 +3542,7 @@ bash_directory_completion_hook (dirname)
   if (no_symbolic_links == 0 && (local_dirname[0] != '.' || local_dirname[1]))
     {
       char *temp1, *temp2;
-      int len1, len2;
+      size_t len1, len2;
 
       /* If we have a relative path
                (local_dirname[0] != '/' && local_dirname[0] != '.')
@@ -3560,7 +3563,16 @@ bash_directory_completion_hook (dirname)
         subsequent directory checks don't fail. */
       if (temp2 == 0 && dircomplete_spelling && dircomplete_expand)
        {
+         size_t l1, l2;
+
          temp2 = dirspell (temp1);
+         l2 = STRLEN (temp2);
+         /* Don't take matches if they are shorter than the original path */
+         if (temp2 && l2 < strlen (temp1) && STREQN (temp1, temp2, l2))
+           {
+             free (temp2);
+             temp2 = 0;
+           }
          if (temp2)
            {
              free (temp1);
@@ -3604,11 +3616,11 @@ bash_directory_completion_hook (dirname)
 }
 
 static char **history_completion_array = (char **)NULL;
-static int harry_size;
-static int harry_len;
+static size_t harry_size;
+static size_t harry_len;
 
 static void
-build_history_completion_array ()
+build_history_completion_array (void)
 {
   register int i, j;
   HIST_ENTRY **hlist;
@@ -3654,11 +3666,10 @@ build_history_completion_array ()
 }
 
 static char *
-history_completion_generator (hint_text, state)
-     const char *hint_text;
-     int state;
+history_completion_generator (const char *hint_text, int state)
 {
-  static int local_index, len;
+  static int local_index;
+  static size_t len;
   static const char *text;
 
   /* If this is the first call to the generator, then initialize the
@@ -3683,8 +3694,7 @@ history_completion_generator (hint_text, state)
 }
 
 static int
-dynamic_complete_history (count, key)
-     int count, key;
+dynamic_complete_history (int count, int key)
 {
   int r;
   rl_compentry_func_t *orig_func;
@@ -3713,8 +3723,7 @@ dynamic_complete_history (count, key)
 }
 
 static int
-bash_dabbrev_expand (count, key)
-     int count, key;
+bash_dabbrev_expand (int count, int key)
 {
   int r, orig_suppress, orig_sort;
   rl_compentry_func_t *orig_func;
@@ -3753,49 +3762,43 @@ bash_dabbrev_expand (count, key)
 
 #if defined (SPECIFIC_COMPLETION_FUNCTIONS)
 static int
-bash_complete_username (ignore, ignore2)
-     int ignore, ignore2;
+bash_complete_username (int ignore, int ignore2)
 {
   return bash_complete_username_internal (rl_completion_mode (bash_complete_username));
 }
 
 static int
-bash_possible_username_completions (ignore, ignore2)
-     int ignore, ignore2;
+bash_possible_username_completions (int ignore, int ignore2)
 {
   return bash_complete_username_internal ('?');
 }
 
 static int
-bash_complete_username_internal (what_to_do)
-     int what_to_do;
+bash_complete_username_internal (int what_to_do)
 {
   return bash_specific_completion (what_to_do, rl_username_completion_function);
 }
 
 static int
-bash_complete_filename (ignore, ignore2)
-     int ignore, ignore2;
+bash_complete_filename (int ignore, int ignore2)
 {
   return bash_complete_filename_internal (rl_completion_mode (bash_complete_filename));
 }
 
 static int
-bash_possible_filename_completions (ignore, ignore2)
-     int ignore, ignore2;
+bash_possible_filename_completions (int ignore, int ignore2)
 {
   return bash_complete_filename_internal ('?');
 }
 
 static int
-bash_complete_filename_internal (what_to_do)
-     int what_to_do;
+bash_complete_filename_internal (int what_to_do)
 {
   rl_compentry_func_t *orig_func;
   rl_completion_func_t *orig_attempt_func;
   rl_icppfunc_t *orig_dir_func;
   rl_compignore_func_t *orig_ignore_func;
-  /*const*/ char *orig_rl_completer_word_break_characters;
+  const char *orig_rl_completer_word_break_characters;
   int r;
 
   orig_func = rl_completion_entry_function;
@@ -3823,71 +3826,61 @@ bash_complete_filename_internal (what_to_do)
 }
 
 static int
-bash_complete_hostname (ignore, ignore2)
-     int ignore, ignore2;
+bash_complete_hostname (int ignore, int ignore2)
 {
   return bash_complete_hostname_internal (rl_completion_mode (bash_complete_hostname));
 }
 
 static int
-bash_possible_hostname_completions (ignore, ignore2)
-     int ignore, ignore2;
+bash_possible_hostname_completions (int ignore, int ignore2)
 {
   return bash_complete_hostname_internal ('?');
 }
 
 static int
-bash_complete_variable (ignore, ignore2)
-     int ignore, ignore2;
+bash_complete_variable (int ignore, int ignore2)
 {
   return bash_complete_variable_internal (rl_completion_mode (bash_complete_variable));
 }
 
 static int
-bash_possible_variable_completions (ignore, ignore2)
-     int ignore, ignore2;
+bash_possible_variable_completions (int ignore, int ignore2)
 {
   return bash_complete_variable_internal ('?');
 }
 
 static int
-bash_complete_command (ignore, ignore2)
-     int ignore, ignore2;
+bash_complete_command (int ignore, int ignore2)
 {
   return bash_complete_command_internal (rl_completion_mode (bash_complete_command));
 }
 
 static int
-bash_possible_command_completions (ignore, ignore2)
-     int ignore, ignore2;
+bash_possible_command_completions (int ignore, int ignore2)
 {
   return bash_complete_command_internal ('?');
 }
 
 static int
-bash_complete_hostname_internal (what_to_do)
-     int what_to_do;
+bash_complete_hostname_internal (int what_to_do)
 {
   return bash_specific_completion (what_to_do, hostname_completion_function);
 }
 
 static int
-bash_complete_variable_internal (what_to_do)
-     int what_to_do;
+bash_complete_variable_internal (int what_to_do)
 {
   return bash_specific_completion (what_to_do, variable_completion_function);
 }
 
 static int
-bash_complete_command_internal (what_to_do)
-     int what_to_do;
+bash_complete_command_internal (int what_to_do)
 {
   return bash_specific_completion (what_to_do, command_word_completion_function);
 }
 
 static int
-completion_glob_pattern (string)
-     char *string;
+completion_glob_pattern (const char *string)
 {
   return (glob_pattern_p (string) == 1);
 }
@@ -3896,22 +3889,22 @@ static char *globtext;
 static char *globorig;
 
 static char *
-glob_complete_word (text, state)
-     const char *text;
-     int state;
+glob_complete_word (const char *text, int state)
 {
   static char **matches = (char **)NULL;
   static int ind;
-  int glen;
+  size_t glen;
   char *ret, *ttext;
 
   if (state == 0)
     {
       rl_filename_completion_desired = 1;
       FREE (matches);
+      matches = (char **)NULL;
       if (globorig != globtext)
        FREE (globorig);
       FREE (globtext);
+      globorig = globtext = (char *)NULL;
 
       ttext = bash_tilde_expand (text, 0);
 
@@ -3942,8 +3935,7 @@ glob_complete_word (text, state)
 }
 
 static int
-bash_glob_completion_internal (what_to_do)
-     int what_to_do;
+bash_glob_completion_internal (int what_to_do)
 {
   return bash_specific_completion (what_to_do, glob_complete_word);
 }
@@ -3951,10 +3943,7 @@ bash_glob_completion_internal (what_to_do)
 /* A special quoting function so we don't end up quoting globbing characters
    in the word if there are no matches or multiple matches. */
 static char *
-bash_glob_quote_filename (s, rtype, qcp)
-     char *s;
-     int rtype;
-     char *qcp;
+bash_glob_quote_filename (char *s, int rtype, char *qcp)
 {
   if (globorig && qcp && *qcp == '\0' && STREQ (s, globorig))
     return (savestring (s));
@@ -3963,8 +3952,7 @@ bash_glob_quote_filename (s, rtype, qcp)
 }
 
 static int
-bash_glob_complete_word (count, key)
-     int count, key;
+bash_glob_complete_word (int count, int key)
 {
   int r;
   rl_quote_func_t *orig_quoting_function;
@@ -3981,23 +3969,19 @@ bash_glob_complete_word (count, key)
 }
 
 static int
-bash_glob_expand_word (count, key)
-     int count, key;
+bash_glob_expand_word (int count, int key)
 {
   return bash_glob_completion_internal ('*');
 }
 
 static int
-bash_glob_list_expansions (count, key)
-     int count, key;
+bash_glob_list_expansions (int count, int key)
 {
   return bash_glob_completion_internal ('?');
 }
 
 static int
-bash_specific_completion (what_to_do, generator)
-     int what_to_do;
-     rl_compentry_func_t *generator;
+bash_specific_completion (int what_to_do, rl_compentry_func_t *generator)
 {
   rl_compentry_func_t *orig_func;
   rl_completion_func_t *orig_attempt_func;
@@ -4028,8 +4012,7 @@ bash_specific_completion (what_to_do, generator)
    specifies, which is to append a `*' and attempt filename generation (which
    has the side effect of expanding any globbing characters in the word). */
 static int
-bash_vi_complete (count, key)
-     int count, key;
+bash_vi_complete (int count, int key)
 {
 #if defined (SPECIFIC_COMPLETION_FUNCTIONS)
   int p, r;
@@ -4084,12 +4067,11 @@ bash_vi_complete (count, key)
    quotes, and backslashes).  It allows single quotes to appear
    within double quotes, and vice versa.  It should be smarter. */
 static char *
-bash_dequote_filename (text, quote_char)
-     char *text;
-     int quote_char;
+bash_dequote_filename (char *text, int quote_char)
 {
   char *ret, *p, *r;
-  int l, quoted;
+  int quoted;
+  size_t l;
 
   l = strlen (text);
   ret = (char *)xmalloc (l + 1);
@@ -4133,11 +4115,10 @@ bash_dequote_filename (text, quote_char)
    word break characters with backslashes.  Pass backslash-quoted
    characters through without examination. */
 static char *
-quote_word_break_chars (text)
-     char *text;
+quote_word_break_chars (char *text)
 {
   char *ret, *r, *s;
-  int l;
+  size_t l;
 
   l = strlen (text);
   ret = (char *)xmalloc ((2 * l) + 1);
@@ -4165,12 +4146,97 @@ quote_word_break_chars (text)
   return ret;
 }
 
+/* Return a character in DIRNAME that will cause shell expansion to be
+   performed. If NEXTP is non-null, *NEXTP gets the expansion character that
+   follows RET (e.g., '{' or `(' for `$'). If CLOSERP is non-null, *CLOSERP
+   gets the character that should close <RET><NEXTP>. If NEED_CLOSER is non-
+   zero, any expansion pair that isn't closed causes this function to
+   return 0, which indicates that we didn't find an expansion character. It's
+   used in case DIRNAME is going to be expanded. If DIRNAME is just going to
+   be quoted, NEED_CLOSER will be 0. */
+static int
+bash_check_expchar (char *dirname, int need_closer, int *nextp, int *closerp)
+{
+  char *t;
+  int ret, n, c;
+
+  ret = n = c = 0;
+  if (t = mbschr (dirname, '$'))
+    {
+      ret = '$';
+      n = t[1];
+      /* Deliberately does not handle the deprecated $[...] arithmetic
+        expansion syntax */
+      if (n == '(')
+       c = ')';
+      else if (n == '{')
+       c = '}';
+      else
+       n = 0;
+
+      if (c && need_closer)            /* XXX */
+       {
+         int p;
+         char delims[2];
+
+         delims[0] = c; delims[1] = 0;
+         p = skip_to_delim (t, 1, delims, SD_NOJMP|SD_COMPLETE);
+         if (t[p] != c)
+           ret = 0;
+       }
+    }
+  else if (dirname[0] == '~')
+    ret = '~';
+  else
+    {
+      t = mbschr (dirname, '`');
+      if (t)
+       {
+         if (need_closer == 0)
+           ret = '`';
+         else if (unclosed_pair (dirname, strlen (dirname), "`") == 0)
+           ret = '`';
+       }
+    }
+
+  if (nextp)
+    *nextp = n;
+  if (closerp)
+    *closerp = c;
+
+  return ret;
+}
+
+/* Make sure EXPCHAR and, if non-zero, NEXTCH and CLOSER are not in the set
+   of characters to be backslash-escaped. This is the only place
+   custom_filename_quote_characters is modified. */
+static void
+set_filename_quote_chars (int expchar, int nextch, int closer)
+{
+  size_t i, j;
+  int c;
+
+  if (rl_filename_quote_characters && *rl_filename_quote_characters)
+    {
+      i = strlen (default_filename_quote_characters);
+      custom_filename_quote_characters = xrealloc (custom_filename_quote_characters, i+1);
+      for (i = j = 0; c = default_filename_quote_characters[i]; i++)
+       {
+         if (c == expchar || c == nextch || c == closer)
+           continue;
+         custom_filename_quote_characters[j++] = c;
+       }
+      custom_filename_quote_characters[j] = '\0';
+      rl_filename_quote_characters = custom_filename_quote_characters;
+      set_filename_bstab (rl_filename_quote_characters);
+    }
+}
+
 /* Use characters in STRING to populate the table of characters that should
    be backslash-quoted.  The table will be used for sh_backslash_quote from
    this file. */
 static void
-set_filename_bstab (string)
-     const char *string;
+set_filename_bstab (const char *string)
 {
   const char *s;
 
@@ -4186,13 +4252,11 @@ set_filename_bstab (string)
    quote_word_break_chars on the result.  This returns newly-allocated
    memory. */
 static char *
-bash_quote_filename (s, rtype, qcp)
-     char *s;
-     int rtype;
-     char *qcp;
+bash_quote_filename (char *s, int rtype, char *qcp)
 {
   char *rtext, *mtext, *ret;
-  int rlen, cs;
+  size_t rlen;
+  int cs, expchar, nextch, closer;
 
   rtext = (char *)NULL;
 
@@ -4210,10 +4274,37 @@ bash_quote_filename (s, rtype, qcp)
      the word being completed contains newlines, since those are not
      quoted correctly using backslashes (a backslash-newline pair is
      special to the shell parser). */
-  if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && mbschr (s, '\n'))
+  expchar = nextch = closer = 0;
+  if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && dircomplete_expand == 0 &&
+      (expchar = bash_check_expchar (s, 0, &nextch, &closer)) &&
+      file_exists (s) == 0)
+    {
+      /* If it looks like the name is subject to expansion, see if we want to
+        double-quote it. */
+      if (expchar == '$' || expchar == '`')
+       {
+         char *newname;
+         newname = bash_expand_filename (s);
+         if (newname && strpbrk (newname, rl_filename_quote_characters))
+           cs = COMPLETE_DQUOTE2;
+         if (newname != s)
+           free (newname);
+       }
+      /* Usually this will have been set by bash_directory_completion_hook,
+        but there are cases where it will not be. */
+      if (rl_filename_quote_characters != custom_filename_quote_characters)
+       set_filename_quote_chars (expchar, nextch, closer);
+      complete_fullquote = 0;
+    }
+  else if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && mbschr (s, '\n'))
     cs = COMPLETE_SQUOTE;
   else if (*qcp == '"')
-    cs = COMPLETE_DQUOTE;
+    {
+      if ((expchar = bash_check_expchar (s, 0, &nextch, &closer)) == '$' || expchar == '`')
+       cs = COMPLETE_DQUOTE2;
+      else
+       cs = COMPLETE_DQUOTE;
+    }
   else if (*qcp == '\'')
     cs = COMPLETE_SQUOTE;
 #if defined (BANG_HISTORY)
@@ -4237,6 +4328,9 @@ bash_quote_filename (s, rtype, qcp)
 
   switch (cs)
     {
+    case COMPLETE_DQUOTE2:
+      rtext = sh_mkdoublequoted (mtext, strlen (mtext), 1);    /* For now */
+      break;
     case COMPLETE_DQUOTE:
       rtext = sh_double_quote (mtext);
       break;
@@ -4253,7 +4347,8 @@ bash_quote_filename (s, rtype, qcp)
 
   /* We may need to quote additional characters: those that readline treats
      as word breaks that are not quoted by backslash_quote. */
-  if (rtext && cs == COMPLETE_BSQUOTE)
+  /* XXX - test complete_fullquote here? */
+  if (rtext && cs == COMPLETE_BSQUOTE && rl_completer_word_break_characters)
     {
       mtext = quote_word_break_chars (rtext);
       free (rtext);
@@ -4295,8 +4390,7 @@ static void
 #else
 static int
 #endif
-putx(c)
-     int c;
+putx(int c)
 {
   int x;
   x = putc (c, rl_outstream);
@@ -4306,8 +4400,7 @@ putx(c)
 }
 
 static int
-readline_get_char_offset (ind)
-     int ind;
+readline_get_char_offset (int ind)
 {
   int r, old_ch;
 
@@ -4325,9 +4418,7 @@ readline_get_char_offset (ind)
 }
 
 static void
-readline_set_char_offset (ind, varp)
-     int ind;
-     int *varp;
+readline_set_char_offset (int ind, int *varp)
 {
   int i;
 
@@ -4347,25 +4438,67 @@ readline_set_char_offset (ind, varp)
     }
 }
 
+void
+uw_restore_parser_state (void *ps)
+{
+  restore_parser_state (ps);
+}
+
+void
+uw_rl_set_signals (void *ignore)
+{
+  rl_set_signals ();
+}
+
+static void
+unbind_readline_variables (void)
+{
+  check_unbind_variable ("READLINE_LINE");
+  check_unbind_variable ("READLINE_POINT");
+  check_unbind_variable ("READLINE_MARK");
+  check_unbind_variable ("READLINE_ARGUMENT");
+  array_needs_making = 1;
+}
+
+static void
+uw_unbind_readline_variables (void *ignore)
+{
+  unbind_readline_variables ();
+}
+   
 int
-bash_execute_unix_command (count, key)
-     int count;        /* ignored */
-     int key;
+bash_execute_unix_command (int count, int key)
 {
   int type;
   register int i, r;
   intmax_t mi;
   sh_parser_state_t ps;
-  char *cmd, *value, *ce, old_ch;
+  char *cmd, *value, *ce;
   SHELL_VAR *v;
   char ibuf[INT_STRLEN_BOUND(int) + 1];
   Keymap cmd_xmap;
+  const char *kseq;
+  size_t kslen;
+
+  kseq = rl_executing_keyseq;
+  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)
+    {
+      i = rl_trim_arg_from_keyseq (rl_executing_keyseq, rl_key_sequence_length, rl_get_keymap ());
+      if (i > 0)
+       {
+        kseq = rl_executing_keyseq + i;
+        kslen = rl_key_sequence_length - i;
+       }
+    }
 
   /* First, we need to find the right command to execute.  This is tricky,
      because we might have already indirected into another keymap, so we
      have to walk cmd_xmap using the entire key sequence. */
   cmd_xmap = get_cmd_xmap_from_keymap (rl_get_keymap ());
-  cmd = (char *)rl_function_of_keyseq_len (rl_executing_keyseq, rl_key_sequence_length, cmd_xmap, &type);
+  cmd = (char *)rl_function_of_keyseq_len (kseq, kslen, cmd_xmap, &type);
 
   if (type == ISKMAP && (type = ((Keymap) cmd)[ANYOTHERKEY].type) == ISMACR)
     cmd = (char*)((Keymap) cmd)[ANYOTHERKEY].function;
@@ -4402,10 +4535,22 @@ bash_execute_unix_command (count, key)
   v = bind_int_variable ("READLINE_MARK", value, 0);
   if (v)
     VSETATTR (v, att_exported);
+
+  if (count > 1 || rl_explicit_arg)
+    {
+      value = inttostr (count, ibuf, sizeof (ibuf));
+      v = bind_int_variable ("READLINE_ARGUMENT", value, 0);
+      if (v)
+        VSETATTR (v, att_exported);
+    }
   array_needs_making = 1;
 
+  begin_unwind_frame ("execute-unix-command");
   save_parser_state (&ps);
   rl_clear_signals ();
+  add_unwind_protect (uw_unbind_readline_variables, 0);
+  add_unwind_protect (uw_restore_parser_state, &ps);
+  add_unwind_protect (uw_rl_set_signals, 0);
   r = parse_and_execute (savestring (cmd), "bash_execute_unix_command", SEVAL_NOHIST);
   rl_set_signals ();
   restore_parser_state (&ps);
@@ -4414,17 +4559,15 @@ bash_execute_unix_command (count, key)
   maybe_make_readline_line (v ? value_cell (v) : 0);
 
   v = find_variable ("READLINE_POINT");
-  if (v && legal_number (value_cell (v), &mi))
+  if (v && valid_number (value_cell (v), &mi))
     readline_set_char_offset (mi, &rl_point);
 
   v = find_variable ("READLINE_MARK");
-  if (v && legal_number (value_cell (v), &mi))
+  if (v && valid_number (value_cell (v), &mi))
     readline_set_char_offset (mi, &rl_mark);
 
-  check_unbind_variable ("READLINE_LINE");
-  check_unbind_variable ("READLINE_POINT");
-  check_unbind_variable ("READLINE_MARK");
-  array_needs_making = 1;
+  unbind_readline_variables ();
+  discard_unwind_frame ("execute-unix-command");
 
   /* and restore the readline buffer and display after command execution. */
   /* If we clear the last line of the prompt above, redraw only that last
@@ -4438,21 +4581,35 @@ bash_execute_unix_command (count, key)
   return 0;
 }
 
+/* This only has to handle macros/shell commandsfrom print_unix_command_map */
+static void
+print_unix_command (const char *kseq, const char *value, int readable, const char *prefix)
+{
+  if (readable)
+    fprintf (rl_outstream, "\"%s%s\" \"%s\"\n", prefix ? prefix : "", kseq, value ? value : "");
+  else
+    fprintf (rl_outstream, "%s%s outputs %s\n", prefix ? prefix : "", kseq, value ? value : "");
+}
+
 int
-print_unix_command_map ()
+print_unix_command_map (void)
 {
   Keymap save, cmd_xmap;
+  rl_macro_print_func_t *old_printer;
 
   save = rl_get_keymap ();
   cmd_xmap = get_cmd_xmap_from_keymap (save);
   rl_set_keymap (cmd_xmap);
+  old_printer = rl_macro_display_hook;
+  rl_macro_display_hook = print_unix_command;
   rl_macro_dumper (1);
+  rl_macro_display_hook = old_printer;
   rl_set_keymap (save);
   return 0;
 }
 
 static void
-init_unix_command_map ()
+init_unix_command_map (void)
 {
   emacs_std_cmd_xmap = rl_make_bare_keymap ();
 
@@ -4468,7 +4625,7 @@ init_unix_command_map ()
 }
 
 static Keymap
-get_cmd_xmap_from_edit_mode ()
+get_cmd_xmap_from_edit_mode (void)
 {
   if (emacs_std_cmd_xmap == 0)
     init_unix_command_map ();
@@ -4487,8 +4644,7 @@ get_cmd_xmap_from_edit_mode ()
 }
 
 static Keymap
-get_cmd_xmap_from_keymap (kmap)
-     Keymap kmap;
+get_cmd_xmap_from_keymap (Keymap kmap)
 {
   if (emacs_std_cmd_xmap == 0)
     init_unix_command_map ();
@@ -4510,9 +4666,7 @@ get_cmd_xmap_from_keymap (kmap)
 }
 
 static int
-isolate_sequence (string, ind, need_dquote, startp)
-     char *string;
-     int ind, need_dquote, *startp;
+isolate_sequence (char *string, int ind, int need_dquote, int *startp)
 {
   register int i;
   int c, passc, delim;
@@ -4559,17 +4713,16 @@ isolate_sequence (string, ind, need_dquote, startp)
 }
 
 int
-bind_keyseq_to_unix_command (line)
-     char *line;
+bind_keyseq_to_unix_command (char *line)
 {
   Keymap kmap, cmd_xmap;
   char *kseq, *value;
-  int i, kstart;
+  int i, kstart, translate;
 
   kmap = rl_get_keymap ();
 
   /* We duplicate some of the work done by rl_parse_and_bind here, but
-     this code only has to handle `"keyseq"["]command["]' and can
+     this code only has to handle `"keyseq"[:][ \t]+["]command["]' and can
      generate an error for anything else. */
   i = isolate_sequence (line, 0, 1, &kstart);
   if (i < 0)
@@ -4578,16 +4731,26 @@ bind_keyseq_to_unix_command (line)
   /* Create the key sequence string to pass to rl_generic_bind */
   kseq = substring (line, kstart, i);
 
-  for ( ; line[i] && line[i] != ':'; i++)
+  /* Allow colon or whitespace to separate the key sequence and command string. */
+  for ( ; line[i] && line[i] != ':' && whitespace (line[i]) == 0; i++)
     ;
-  if (line[i] != ':')
+  if (line[i] != ':' && whitespace (line[i]) == 0)
     {
-      builtin_error (_("%s: missing colon separator"), line);
+      builtin_error (_("%s: missing separator"), line);
       FREE (kseq);
       return -1;
     }
 
-  i = isolate_sequence (line, i + 1, 0, &kstart);
+  /* If we have a whitespace separator we're going to call rl_macro_bind so
+     we get the readline-translated version of the value (backslash-escapes
+     handled, etc.) */
+  translate = line[i] != ':';
+
+  /* Kind of tricky. If we use whitespace as a delimiter, we can backslash-
+     quote double quotes and have them preserved in the value. However, we
+     still want to be able to auto-detect quoted strings and only require
+     them with whitespace delimiters. */
+  i = isolate_sequence (line, i + 1, translate, &kstart);
   if (i < 0)
     {
       FREE (kseq);
@@ -4599,7 +4762,10 @@ bind_keyseq_to_unix_command (line)
 
   /* Save the command to execute and the key sequence in the CMD_XMAP */
   cmd_xmap = get_cmd_xmap_from_keymap (kmap);
-  rl_generic_bind (ISMACR, kseq, value, cmd_xmap);
+  if (translate)
+    rl_macro_bind (kseq, value, cmd_xmap);
+  else
+    rl_generic_bind (ISMACR, kseq, value, cmd_xmap);
 
   /* and bind the key sequence in the current keymap to a function that
      understands how to execute from CMD_XMAP */
@@ -4610,8 +4776,7 @@ bind_keyseq_to_unix_command (line)
 }
 
 int
-unbind_unix_command (kseq)
-     char *kseq;
+unbind_unix_command (char *kseq)
 {
   Keymap cmd_xmap;
 
@@ -4628,8 +4793,7 @@ unbind_unix_command (kseq)
    but return only directories as matches.  Dequotes the filename before
    attempting to find matches. */
 char **
-bash_directory_completion_matches (text)
-     const char *text;
+bash_directory_completion_matches (const char *text)
 {
   char **m1;
   char *dfn;
@@ -4657,8 +4821,7 @@ bash_directory_completion_matches (text)
 }
 
 char *
-bash_dequote_text (text)
-     const char *text;
+bash_dequote_text (const char *text)
 {
   char *dtxt;
   int qc;
@@ -4673,7 +4836,7 @@ bash_dequote_text (text)
    and fatal signals without executing too much code in a signal handler
    context. */
 static int
-bash_event_hook ()
+bash_event_hook (void)
 {
   int sig;