]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - bashline.c
fix quoting for positional parameters if not word splitting; retry open for startup...
[thirdparty/bash.git] / bashline.c
index d56cd79db3a280d20b61fddf9e37f14e8b2bc585..353e8b1e68aab2578cbcb2db8e003e14a8aa6dd3 100644 (file)
@@ -1,6 +1,6 @@
 /* bashline.c -- Bash's interface to the readline library. */
 
-/* Copyright (C) 1987-2017 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2024 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
 #include "shmbutil.h"
 #include "trap.h"
 #include "flags.h"
+#include "timer.h"
 
 #if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
 #  include <mbstr.h>           /* mbschr */
 #endif
 
 #include "builtins/common.h"
+#include "builtins/builtext.h"         /* for read_builtin */
 
 #include <readline/rlconf.h>
 #include <readline/readline.h>
 #  define VI_EDITING_MODE       0
 #endif
 
+/* Copied from rldefs.h, since that's not a public readline header file. */
+#ifndef FUNCTION_TO_KEYMAP
+
+#if defined (CRAY)
+#  define FUNCTION_TO_KEYMAP(map, key) (Keymap)((int)map[key].function)
+#  define KEYMAP_TO_FUNCTION(data)     (rl_command_func_t *)((int)(data))
+#else
+#  define FUNCTION_TO_KEYMAP(map, key) (Keymap)(map[key].function)
+#  define KEYMAP_TO_FUNCTION(data)     (rl_command_func_t *)(data)
+#endif
+
+#endif
+
 #define RL_BOOLEAN_VARIABLE_VALUE(s)   ((s)[0] == 'o' && (s)[1] == 'n' && (s)[2] == '\0')
 
 #if defined (BRACE_COMPLETION)
-extern int bash_brace_completion __P((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 __P((const char *string, int nlines, void (*outx)(int)));
+extern int tputs (const char *string, int nlines, void (*outx)(int));
 #else
-extern int tputs __P((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 __P((int, int));
-static int display_shell_version __P((int, int));
-static int operate_and_get_next __P((int, int));
+static int shell_expand_line (int, int);
+static int display_shell_version (int, int);
 
-static int bash_ignore_filenames __P((char **));
-static int bash_ignore_everything __P((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 __P((char *));
-static int history_expand_line __P((int, int));
-static int tcsh_magic_space __P((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 __P((int, int));
+static int alias_expand_line (int, int);
 #endif
 #if defined (BANG_HISTORY) && defined (ALIAS)
-static int history_and_alias_expand_line __P((int, int));
+static int history_and_alias_expand_line (int, int);
 #endif
 
-static int bash_forward_shellword __P((int, int));
-static int bash_backward_shellword __P((int, int));
-static int bash_kill_shellword __P((int, int));
-static int bash_backward_kill_shellword __P((int, int));
-
-/* Helper functions for Readline. */
-static char *restore_tilde __P((char *, char *));
-static char *maybe_restore_tilde __P((char *, char *));
-
-static char *bash_filename_rewrite_hook __P((char *, int));
-
-static void bash_directory_expansion __P((char **));
-static int bash_filename_stat_hook __P((char **));
-static int bash_command_name_stat_hook __P((char **));
-static int bash_directory_completion_hook __P((char **));
-static int filename_completion_ignore __P((char **));
-static int bash_push_line __P((void));
-
-static int executable_completion __P((const char *, int));
-
-static rl_icppfunc_t *save_directory_hook __P((void));
-static void restore_directory_hook __P((rl_icppfunc_t));
-
-static int directory_exists __P((const char *, int));
-
-static void cleanup_expansion_error __P((void));
-static void maybe_make_readline_line __P((char *));
-static void set_up_new_line __P((char *));
+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 check_redir __P((int));
-static char **attempt_shell_completion __P((const char *, int, int));
-static char *variable_completion_function __P((const char *, int));
-static char *hostname_completion_function __P((const char *, int));
-static char *command_subst_completion_function __P((const char *, int));
+static int bash_spell_correct_shellword (int, int);
 
-static void build_history_completion_array __P((void));
-static char *history_completion_generator __P((const char *, int));
-static int dynamic_complete_history __P((int, int));
-static int bash_dabbrev_expand __P((int, int));
-
-static void initialize_hostname_list __P((void));
-static void add_host_name __P((char *));
-static void snarf_hosts_from_file __P((char *));
-static char **hostnames_matching __P((char *));
-
-static void _ignore_completion_names __P((char **, sh_ignore_func_t *));
-static int name_is_acceptable __P((const char *));
-static int test_for_directory __P((const char *));
-static int return_zero __P((const char *));
-
-static char *bash_dequote_filename __P((char *, int));
-static char *quote_word_break_chars __P((char *));
-static void set_filename_bstab __P((const char *));
-static char *bash_quote_filename __P((char *, int, char *));
+/* Helper functions for Readline. */
+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 __P((int));
+static void putx (int);
 #else
-static int putx __P((int));
+static int putx (int);
 #endif
-static int bash_execute_unix_command __P((int, int));
-static void init_unix_command_map __P((void));
-static int isolate_sequence __P((char *, int, int, int *));
+static int readline_get_char_offset (int);
+static void readline_set_char_offset (int, int *);
 
-static int set_saved_history __P((void));
+static Keymap get_cmd_xmap_from_edit_mode (void);
+static Keymap get_cmd_xmap_from_keymap (Keymap);
+
+static void init_unix_command_map (void);
+static int isolate_sequence (char *, int, int, int *);
+
+static int set_saved_history (void);
 
 #if defined (ALIAS)
-static int posix_edit_macros __P((int, int));
+static int posix_edit_macros (int, int);
 #endif
 
-static int bash_event_hook __P((void));
+static int bash_event_hook (void);
 
 #if defined (PROGRAMMABLE_COMPLETION)
-static int find_cmd_start __P((int));
-static int find_cmd_end __P((int));
-static char *find_cmd_name __P((int, int *, int *));
-static char *prog_complete_return __P((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
 
 extern int no_symbolic_links;
 extern STRING_INT_ALIST word_token_alist[];
+extern sh_timer *read_timeout;
 
 /* SPECIFIC_COMPLETION_FUNCTIONS specifies that we have individual
    completion functions which indicate what type of completion should be
@@ -212,41 +240,42 @@ extern STRING_INT_ALIST word_token_alist[];
 #define SPECIFIC_COMPLETION_FUNCTIONS
 
 #if defined (SPECIFIC_COMPLETION_FUNCTIONS)
-static int bash_specific_completion __P((int, rl_compentry_func_t *));
-
-static int bash_complete_filename_internal __P((int));
-static int bash_complete_username_internal __P((int));
-static int bash_complete_hostname_internal __P((int));
-static int bash_complete_variable_internal __P((int));
-static int bash_complete_command_internal __P((int));
-
-static int bash_complete_filename __P((int, int));
-static int bash_possible_filename_completions __P((int, int));
-static int bash_complete_username __P((int, int));
-static int bash_possible_username_completions __P((int, int));
-static int bash_complete_hostname __P((int, int));
-static int bash_possible_hostname_completions __P((int, int));
-static int bash_complete_variable __P((int, int));
-static int bash_possible_variable_completions __P((int, int));
-static int bash_complete_command __P((int, int));
-static int bash_possible_command_completions __P((int, int));
-
-static char *glob_complete_word __P((const char *, int));
-static int bash_glob_completion_internal __P((int));
-static int bash_glob_complete_word __P((int, int));
-static int bash_glob_expand_word __P((int, int));
-static int bash_glob_list_expansions __P((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 __P((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 __P((int, int));
-static int bash_vi_complete __P((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 __P((int, int));
+static int emacs_edit_and_execute_command (int, int);
 
-/* Non-zero once initalize_readline () has been called. */
+/* Non-zero once initialize_readline () has been called. */
 int bash_readline_initialized = 0;
 
 /* If non-zero, we do hostname completion, breaking words at `@' and
@@ -276,7 +305,7 @@ int dircomplete_expand_relpath = 0;
 
 /* When non-zero, perform `normal' shell quoting on completed filenames
    even when the completed name contains a directory name with a shell
-   variable referene, so dollar signs in a filename get quoted appropriately.
+   variable reference, so dollar signs in a filename get quoted appropriately.
    Set to zero to remove dollar sign (and braces or parens as needed) from
    the set of characters that will be quoted. */
 int complete_fullquote = 1;
@@ -300,30 +329,45 @@ 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 */
 #define DEFCOMP_CMDPOS         1
 
+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 */
+
   if (on_or_off)
     rl_variable_bind ("comment-begin", "#");
 #if defined (VI_MODE)
-  rl_bind_key_in_map (CTRL ('I'), on_or_off ? rl_insert : rl_complete, vi_insertion_keymap);
+  if (on_or_off)
+    {
+      vi_tab_binding = rl_function_of_keyseq (kseq, vi_insertion_keymap, (int *)NULL);
+      rl_bind_key_in_map (CTRL ('I'), rl_insert, vi_insertion_keymap);
+    }
+  else
+    {
+      if (rl_function_of_keyseq (kseq, vi_insertion_keymap, (int *)NULL) == rl_insert)
+        rl_bind_key_in_map (CTRL ('I'), vi_tab_binding, vi_insertion_keymap);
+    }
 #endif
 }
 
 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);
 }
@@ -331,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;
 
@@ -395,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;
     }
 
@@ -404,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];
@@ -431,6 +475,10 @@ initialize_readline ()
   rl_add_defun ("shell-backward-word", bash_backward_shellword, -1);
   rl_add_defun ("shell-kill-word", bash_kill_shellword, -1);
   rl_add_defun ("shell-backward-kill-word", bash_backward_kill_shellword, -1);
+  rl_add_defun ("shell-transpose-words", bash_transpose_shellwords, -1);
+
+  rl_add_defun ("spell-correct-word", bash_spell_correct_shellword, -1);
+  rl_bind_key_if_unbound_in_map ('s', bash_spell_correct_shellword, emacs_ctlx_keymap);
 
 #ifdef ALIAS
   rl_add_defun ("alias-expand-line", alias_expand_line, -1);
@@ -442,9 +490,11 @@ initialize_readline ()
   /* Backwards compatibility. */
   rl_add_defun ("insert-last-argument", rl_yank_last_arg, -1);
 
-  rl_add_defun ("operate-and-get-next", operate_and_get_next, -1);
   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);
@@ -480,7 +530,6 @@ initialize_readline ()
   rl_bind_key_if_unbound_in_map ('^', history_expand_line, emacs_meta_keymap);
 #endif
 
-  rl_bind_key_if_unbound_in_map (CTRL ('O'), operate_and_get_next, emacs_standard_keymap);
   rl_bind_key_if_unbound_in_map (CTRL ('V'), display_shell_version, emacs_ctlx_keymap);
 
   /* In Bash, the user can switch editing modes with "set -o [vi emacs]",
@@ -496,7 +545,10 @@ initialize_readline ()
   if (func == rl_vi_editing_mode)
     rl_unbind_key_in_map (CTRL('M'), emacs_meta_keymap);
 #if defined (VI_MODE)
-  rl_unbind_key_in_map (CTRL('E'), vi_movement_keymap);
+  kseq[0] = CTRL('E');
+  func = rl_function_of_keyseq (kseq, vi_movement_keymap, (int *)NULL);
+  if (func == rl_emacs_editing_mode)
+    rl_unbind_key_in_map (CTRL('E'), vi_movement_keymap);
 #endif
 
 #if defined (BRACE_COMPLETION)
@@ -541,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;
@@ -560,6 +606,7 @@ initialize_readline ()
   rl_bind_key_if_unbound_in_map ('@', posix_edit_macros, vi_movement_keymap);
 #  endif
 
+  rl_add_defun ("bash-vi-complete", bash_vi_complete, -1);
   rl_bind_key_in_map ('\\', bash_vi_complete, vi_movement_keymap);
   rl_bind_key_in_map ('*', bash_vi_complete, vi_movement_keymap);
   rl_bind_key_in_map ('=', bash_vi_complete, vi_movement_keymap);
@@ -580,6 +627,13 @@ initialize_readline ()
   rl_filename_dequoting_function = bash_dequote_filename;
   rl_char_is_quoted_p = char_is_quoted;
 
+  /* Add some default bindings for the "shellwords" functions, roughly
+     parallelling the default word bindings in emacs mode. */
+  rl_bind_key_if_unbound_in_map (CTRL('B'), bash_backward_shellword, emacs_meta_keymap);
+  rl_bind_key_if_unbound_in_map (CTRL('D'), bash_kill_shellword, emacs_meta_keymap);
+  rl_bind_key_if_unbound_in_map (CTRL('F'), bash_forward_shellword, emacs_meta_keymap);
+  rl_bind_key_if_unbound_in_map (CTRL('T'), bash_transpose_shellwords, emacs_meta_keymap);
+
 #if 0
   /* This is superfluous and makes it impossible to use tab completion in
      vi mode even when explicitly binding it in ~/.inputrc.  sv_strict_posix()
@@ -593,19 +647,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;
 }
@@ -616,17 +670,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 ();
 
@@ -639,7 +696,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)
     {
@@ -654,8 +711,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);
 
@@ -667,8 +723,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);
@@ -694,17 +749,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;
 
@@ -722,8 +777,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)
     {
@@ -738,14 +792,17 @@ 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];
   register int i, start;
 
+#ifdef __MSYS__
+  file = fopen (filename, "rt");
+#else
   file = fopen (filename, "r");
+#endif
   if (file == 0)
     return;
 
@@ -805,7 +862,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 ();
@@ -813,7 +870,7 @@ get_hostname_list ()
 }
 
 void
-clear_hostname_list ()
+clear_hostname_list (void)
 {
   register int i;
 
@@ -828,11 +885,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 ();
@@ -860,7 +917,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);
@@ -873,56 +930,6 @@ hostnames_matching (text)
   return (result);
 }
 
-/* The equivalent of the Korn shell C-o operate-and-get-next-history-line
-   editing command. */
-static int saved_history_line_to_use = -1;
-static int last_saved_history_line = -1;
-
-#define HISTORY_FULL() (history_is_stifled () && history_length >= history_max_entries)
-
-static int
-set_saved_history ()
-{
-  /* XXX - compensate for assumption that history was `shuffled' if it was
-     actually not. */
-  if (HISTORY_FULL () &&
-      hist_last_line_added == 0 &&
-      saved_history_line_to_use < history_length - 1)
-    saved_history_line_to_use++;
-
-  if (saved_history_line_to_use >= 0)
-    {
-     rl_get_previous_history (history_length - saved_history_line_to_use, 0);
-     last_saved_history_line = saved_history_line_to_use;
-    }
-  saved_history_line_to_use = -1;
-  rl_startup_hook = old_rl_startup_hook;
-  return (0);
-}
-
-static int
-operate_and_get_next (count, c)
-     int count, c;
-{
-  int where;
-
-  /* Accept the current line. */
-  rl_newline (1, c);
-
-  /* Find the current line, and find the next line to use. */
-  where = rl_explicit_arg ? count : where_history ();
-
-  if (HISTORY_FULL () || (where >= history_length - 1) || rl_explicit_arg)
-    saved_history_line_to_use = where;
-  else
-    saved_history_line_to_use = where + 1;
-
-  old_rl_startup_hook = rl_startup_hook;
-  rl_startup_hook = set_saved_history;
-
-  return 0;
-}
-
 /* This vi mode command causes VI_EDIT_COMMAND to be run on the current
    command being entered (if no explicit argument is given), otherwise on
    a command from the history file. */
@@ -932,9 +939,7 @@ operate_and_get_next (count, c)
 #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;
@@ -948,8 +953,11 @@ edit_and_execute_command (count, c, editing_mode, edit_command)
 
   if (rl_explicit_arg)
     {
-      command = (char *)xmalloc (strlen (edit_command) + 8);
-      sprintf (command, "%s %d", edit_command, count);
+      size_t clen;
+      /* 32 exceeds strlen (itos (INTMAX_MAX)) (19) */
+      clen = strlen (edit_command) + 32;
+      command = (char *)xmalloc (clen);
+      snprintf (command, clen, "%s %d", edit_command, count);
     }
   else
     {
@@ -960,11 +968,8 @@ edit_and_execute_command (count, c, editing_mode, edit_command)
       /* This breaks down when using command-oriented history and are not
         finished with the command, so we should not ignore the last command */
       using_history ();
-      if (rl_line_buffer[0])
-       {
-         current_command_line_count++; /* for rl_newline above */
-         bash_add_history (rl_line_buffer);
-       }
+      current_command_line_count++;    /* for rl_newline above */
+      bash_add_history (rl_line_buffer);
       current_command_line_count = 0;  /* for dummy history entry */
       bash_add_history ("");
       history_lines_this_session++;
@@ -977,11 +982,17 @@ edit_and_execute_command (count, c, editing_mode, edit_command)
   
   if (rl_deprep_term_function)
     (*rl_deprep_term_function) ();
+  rl_clear_signals ();
   save_parser_state (&ps);
   r = parse_and_execute (command, (editing_mode == VI_EDITING_MODE) ? "v" : "C-xC-e", SEVAL_NOHIST);
   restore_parser_state (&ps);
+
+  /* if some kind of reset_parser was called, undo it. */
+  reset_readahead_token ();
+
   if (rl_prep_term_function)
     (*rl_prep_term_function) (metaflag);
+  rl_set_signals ();
 
   current_command_line_count = saved_command_line_count;
 
@@ -1005,32 +1016,31 @@ 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;
 
   c = rl_read_key ();
+  if (c <= 0)
+    return 0;
   alias_name[0] = '_';
   alias_name[1] = c;
   alias_name[2] = '\0';
@@ -1051,8 +1061,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;
@@ -1160,11 +1169,10 @@ 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;
+  int c, p, prev_p;
   DECLARE_MBSTATE;
 
   if (count < 0)
@@ -1173,9 +1181,6 @@ bash_backward_shellword (count, key)
   p = rl_point;
   slen = rl_end;
 
-  if (p == rl_end && p > 0)
-    p--;  
-
   while (count)
     {
       if (p == 0)
@@ -1184,7 +1189,10 @@ bash_backward_shellword (count, key)
          return 0;
        }
 
-      /* Move backward until we hit a non-metacharacter. */
+      /* Move backward until we hit a non-metacharacter. We want to deal
+        with the characters before point, so we move off a word if we're
+        at its first character. */
+      BACKUP_CHAR (rl_line_buffer, slen, p);
       while (p > 0)
        {
          c = rl_line_buffer[p];
@@ -1199,12 +1207,18 @@ bash_backward_shellword (count, key)
          return 0;
        }
 
-      /* Now move backward until we hit a metacharacter or BOL. */
+      /* Now move backward until we hit a metacharacter or BOL. Leave point
+        at the start of the shellword or at BOL. */
+      prev_p = p;
       while (p > 0)
        {
          c = rl_line_buffer[p];
          if (WORDDELIM (c) && char_is_quoted (rl_line_buffer, p) == 0)
-           break;
+           {
+             p = prev_p;
+             break;
+           }
+         prev_p = p;
          BACKUP_CHAR (rl_line_buffer, slen, p);
        }
 
@@ -1216,8 +1230,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;
 
@@ -1231,15 +1244,14 @@ bash_kill_shellword (count, key)
     rl_kill_text (p, rl_point);
 
   rl_point = p;
-  if (rl_editing_mode == 1)    /* 1 == emacs_mode */
+  if (rl_editing_mode == EMACS_EDITING_MODE)   /* 1 == emacs_mode */
     rl_mark = rl_point;
 
   return 0;
 }
 
 static int
-bash_backward_kill_shellword (count, key)
-     int count, key;
+bash_backward_kill_shellword (int count, int key)
 {
   int p;
 
@@ -1252,12 +1264,118 @@ bash_backward_kill_shellword (count, key)
   if (rl_point != p)
     rl_kill_text (p, rl_point);
 
-  if (rl_editing_mode == 1)    /* 1 == emacs_mode */
+  if (rl_editing_mode == EMACS_EDITING_MODE)   /* 1 == emacs_mode */
     rl_mark = rl_point;
 
   return 0;
 }
 
+static int
+bash_transpose_shellwords (int count, int key)
+{
+  char *word1, *word2;
+  int w1_beg, w1_end, w2_beg, w2_end;
+  int orig_point = rl_point;
+
+  if (count == 0)
+    return 0;
+
+  /* Find the two shell words. */
+  bash_forward_shellword (count, key);
+  w2_end = rl_point;
+  bash_backward_shellword (1, key);
+  w2_beg = rl_point;
+  bash_backward_shellword (count, key);
+  w1_beg = rl_point;
+  bash_forward_shellword (1, key);
+  w1_end = rl_point;
+
+  /* check that there really are two words. */
+  if ((w1_beg == w2_beg) || (w2_beg < w1_end))
+    {
+      rl_ding ();
+      rl_point = orig_point;
+      return 1;
+    }
+
+  /* Get the text of the words. */
+  word1 = rl_copy_text (w1_beg, w1_end);
+  word2 = rl_copy_text (w2_beg, w2_end);
+
+  /* We are about to do many insertions and deletions.  Remember them
+     as one operation. */
+  rl_begin_undo_group ();
+
+  /* Do the stuff at word2 first, so that we don't have to worry
+     about word1 moving. */
+  rl_point = w2_beg;
+  rl_delete_text (w2_beg, w2_end);
+  rl_insert_text (word1);
+
+  rl_point = w1_beg;
+  rl_delete_text (w1_beg, w1_end);
+  rl_insert_text (word2);
+
+  /* This is exactly correct since the text before this point has not
+     changed in length. */
+  rl_point = w2_end;
+
+  /* I think that does it. */
+  rl_end_undo_group ();
+  xfree (word1);
+  xfree (word2);
+
+  return 0;
+}
+
+/* Directory name spelling correction on the current word (not shellword).
+   COUNT > 1 is not exactly correct yet. */
+static int
+bash_spell_correct_shellword (int count, int key)
+{
+  int wbeg, wend;
+  char *text, *newdir;
+
+  while (count)
+    {
+      bash_backward_shellword (1, key);
+      wbeg = rl_point;
+      bash_forward_shellword (1, key);
+      wend = rl_point;
+
+      if (wbeg > wend)
+       break;
+
+      text = rl_copy_text (wbeg, wend);
+      if (text == 0 || *text == 0)
+       break;
+
+      newdir = dirspell (text);
+      if (newdir)
+       {
+         rl_begin_undo_group ();
+         rl_delete_text (wbeg, wend);
+         rl_point = wbeg;
+         if (*newdir)
+           rl_insert_text (newdir);
+         rl_mark = wbeg;
+         rl_end_undo_group ();
+       }
+
+      free (text);
+      free (newdir);
+
+      if (rl_point >= rl_end)
+       break;
+
+      count--;
+
+      if (count)
+       bash_forward_shellword (1, key);                /* XXX */
+    }
+
+  return 0;
+}
 
 /* **************************************************************** */
 /*                                                                 */
@@ -1272,21 +1390,21 @@ bash_backward_kill_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;
+  int this_char, prev_char, next_char;
 
   /* Handle the two character tokens `>&', `<&', and `>|'.
      We are not in a command position after one of these. */
   this_char = rl_line_buffer[ti];
   prev_char = (ti > 0) ? rl_line_buffer[ti - 1] : 0;
+  next_char = (ti < rl_end) ? rl_line_buffer[ti + 1] : 0;
 
   if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) ||
       (this_char == '|' && prev_char == '>'))
     return (1);
-  else if (this_char == '{' && prev_char == '$') /*}*/
+  else if (this_char == '{' && prev_char == '$' && FUNSUB_CHAR (next_char) == 0) /*}*/
     return (1);
 #if 0  /* Not yet */
   else if (this_char == '(' && prev_char == '$') /*)*/
@@ -1310,8 +1428,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;
 
@@ -1323,7 +1440,7 @@ find_cmd_start (start)
         rl_line_buffer[s])
     {
       /* Handle >| token crudely; treat as > not | */
-      if (rl_line_buffer[s] == '|' && rl_line_buffer[s-1] == '>')
+      if (s > 0 && rl_line_buffer[s] == '|' && rl_line_buffer[s-1] == '>')
        {
          ns = skip_to_delim (rl_line_buffer, s+1, COMMAND_SEPARATORS, SD_NOJMP|SD_COMPLETE/*|SD_NOSKIPCMD*/);
          if (ns > start || rl_line_buffer[ns] == 0)
@@ -1331,14 +1448,35 @@ find_cmd_start (start)
          os = ns+1;
          continue;
        }
+      /* The only reserved word in COMMAND_SEPARATORS is `{', so handle that
+        specially, making sure it's in a spot acceptable for reserved words */
+      if (s >= os && rl_line_buffer[s] == '{')
+       {
+         int pc, nc;   /* index of previous non-whitespace, next char */
+         for (pc = (s > os) ? s - 1 : os; pc > os && whitespace(rl_line_buffer[pc]); pc--)
+           ;
+         nc = rl_line_buffer[s+1];
+         /* must be preceded by a command separator or be the first non-
+            whitespace character since the last command separator, and
+            followed by a shell break character (not another `{') to be a reserved word. */
+         if ((pc > os && (rl_line_buffer[s-1] == '{' || strchr (COMMAND_SEPARATORS, rl_line_buffer[pc]) == 0)) ||
+             (shellbreak(nc) == 0))    /* }} */
+           {
+             /* Not a reserved word, look for another delim */
+             ns = skip_to_delim (rl_line_buffer, s+1, COMMAND_SEPARATORS, SD_NOJMP|SD_COMPLETE/*|SD_NOSKIPCMD*/);
+             if (ns > start || rl_line_buffer[ns] == 0)
+               return os;
+             os = ns+1;
+             continue;
+           }
+       }
       os = s+1;
     }
   return os;
 }
 
 static int
-find_cmd_end (end)
-     int end;
+find_cmd_end (int end)
 {
   register int e;
 
@@ -1347,9 +1485,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;
@@ -1371,9 +1507,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;
 
@@ -1390,9 +1524,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;
 
@@ -1418,9 +1550,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;
@@ -1433,10 +1563,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 */
 
@@ -1477,7 +1607,7 @@ attempt_shell_completion (text, start, end)
       in_command_position++;
 
       if (check_redir (ti) == 1)
-       in_command_position = 0;
+       in_command_position = -1;       /* sentinel that we're not the first word on the line */
     }
   else
     {
@@ -1486,7 +1616,7 @@ attempt_shell_completion (text, start, end)
         assignments. */
     }
 
-  if (in_command_position && invalid_completion (text, ti))
+  if (in_command_position > 0 && invalid_completion (text, ti))
     {
       rl_attempted_completion_over = 1;
       return ((char **)NULL);
@@ -1494,9 +1624,9 @@ attempt_shell_completion (text, start, end)
 
   /* Check that we haven't incorrectly flagged a closed command substitution
      as indicating we're in a command position. */
-  if (in_command_position && ti >= 0 && rl_line_buffer[ti] == '`' &&
+  if (in_command_position > 0 && ti >= 0 && rl_line_buffer[ti] == '`' &&
        *text != '`' && unclosed_pair (rl_line_buffer, end, "`") == 0)
-    in_command_position = 0;
+    in_command_position = -1;  /* not following a command separator */
 
   /* Special handling for command substitution.  If *TEXT is a backquote,
      it can be the start or end of an old-style command substitution, or
@@ -1504,8 +1634,8 @@ attempt_shell_completion (text, start, end)
      succeed.  Don't bother if readline found a single quote and we are
      completing on the substring.  */
   if (*text == '`' && rl_completion_quote_character != '\'' &&
-       (in_command_position || (unclosed_pair (rl_line_buffer, start, "`") &&
-                                unclosed_pair (rl_line_buffer, end, "`"))))
+       (in_command_position > 0 || (unclosed_pair (rl_line_buffer, start, "`") &&
+                                    unclosed_pair (rl_line_buffer, end, "`"))))
     matches = rl_completion_matches (text, command_subst_completion_function);
 
 #if defined (PROGRAMMABLE_COMPLETION)
@@ -1513,7 +1643,7 @@ attempt_shell_completion (text, start, end)
   have_progcomps = prog_completion_enabled && (progcomp_size () > 0);
   iw_compspec = progcomp_search (INITIALWORD);
   if (matches == 0 &&
-      (in_command_position == 0 || text[0] == '\0' || (in_command_position && iw_compspec)) &&
+      (in_command_position == 0 || text[0] == '\0' || (in_command_position > 0 && iw_compspec)) &&
       current_prompt_string == ps1_prompt)
     {
       int s, e, s1, e1, os, foundcs;
@@ -1543,6 +1673,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
@@ -1562,10 +1696,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]))
@@ -1583,7 +1719,8 @@ attempt_shell_completion (text, start, end)
          /* command completion if programmable completion fails */
          /* If we have a completion for the initial word, we can prefer that */
          in_command_position = s == start && (iw_compspec || STREQ (n, text)); /* XXX */
-         foundcs = foundcs && (iw_compspec == 0);
+         if (iw_compspec && in_command_position)
+           foundcs = 0;
        }
       /* empty command name following command separator */
       else if (s >= e && n[0] == '\0' && text[0] == '\0' && start > 0 &&
@@ -1608,7 +1745,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
@@ -1634,7 +1778,7 @@ attempt_shell_completion (text, start, end)
   if (matches == 0)
     {
       dflags = 0;
-      if (in_command_position)
+      if (in_command_position > 0)
        dflags |= DEFCOMP_CMDPOS;
       matches = bash_default_completion (text, start, end, qc, dflags);
     }
@@ -1643,9 +1787,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;
 
@@ -1656,6 +1798,8 @@ bash_default_completion (text, start, end, qc, compflags)
     {
       if (qc != '\'' && text[1] == '(') /* ) */
        matches = rl_completion_matches (text, command_subst_completion_function);
+      else if (qc != '\'' && text[1] == '{' && FUNSUB_CHAR (text[2])) /* } */
+       matches = rl_completion_matches (text, command_subst_completion_function);
       else
        {
          matches = rl_completion_matches (text, variable_completion_function);
@@ -1722,7 +1866,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
@@ -1740,7 +1889,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 && glob_pattern_p (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.
@@ -1761,8 +1910,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;
 
@@ -1777,6 +1925,7 @@ bash_command_name_stat_hook (name)
   result = search_for_command (cname, 0);
   if (result)
     {
+      FREE (*name);
       *name = result;
       return 1;
     }
@@ -1784,14 +1933,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);
@@ -1804,9 +1954,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;
@@ -1816,7 +1964,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;
@@ -1835,6 +1984,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);
@@ -1843,13 +1993,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 = glob_pattern_p (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
@@ -1927,7 +2079,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. */
@@ -2028,8 +2180,10 @@ globword:
     {
       if (state == 0)
        {
+         rl_filename_completion_desired = 1;
+
          glob_ignore_case = igncase;
-         glob_matches = shell_glob_filename (hint);
+         glob_matches = shell_glob_filename (hint, 0);
          glob_ignore_case = old_glob_ignore_case;
 
          if (GLOB_FAILED (glob_matches) || glob_matches == 0)
@@ -2041,7 +2195,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++])
@@ -2113,6 +2271,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
@@ -2148,10 +2307,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. */
@@ -2202,6 +2367,7 @@ globword:
          t1 = make_absolute (val, t);
          free (t);
          cval = sh_canonpath (t1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
+         free (t1);
        }
       else
 #endif
@@ -2229,9 +2395,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;
@@ -2248,13 +2412,18 @@ command_subst_completion_function (text, state)
        text++;
       else if (*text == '$' && text[1] == '(') /* ) */
        text += 2;
+      else if (*text == '$' && text[1] == '{' && FUNSUB_CHAR (text[2])) /*}*/
+       text += 3;      /* nofork command substitution */
       /* If the text was quoted, suppress any quote character that the
         readline completion code would insert. */
       rl_completion_suppress_quote = 1;
       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
@@ -2312,9 +2481,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;
@@ -2372,9 +2539,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;
@@ -2415,16 +2580,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;
@@ -2473,16 +2636,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)
@@ -2518,8 +2679,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;
@@ -2536,7 +2696,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)
@@ -2561,8 +2721,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)
     {
@@ -2576,15 +2735,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. */
@@ -2592,21 +2763,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;
 
@@ -2628,8 +2815,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;
 
@@ -2650,8 +2836,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;
 
@@ -2673,8 +2858,7 @@ 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;
 
@@ -2711,10 +2895,9 @@ 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;
+  char *new_line, *t;
   WORD_LIST *expanded_string;
   WORD_DESC *w;
 
@@ -2723,6 +2906,10 @@ shell_expand_line (count, ignore)
   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)
     {
@@ -2809,12 +2996,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;
 
@@ -2900,15 +3085,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);
     }
 
@@ -2917,16 +3101,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)
@@ -2945,13 +3127,39 @@ 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;
 
   fn = bash_tilde_expand (name, 0);
+
+#if __CYGWIN
+  /* stat("//server") can only be successful as a directory, but can take
+     seconds to time out on failure.  It is much faster to assume that
+     "//server" is a valid name than it is to wait for a stat, even if it
+     gives false positives on bad names.  */
+  if (fn[0] == '/' && fn[1] == '/' && ! strchr (&fn[2], '/'))
+    {
+      free (fn);
+      return 1;
+    }
+#endif
+
+  r = file_isdir (fn);
+  free (fn);
+
+  return (r);
+}
+
+static int
+test_for_canon_directory (const char *name)
+{
+  char *fn;
+  int r;
+
+  fn = (*name == '~') ? bash_tilde_expand (name, 0) : savestring (name);
+  bash_filename_stat_hook (&fn);
   r = file_isdir (fn);
   free (fn);
 
@@ -2960,23 +3168,27 @@ test_for_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
-return_zero (name)
-     const char *name;
+bash_progcomp_ignore_filenames (char **names)
+{
+  _ignore_completion_names (names, test_for_canon_directory);
+  return 0;
+}
+
+static int
+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;
@@ -2986,10 +3198,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);
@@ -3043,8 +3254,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;
@@ -3060,8 +3270,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;
 
@@ -3084,13 +3293,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;
 
@@ -3103,7 +3312,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)
     {
@@ -3118,7 +3327,7 @@ set_directory_hook ()
 }
 
 static rl_icppfunc_t *
-save_directory_hook ()
+save_directory_hook (void)
 {
   rl_icppfunc_t *ret;
 
@@ -3137,8 +3346,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;
@@ -3146,12 +3354,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;
@@ -3171,12 +3391,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;
@@ -3255,8 +3505,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;
@@ -3265,38 +3514,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;
@@ -3315,24 +3533,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
        {
@@ -3366,7 +3568,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] != '.')
@@ -3387,7 +3589,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);
@@ -3431,11 +3642,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;
@@ -3481,11 +3692,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
@@ -3510,8 +3720,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;
@@ -3540,8 +3749,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;
@@ -3580,49 +3788,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;
@@ -3650,88 +3852,85 @@ 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 (const char *string)
+{
+  return (glob_pattern_p (string) == 1);
+}
+
 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);
 
@@ -3750,7 +3949,7 @@ glob_complete_word (text, state)
       if (ttext != text)
        free (ttext);
 
-      matches = shell_glob_filename (globtext);
+      matches = shell_glob_filename (globtext, 0);
       if (GLOB_FAILED (matches))
        matches = (char **)NULL;
       ind = 0;
@@ -3762,8 +3961,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);
 }
@@ -3771,10 +3969,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));
@@ -3783,8 +3978,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;
@@ -3801,23 +3995,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;
@@ -3843,13 +4033,40 @@ bash_specific_completion (what_to_do, generator)
 #endif /* SPECIFIC_COMPLETION_FUNCTIONS */
 
 #if defined (VI_MODE)
+/* This does pretty much what _rl_vi_advance_point does. */
+static inline int
+vi_advance_point (void)
+{
+  int point;
+  DECLARE_MBSTATE;
+
+  point = rl_point;
+  if (rl_point < rl_end)
+#if defined (HANDLE_MULTIBYTE)
+    {
+      if (locale_mb_cur_max == 1)
+       rl_point++;
+      else
+       {
+         point = rl_point;
+         ADVANCE_CHAR (rl_line_buffer, rl_end, rl_point);
+         if (point == rl_point || rl_point > rl_end)
+           rl_point = rl_end;
+       }
+    }
+#else
+    rl_point++:
+#endif
+  return point;
+}
+
 /* Completion, from vi mode's point of view.  This is a modified version of
    rl_vi_complete which uses the bash globbing code to implement what POSIX
-   specifies, which is to append a `*' and attempt filename generation (which
-   has the side effect of expanding any globbing characters in the word). */
+   specifies, which is to optinally 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;
@@ -3859,7 +4076,7 @@ bash_vi_complete (count, key)
     {
       if (!whitespace (rl_line_buffer[rl_point + 1]))
        rl_vi_end_word (1, 'E');
-      rl_point++;
+      vi_advance_point ();
     }
 
   /* Find boundaries of current word, according to vi definition of a
@@ -3876,7 +4093,7 @@ bash_vi_complete (count, key)
       t = substring (rl_line_buffer, p, rl_point);
     }      
 
-  if (t && glob_pattern_p (t) == 0)
+  if (t && completion_glob_pattern (t) == 0)
     rl_explicit_arg = 1;       /* XXX - force glob_complete_word to append `*' */
   FREE (t);
 
@@ -3904,26 +4121,23 @@ 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);
   for (quoted = quote_char, p = text, r = ret; p && *p; p++)
     {
-      /* Allow backslash-escaped characters to pass through unscathed. */
-      if (*p == '\\')
+      /* Allow backslash-escaped characters to pass through unscathed. Backslashes
+        aren't special in single quotes. */
+      if (quoted != '\'' && *p == '\\')
        {
-         /* Backslashes are preserved within single quotes. */
-         if (quoted == '\'')
-           *r++ = *p;
          /* Backslashes are preserved within double quotes unless the
             character is one that is defined to be escaped */
-         else if (quoted == '"' && ((sh_syntaxtab[(unsigned char)p[1]] & CBSDQUOTE) == 0))
+         if (quoted == '"' && ((sh_syntaxtab[(unsigned char)p[1]] & CBSDQUOTE) == 0))
            *r++ = *p;
 
          *r++ = *++p;
@@ -3953,11 +4167,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);
@@ -3985,18 +4198,103 @@ 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;
 
   memset (filename_bstab, 0, sizeof (filename_bstab));
   for (s = string; s && *s; s++)
-    filename_bstab[*s] = 1;
+    filename_bstab[(unsigned char)*s] = 1;
 }
 
 /* Quote a filename using double quotes, single quotes, or backslashes
@@ -4006,13 +4304,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;
 
@@ -4030,10 +4326,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)
@@ -4057,6 +4380,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;
@@ -4073,7 +4399,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);
@@ -4101,16 +4428,21 @@ bash_quote_filename (s, rtype, qcp)
   return ret;
 }
 
-/* Support for binding readline key sequences to Unix commands. */
-static Keymap cmd_xmap;
+/* Support for binding readline key sequences to Unix commands. Each editing
+   mode has a separate Unix command keymap. */
+
+static Keymap emacs_std_cmd_xmap;
+#if defined (VI_MODE)
+static Keymap vi_insert_cmd_xmap;
+static Keymap vi_movement_cmd_xmap;
+#endif
 
 #ifdef _MINIX
 static void
 #else
 static int
 #endif
-putx(c)
-     int c;
+putx(int c)
 {
   int x;
   x = putc (c, rl_outstream);
@@ -4118,25 +4450,111 @@ putx(c)
   return x;
 #endif
 }
-  
+
 static int
-bash_execute_unix_command (count, key)
-     int count;        /* ignored */
-     int key;
+readline_get_char_offset (int ind)
+{
+  int r, old_ch;
+
+  r = ind;
+#if defined (HANDLE_MULTIBYTE)
+  if (locale_mb_cur_max > 1)
+    {
+      old_ch = rl_line_buffer[ind];
+      rl_line_buffer[ind] = '\0';
+      r = MB_STRLEN (rl_line_buffer);
+      rl_line_buffer[ind] = old_ch;
+    }
+#endif
+  return r;
+}
+
+static void
+readline_set_char_offset (int ind, int *varp)
+{
+  int i;
+
+  i = ind;
+
+#if defined (HANDLE_MULTIBYTE)
+  if (i > 0 && locale_mb_cur_max > 1)
+    i = _rl_find_next_mbchar (rl_line_buffer, 0, i, 0);                /* XXX */
+#endif
+  if (i != *varp)
+    {
+      if (i > rl_end)
+       i = rl_end;
+      else if (i < 0)
+       i = 0;
+      *varp = i;
+    }
+}
+
+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 (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 = (char *)rl_function_of_keyseq_len (rl_executing_keyseq, rl_key_sequence_length, cmd_xmap, &type);
-    
+  cmd_xmap = get_cmd_xmap_from_keymap (rl_get_keymap ());
+  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;
+
   if (cmd == 0 || type != ISMACR)
     {
       rl_crlf ();
@@ -4148,12 +4566,7 @@ bash_execute_unix_command (count, key)
   ce = rl_get_termcap ("ce");
   if (ce)      /* clear current line */
     {
-#if 0
-      fprintf (rl_outstream, "\r");
-      tputs (ce, 1, putx);
-#else
       rl_clear_visible_line ();
-#endif
       fflush (rl_outstream);
     }
   else
@@ -4162,50 +4575,51 @@ bash_execute_unix_command (count, key)
   v = bind_variable ("READLINE_LINE", rl_line_buffer, 0);
   if (v)
     VSETATTR (v, att_exported);
-  i = rl_point;
-#if defined (HANDLE_MULTIBYTE)
-  if (MB_CUR_MAX > 1)
-    {
-      old_ch = rl_line_buffer[rl_point];
-      rl_line_buffer[rl_point] = '\0';
-      i = MB_STRLEN (rl_line_buffer);
-      rl_line_buffer[rl_point] = old_ch;
-    }
-#endif
+
+  i = readline_get_char_offset (rl_point);
   value = inttostr (i, ibuf, sizeof (ibuf));
   v = bind_int_variable ("READLINE_POINT", value, 0);
   if (v)
     VSETATTR (v, att_exported);
+
+  i = readline_get_char_offset (rl_mark);
+  value = inttostr (i, ibuf, sizeof (ibuf));
+  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);
-  r = parse_and_execute (savestring (cmd), "bash_execute_unix_command", SEVAL_NOHIST|SEVAL_NOFREE);
+  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);
 
   v = find_variable ("READLINE_LINE");
   maybe_make_readline_line (v ? value_cell (v) : 0);
 
   v = find_variable ("READLINE_POINT");
-  if (v && legal_number (value_cell (v), &mi))
-    {
-      i = mi;
-#if defined (HANDLE_MULTIBYTE)
-      if (i > 0 && MB_CUR_MAX > 1)
-       i = _rl_find_next_mbchar (rl_line_buffer, 0, i, 0);
-#endif
-      if (i != rl_point)
-       {
-         rl_point = i;
-         if (rl_point > rl_end)
-           rl_point = rl_end;
-         else if (rl_point < 0)
-           rl_point = 0;
-       }
-    }      
+  if (v && valid_number (value_cell (v), &mi))
+    readline_set_char_offset (mi, &rl_point);
 
-  check_unbind_variable ("READLINE_LINE");
-  check_unbind_variable ("READLINE_POINT");
-  array_needs_making = 1;
+  v = find_variable ("READLINE_MARK");
+  if (v && valid_number (value_cell (v), &mi))
+    readline_set_char_offset (mi, &rl_mark);
+
+  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
@@ -4219,28 +4633,92 @@ 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;
+  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 ();
+
+  emacs_std_cmd_xmap[CTRL('X')].type = ISKMAP;
+  emacs_std_cmd_xmap[CTRL('X')].function = KEYMAP_TO_FUNCTION (rl_make_bare_keymap ());
+  emacs_std_cmd_xmap[ESC].type = ISKMAP;
+  emacs_std_cmd_xmap[ESC].function = KEYMAP_TO_FUNCTION (rl_make_bare_keymap ());
+
+#if defined (VI_MODE)  
+  vi_insert_cmd_xmap = rl_make_bare_keymap ();
+  vi_movement_cmd_xmap = rl_make_bare_keymap ();
+#endif
+}
+
+static Keymap
+get_cmd_xmap_from_edit_mode (void)
 {
-  cmd_xmap = rl_make_bare_keymap ();
+  if (emacs_std_cmd_xmap == 0)
+    init_unix_command_map ();
+
+  switch (rl_editing_mode)
+    {
+    case EMACS_EDITING_MODE:
+      return emacs_std_cmd_xmap;
+#if defined (VI_MODE)
+    case VI_EDITING_MODE:
+      return (get_cmd_xmap_from_keymap (rl_get_keymap ()));
+#endif
+    default:
+      return (Keymap)NULL;
+    }
+}
+
+static Keymap
+get_cmd_xmap_from_keymap (Keymap kmap)
+{
+  if (emacs_std_cmd_xmap == 0)
+    init_unix_command_map ();
+
+  if (kmap == emacs_standard_keymap)
+    return emacs_std_cmd_xmap;
+  else if (kmap == emacs_meta_keymap)
+    return (FUNCTION_TO_KEYMAP (emacs_std_cmd_xmap, ESC));
+  else if (kmap == emacs_ctlx_keymap)
+    return (FUNCTION_TO_KEYMAP (emacs_std_cmd_xmap, CTRL('X')));
+#if defined (VI_MODE)
+  else if (kmap == vi_insertion_keymap)
+    return vi_insert_cmd_xmap;
+  else if (kmap == vi_movement_keymap)
+    return vi_movement_cmd_xmap;
+#endif
+  else
+    return (Keymap)NULL;
 }
 
 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;
@@ -4287,20 +4765,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;
+  Keymap kmap, cmd_xmap;
   char *kseq, *value;
-  int i, kstart;
-
-  if (cmd_xmap == 0)
-    init_unix_command_map ();
+  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)
@@ -4309,16 +4783,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);
@@ -4329,7 +4813,11 @@ bind_keyseq_to_unix_command (line)
   value = substring (line, kstart, i);
 
   /* Save the command to execute and the key sequence in the CMD_XMAP */
-  rl_generic_bind (ISMACR, kseq, value, cmd_xmap);
+  cmd_xmap = get_cmd_xmap_from_keymap (kmap);
+  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 */
@@ -4339,12 +4827,25 @@ bind_keyseq_to_unix_command (line)
   return 0;
 }
 
+int
+unbind_unix_command (char *kseq)
+{
+  Keymap cmd_xmap;
+
+  cmd_xmap = get_cmd_xmap_from_keymap (rl_get_keymap ());
+  if (rl_bind_keyseq_in_map (kseq, (rl_command_func_t *)NULL, cmd_xmap) != 0)
+    {
+      builtin_error (_("`%s': cannot unbind in command keymap"), kseq);
+      return 0;
+    }
+  return 1;
+}
+
 /* Used by the programmable completion code.  Complete TEXT as a filename,
    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;
@@ -4367,13 +4868,12 @@ bash_directory_completion_matches (text)
   /* We don't bother recomputing the lcd of the matches, because it will just
      get thrown away by the programmable completion code and recomputed
      later. */
-  (void)bash_ignore_filenames (m1);
+  (void)bash_progcomp_ignore_filenames (m1);
   return m1;
 }
 
 char *
-bash_dequote_text (text)
-     const char *text;
+bash_dequote_text (const char *text)
 {
   char *dtxt;
   int qc;
@@ -4388,15 +4888,52 @@ 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;
+
+  /* XXX - see if we need to do anything here if sigterm_received == 1,
+     we probably don't want to reset the event hook since we will not be
+     jumping to the top level */
+  if (sigterm_received)
+    {
+      /* RESET_SIGTERM;        */
+      return 0;
+    }
+
+  sig = 0;
+  if (terminating_signal)
+    sig = terminating_signal;
+  else if (interrupt_state)
+    sig = SIGINT;
+  else if (read_timeout && read_timeout->alrmflag)
+    sig = SIGALRM;
+  else if (RL_ISSTATE (RL_STATE_TIMEOUT))              /* just in case */
+    {
+      sig = SIGALRM;
+      if (read_timeout)
+       read_timeout->alrmflag = 1;
+    }
+  else
+    sig = first_pending_trap ();
+
   /* If we're going to longjmp to top_level, make sure we clean up readline.
      check_signals will call QUIT, which will eventually longjmp to top_level,
-     calling run_interrupt_trap along the way.  The check for sigalrm_seen is
-     to clean up the read builtin's state. */
-  if (terminating_signal || interrupt_state || sigalrm_seen)
+     calling run_interrupt_trap along the way.  The check against read_timeout
+     is so we can clean up the read builtin's state. */
+  if (terminating_signal || interrupt_state || (read_timeout && read_timeout->alrmflag))
     rl_cleanup_after_signal ();
   bashline_reset_event_hook ();
+
+  RL_UNSETSTATE (RL_STATE_TIMEOUT);                    /* XXX */
+
+  /* posix mode SIGINT during read -e. We only get here if SIGINT is trapped. */
+  if (posixly_correct && this_shell_builtin == read_builtin && sig == SIGINT)
+    {
+      last_command_exit_value = 128|SIGINT;
+      throw_to_top_level ();
+    }
+
   check_signals_and_traps ();  /* XXX */
   return 0;
 }