Imported from ../bash-1.14.7.tar.gz.
diff --git a/bashline.c b/bashline.c
new file mode 100644
index 0000000..a924b05
--- /dev/null
+++ b/bashline.c
@@ -0,0 +1,1797 @@
+/* bashline.c -- Bash's interface to the readline library. */
+
+/* Copyright (C) 1987,1991 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 1, or (at your option)
+   any later version.
+
+   Bash is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Bash; see the file COPYING.  If not, write to the Free
+   Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "bashtypes.h"
+#include "posixstat.h"
+
+#include <stdio.h>
+#include "bashansi.h"
+#include <readline/rlconf.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#include "shell.h"
+#include "builtins.h"
+#include "builtins/common.h"
+#include "bashhist.h"
+#include "execute_cmd.h"
+
+#if defined (ALIAS)
+#  include "alias.h"
+#endif
+
+#if defined (BRACE_EXPANSION)
+#  define BRACE_COMPLETION
+#endif /* BRACE_EXPANSION */
+
+#if defined (BRACE_COMPLETION)
+extern void bash_brace_completion ();
+#endif /* BRACE_COMPLETION */
+
+/* Functions bound to keys in Readline for Bash users. */
+static void shell_expand_line ();
+static void display_shell_version (), operate_and_get_next ();
+static void history_expand_line (), bash_ignore_filenames ();
+
+/* Helper functions for Readline. */
+static int bash_directory_completion_hook ();
+static void filename_completion_ignore ();
+static void bash_push_line ();
+
+static char **attempt_shell_completion ();
+static char *variable_completion_function ();
+static char *hostname_completion_function ();
+static char *command_word_completion_function ();
+static char *command_subst_completion_function ();
+
+static void snarf_hosts_from_file (), add_host_name ();
+static void sort_hostname_list ();
+
+#define DYNAMIC_HISTORY_COMPLETION
+#if defined (DYNAMIC_HISTORY_COMPLETION)
+static void dynamic_complete_history ();
+#endif /* DYNAMIC_HISTORY_COMPLETION */
+
+/* Variables used here but defined in other files. */
+extern int posixly_correct, no_symbolic_links;
+extern int rl_explicit_arg;
+extern char *current_prompt_string, *ps1_prompt;
+extern STRING_INT_ALIST word_token_alist[];
+extern Function *rl_last_func;
+extern int rl_filename_completion_desired;
+
+/* SPECIFIC_COMPLETION_FUNCTIONS specifies that we have individual
+   completion functions which indicate what type of completion should be
+   done (at or before point) that can be bound to key sequences with
+   the readline library. */
+#define SPECIFIC_COMPLETION_FUNCTIONS
+
+#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
+static void
+  bash_specific_completion (),
+  bash_complete_filename (), bash_possible_filename_completions (),
+  bash_complete_filename_internal (),
+  bash_complete_username (), bash_possible_username_completions (),
+  bash_complete_username_internal (),
+  bash_complete_hostname (), bash_possible_hostname_completions (),
+  bash_complete_hostname_internal (),
+  bash_complete_variable (), bash_possible_variable_completions (),
+  bash_complete_variable_internal (),
+  bash_complete_command (), bash_possible_command_completions (),
+  bash_complete_command_internal ();
+#endif /* SPECIFIC_COMPLETION_FUNCTIONS */
+
+/* Non-zero once initalize_readline () has been called. */
+int bash_readline_initialized = 0;
+
+#if defined (VI_MODE)
+static void vi_edit_and_execute_command ();
+extern char *rl_vi_comment_begin;
+#endif
+
+static Function *old_rl_startup_hook = (Function *) NULL;
+
+/* 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;
+{
+#if defined (VI_MODE)
+  if (on_or_off)
+    {
+      rl_bind_key_in_map (CTRL('I'), rl_insert, vi_insertion_keymap);
+      if (rl_vi_comment_begin)
+	free (rl_vi_comment_begin);
+      rl_vi_comment_begin = savestring ("#");
+    }
+  else
+    rl_bind_key_in_map (CTRL('I'), rl_complete, vi_insertion_keymap);
+#endif
+} 
+
+/* Called once from parse.y if we are going to use readline. */
+void
+initialize_readline ()
+{
+  if (bash_readline_initialized)
+    return;
+
+  rl_terminal_name = get_string_value ("TERM");
+  rl_instream = stdin;
+  rl_outstream = stderr;
+  rl_special_prefixes = "$@";
+
+  /* Allow conditional parsing of the ~/.inputrc file. */
+  rl_readline_name = "Bash";
+
+  /* Bind up our special shell functions. */
+  rl_add_defun ("shell-expand-line", (Function *)shell_expand_line, -1);
+  rl_bind_key_in_map
+    (CTRL('E'), (Function *)shell_expand_line, emacs_meta_keymap);
+
+  /* Bind up our special shell functions. */
+  rl_add_defun ("history-expand-line", (Function *)history_expand_line, -1);
+  rl_bind_key_in_map ('^', (Function *)history_expand_line, emacs_meta_keymap);
+
+  /* Backwards compatibility. */
+  rl_add_defun ("insert-last-argument", rl_yank_last_arg, -1);
+
+  rl_add_defun
+    ("operate-and-get-next", (Function *)operate_and_get_next, CTRL('O'));
+
+  rl_add_defun
+    ("display-shell-version", (Function *)display_shell_version, -1);
+
+  rl_bind_key_in_map
+    (CTRL ('V'), (Function *)display_shell_version, emacs_ctlx_keymap);
+
+  /* In Bash, the user can switch editing modes with "set -o [vi emacs]",
+     so it is not necessary to allow C-M-j for context switching.  Turn
+     off this occasionally confusing behaviour. */
+  rl_unbind_key_in_map (CTRL('J'), emacs_meta_keymap);
+  rl_unbind_key_in_map (CTRL('M'), emacs_meta_keymap);
+#if defined (VI_MODE)
+  rl_unbind_key_in_map (CTRL('E'), vi_movement_keymap);
+#endif
+  
+#if defined (BRACE_COMPLETION)
+  rl_add_defun ("complete-into-braces", bash_brace_completion, -1);
+  rl_bind_key_in_map ('{', bash_brace_completion, emacs_meta_keymap);
+#endif /* BRACE_COMPLETION */
+
+#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
+  rl_add_defun ("complete-filename", bash_complete_filename, -1);
+  rl_bind_key_in_map ('/', bash_complete_filename, emacs_meta_keymap);
+  rl_add_defun ("possible-filename-completions",
+		bash_possible_filename_completions, -1);
+  rl_bind_key_in_map ('/', bash_possible_filename_completions,
+		      emacs_ctlx_keymap);
+
+  rl_add_defun ("complete-username", bash_complete_username, -1);
+  rl_bind_key_in_map ('~', bash_complete_username, emacs_meta_keymap);
+  rl_add_defun ("possible-username-completions",
+		bash_possible_username_completions, -1);
+  rl_bind_key_in_map ('~', bash_possible_username_completions,
+		      emacs_ctlx_keymap);
+
+  rl_add_defun ("complete-hostname", bash_complete_hostname, -1);
+  rl_bind_key_in_map ('@', bash_complete_hostname, emacs_meta_keymap);
+  rl_add_defun ("possible-hostname-completions",
+		bash_possible_hostname_completions, -1);
+  rl_bind_key_in_map ('@', bash_possible_hostname_completions,
+		      emacs_ctlx_keymap);
+
+  rl_add_defun ("complete-variable", bash_complete_variable, -1);
+  rl_bind_key_in_map ('$', bash_complete_variable, emacs_meta_keymap);
+  rl_add_defun ("possible-variable-completions",
+		bash_possible_variable_completions, -1);
+  rl_bind_key_in_map ('$', bash_possible_variable_completions,
+		      emacs_ctlx_keymap);
+
+  rl_add_defun ("complete-command", bash_complete_command, -1);
+  rl_bind_key_in_map ('!', bash_complete_command, emacs_meta_keymap);
+  rl_add_defun ("possible-command-completions",
+		bash_possible_command_completions, -1);
+  rl_bind_key_in_map ('!', bash_possible_command_completions,
+		      emacs_ctlx_keymap);
+
+#endif /* SPECIFIC_COMPLETION_FUNCTIONS */
+
+#if defined (DYNAMIC_HISTORY_COMPLETION)
+  rl_add_defun ("dynamic-complete-history", dynamic_complete_history, -1);
+  rl_bind_key_in_map (TAB, dynamic_complete_history, emacs_meta_keymap);
+#endif /* DYNAMIC_HISTORY_COMPLETION */
+
+  /* Tell the completer that we want a crack first. */
+  rl_attempted_completion_function = (CPPFunction *)attempt_shell_completion;
+
+  /* Tell the completer that we might want to follow symbolic links or
+     do other expansion on directory names. */
+  rl_directory_completion_hook = bash_directory_completion_hook;
+
+  /* Tell the filename completer we want a chance to ignore some names. */
+  rl_ignore_some_completions_function = (Function *)filename_completion_ignore;
+
+#if defined (VI_MODE)
+  rl_bind_key_in_map ('v', vi_edit_and_execute_command, vi_movement_keymap);
+#endif
+
+  rl_completer_quote_characters = "'\"";
+  /* Need to modify this from the default; `$', `{', `\', and ``' are not
+     word break characters. */
+  rl_completer_word_break_characters = " \t\n\"'@><=;|&("; /**/
+
+  if (posixly_correct)
+    posix_readline_initialize (1);
+
+  bash_readline_initialized = 1;
+}
+
+/* On Sun systems at least, rl_attempted_completion_function can end up
+   getting set to NULL, and rl_completion_entry_function set to do command
+   word completion if Bash is interrupted while trying to complete a command
+   word.  This just resets all the completion functions to the right thing.
+   It's called from throw_to_top_level(). */
+void
+bashline_reinitialize ()
+{
+  tilde_initialize ();
+  rl_attempted_completion_function = attempt_shell_completion;
+  rl_completion_entry_function = (Function *)NULL;
+  rl_directory_completion_hook = bash_directory_completion_hook;
+  rl_ignore_some_completions_function = (Function *)filename_completion_ignore;
+}
+
+/* Contains the line to push into readline. */
+static char *push_to_readline = (char *)NULL;
+
+/* Push the contents of push_to_readline into the
+   readline buffer. */
+static void
+bash_push_line ()
+{
+  if (push_to_readline)
+    {
+      rl_insert_text (push_to_readline);
+      free (push_to_readline);
+      push_to_readline = (char *)NULL;
+      rl_startup_hook = old_rl_startup_hook;
+    }
+}
+
+/* Call this to set the initial text for the next line to read
+   from readline. */
+int
+bash_re_edit (line)
+     char *line;
+{
+  if (push_to_readline)
+    free (push_to_readline);
+
+  push_to_readline = savestring (line);
+  old_rl_startup_hook = rl_startup_hook;
+  rl_startup_hook = (Function *)bash_push_line;
+
+  return (0);
+}
+
+static void
+display_shell_version (count, c)
+     int count, c;
+{
+  crlf ();
+  show_shell_version ();
+  putc ('\r', rl_outstream);
+  fflush (rl_outstream);
+  rl_on_new_line ();
+  rl_redisplay ();
+}
+
+/* **************************************************************** */
+/*								    */
+/*			     Readline Stuff			    */
+/*								    */
+/* **************************************************************** */
+
+/* If the user requests hostname completion, then simply build a list
+   of hosts, and complete from that forever more. */
+#if !defined (ETCHOSTS)
+#define ETCHOSTS "/etc/hosts"
+#endif
+
+/* The kept list of hostnames. */
+static char **hostname_list = (char **)NULL;
+
+/* The physical size of the above list. */
+static int hostname_list_size = 0;
+
+/* The length of the above list. */
+static int hostname_list_length = 0;
+
+/* Whether or not HOSTNAME_LIST has been initialized. */
+int hostname_list_initialized = 0;
+
+/* Non-zero means that HOSTNAME_LIST needs to be sorted. */
+static int hostname_list_needs_sorting = 0;
+
+/* Initialize the hostname completion table. */
+static void
+initialize_hostname_list ()
+{
+  char *temp;
+
+  temp = get_string_value ("HOSTFILE");
+  if (!temp)
+    temp = get_string_value ("hostname_completion_file");
+  if (!temp)
+    temp = ETCHOSTS;
+
+  snarf_hosts_from_file (temp);
+  sort_hostname_list ();
+
+  if (hostname_list)
+    hostname_list_initialized++;
+}
+
+/* Add NAME to the list of hosts. */
+static void
+add_host_name (name)
+     char *name;
+{
+  if (hostname_list_length + 2 > hostname_list_size)
+    {
+      hostname_list = (char **)
+	xrealloc (hostname_list,
+		  (1 + (hostname_list_size += 100)) * sizeof (char *));
+    }
+
+  hostname_list[hostname_list_length] = savestring (name);
+  hostname_list[++hostname_list_length] = (char *)NULL;
+  hostname_list_needs_sorting++;
+}
+
+/* After you have added some names, you should sort the list of names. */
+static void
+sort_hostname_list ()
+{
+  if (hostname_list_needs_sorting && hostname_list)
+    sort_char_array (hostname_list);
+  hostname_list_needs_sorting = 0;
+}
+
+#define cr_whitespace(c) ((c) == '\r' || (c) == '\n' || whitespace(c))
+
+static void
+snarf_hosts_from_file (filename)
+     char *filename;
+{
+  FILE *file = fopen (filename, "r");
+  char *temp, buffer[256], name[256];
+  register int i, start;
+
+  if (!file)
+    return;
+
+  while (temp = fgets (buffer, 255, file))
+    {
+      /* Skip to first character. */
+      for (i = 0; buffer[i] && cr_whitespace (buffer[i]); i++);
+
+      /* If comment, ignore. */
+      if (buffer[i] == '#')
+	continue;
+
+      /* If `preprocessor' directive, do the include. */
+      if (strncmp (&buffer[i], "$include ", 9) == 0)
+	{
+	  char *includefile = &buffer[i + 9];
+	  char *t;
+
+	  /* Find start of filename. */
+	  while (*includefile && whitespace (*includefile))
+	    includefile++;
+
+	  t = includefile;
+
+	  /* Find end of filename. */
+	  while (*t && !cr_whitespace (*t))
+	    t++;
+
+	  *t = '\0';
+
+	  snarf_hosts_from_file (includefile);
+	  continue;
+	}
+
+      /* Skip internet address. */
+      for (; buffer[i] && !cr_whitespace (buffer[i]); i++);
+
+      /* Gobble up names.  Each name is separated with whitespace. */
+      while (buffer[i] && buffer[i] != '#')
+	{
+	  for (; i && cr_whitespace (buffer[i]); i++);
+	  if (buffer[i] ==  '#')
+	    continue;
+	  for (start = i; buffer[i] && !cr_whitespace (buffer[i]); i++);
+	  if ((i - start) == 0)
+	    continue;
+	  strncpy (name, buffer + start, i - start);
+	  name[i - start] = '\0';
+	  add_host_name (name);
+	}
+    }
+  fclose (file);
+}
+
+/* Return a NULL terminated list of hostnames which begin with TEXT.
+   Initialize the hostname list the first time if neccessary.
+   The array is malloc ()'ed, but not the individual strings. */
+static char **
+hostnames_matching (text)
+     char *text;
+{
+  register int i, len = strlen (text);
+  register int begin, end;
+  int last_search = -1;
+  char **result = (char **)NULL;
+
+  if (!hostname_list_initialized)
+    {
+      initialize_hostname_list ();
+
+      if (!hostname_list_initialized)
+	return ((char **)NULL);
+    }
+
+  sort_hostname_list ();
+
+  /* The list is sorted.  Do a binary search on it for the first character
+     in TEXT, and then grovel the names of interest. */
+  begin = 0; end = hostname_list_length;
+
+  /* Special case.  If TEXT consists of nothing, then the whole list is
+     what is desired. */
+  if (!*text)
+    {
+      result = (char **)xmalloc ((1 + hostname_list_length) * sizeof (char *));
+      for (i = 0; i < hostname_list_length; i++)
+	result[i] = hostname_list[i];
+      result[i] = (char *)NULL;
+      return (result);
+    }
+
+  /* Scan until found, or failure. */
+  while (end != begin)
+    {
+      int r = 0;
+
+      i = ((end - begin) / 2) + begin;
+      if (i == last_search)
+	break;
+
+      if (hostname_list[i] &&
+	  (r = strncmp (hostname_list[i], text, len)) == 0)
+	{
+	  while (strncmp (hostname_list[i], text, len) == 0 && i) i--;
+	  if (strncmp (hostname_list[i], text, len) != 0) i++;
+
+	  begin = i;
+	  while (hostname_list[i] &&
+		 strncmp (hostname_list[i], text, len) == 0) i++;
+	  end = i;
+
+	  result = (char **)xmalloc ((1 + (end - begin)) * sizeof (char *));
+	  for (i = 0; i + begin < end; i++)
+	    result[i] = hostname_list[begin + i];
+	  result[i] = (char *)NULL;
+	  return (result);
+	}
+
+      last_search = i;
+
+      if (r < 0)
+	begin = i;
+      else
+	end = i;
+    }
+  return ((char **)NULL);
+}
+
+/* The equivalent of the K*rn shell C-o operate-and-get-next-history-line
+   editing command. */
+static int saved_history_line_to_use = 0;
+
+static void
+set_saved_history ()
+{
+  if (saved_history_line_to_use)
+    rl_get_previous_history (history_length - saved_history_line_to_use);
+  saved_history_line_to_use = 0;
+  rl_startup_hook = old_rl_startup_hook;
+}  
+
+static void
+operate_and_get_next (count, c)
+     int count, c;
+{
+  int where;
+
+  /* Accept the current line. */
+  rl_newline ();	
+
+  /* Find the current line, and find the next line to use. */
+  where = where_history ();
+
+  if ((history_is_stifled () && (history_length >= max_input_history)) ||
+      (where >= history_length - 1))
+    saved_history_line_to_use = where;
+  else
+    saved_history_line_to_use = where + 1;
+
+  old_rl_startup_hook = rl_startup_hook;
+  rl_startup_hook = (Function *)set_saved_history;
+}
+
+#if defined (VI_MODE)
+/* 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. */
+
+#define VI_EDIT_COMMAND "fc -e ${VISUAL:-${EDITOR:-vi}}"
+
+static void
+vi_edit_and_execute_command (count, c)
+{
+  char *command;
+
+  /* Accept the current line. */
+  rl_newline ();	
+
+  if (rl_explicit_arg)
+    {
+      command = xmalloc (strlen (VI_EDIT_COMMAND) + 8);
+      sprintf (command, "%s %d", VI_EDIT_COMMAND, count);
+    }
+  else
+    {
+      /* Take the command we were just editing, add it to the history file,
+	 then call fc to operate on it.  We have to add a dummy command to
+	 the end of the history because fc ignores the last command (assumes
+	 it's supposed to deal with the command before the `fc'). */
+      using_history ();
+      add_history (rl_line_buffer);
+      add_history ("");
+      history_lines_this_session++;
+      using_history ();
+      command = savestring (VI_EDIT_COMMAND);
+    }
+  parse_and_execute (command, "v", -1);
+  rl_line_buffer[0] = '\0';	/* erase pre-edited command */
+}
+#endif /* VI_MODE */
+
+/* **************************************************************** */
+/*								    */
+/*			How To Do Shell Completion		    */
+/*								    */
+/* **************************************************************** */
+
+/* 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)
+     char *text;
+     int start, end;
+{
+  int in_command_position, ti;
+  char **matches = (char **)NULL;
+  char *command_separator_chars = ";|&{(`";
+
+  rl_ignore_some_completions_function =
+    (Function *)filename_completion_ignore;
+
+  /* Determine if this could be a command word.  It is if it appears at
+     the start of the line (ignoring preceding whitespace), or if it
+     appears after a character that separates commands.  It cannot be a
+     command word if we aren't at the top-level prompt. */
+  ti = start - 1;
+
+  while ((ti > -1) && (whitespace (rl_line_buffer[ti])))
+    ti--;
+
+  in_command_position = 0;
+  if (ti < 0)
+    {
+      /* Only do command completion at the start of a line when we
+         are prompting at the top level. */
+      if (current_prompt_string == ps1_prompt)
+	in_command_position++;
+    }
+  else if (member (rl_line_buffer[ti], command_separator_chars))
+    {
+      register int this_char, prev_char;
+
+      in_command_position++;
+
+      /* 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 = rl_line_buffer[ti - 1];
+
+      if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) ||
+	  (this_char == '|' && prev_char == '>'))
+	in_command_position = 0;
+      else if (char_is_quoted (rl_line_buffer, ti))
+	in_command_position = 0;
+    }
+  else
+    {
+      /* This still could be in command position.  It is possible
+	 that all of the previous words on the line are variable
+	 assignments. */
+    }
+
+  /* Special handling for command substitution.  XXX - this should handle
+     `$(' as well. */
+  if (*text == '`' && unclosed_pair (rl_line_buffer, start, "`"))
+    matches = completion_matches (text, command_subst_completion_function);
+
+  /* Variable name? */
+  if (!matches && *text == '$')
+    matches = completion_matches (text, variable_completion_function);
+
+  /* If the word starts in `~', and there is no slash in the word, then
+     try completing this word as a username. */
+  if (!matches && *text == '~' && !strchr (text, '/'))
+    matches = completion_matches (text, username_completion_function);
+
+  /* Another one.  Why not?  If the word starts in '@', then look through
+     the world of known hostnames for completion first. */
+  if (!matches && *text == '@')
+    matches = completion_matches (text, hostname_completion_function);
+
+  /* And last, (but not least) if this word is in a command position, then
+     complete over possible command names, including aliases, functions,
+     and command names. */
+  if (!matches && in_command_position)
+    {
+      matches = completion_matches (text, command_word_completion_function);
+      /* If we are attempting command completion and nothing matches, we
+	 do not want readline to perform filename completion for us.  We
+	 still want to be able to complete partial pathnames, so set the
+	 completion ignore function to something which will remove filenames
+	 and leave directories in the match list. */
+      if (!matches)
+	rl_ignore_some_completions_function = (Function *)bash_ignore_filenames;
+    }
+
+  return (matches);
+}
+
+/* This is the function to call when the word to complete is in a position
+   where a command word can be found.  It grovels $PATH, looking for commands
+   that match.  It also scans aliases, function names, and the shell_builtin
+   table. */
+static char *
+command_word_completion_function (hint_text, state)
+     char *hint_text;
+     int state;
+{
+  static char *hint = (char *)NULL;
+  static char *path = (char *)NULL;
+  static char *val = (char *)NULL;
+  static char *filename_hint = (char *)NULL;
+  static int path_index, hint_len, istate;
+  static int mapping_over, local_index;
+  static SHELL_VAR **varlist = (SHELL_VAR **)NULL;
+#if defined (ALIAS)
+  static ASSOC **alias_list = (ASSOC **)NULL;
+#endif /* ALIAS */
+
+  /* We have to map over the possibilities for command words.  If we have
+     no state, then make one just for that purpose. */
+
+  if (!state)
+    {
+      if (hint)
+	free (hint);
+
+      mapping_over = 0;
+      val = (char *)NULL;
+
+      /* If this is an absolute program name, do not check it against
+	 aliases, reserved words, functions or builtins.  We must check
+	 whether or not it is unique, and, if so, whether that filename
+	 is executable. */
+      if (absolute_program (hint_text))
+	{
+	  /* Perform tilde expansion on what's passed, so we don't end up
+	     passing filenames with tildes directly to stat(). */
+	  if (*hint_text == '~')
+	    hint = tilde_expand (hint_text);
+	  else
+	    hint = savestring (hint_text);
+	  hint_len = strlen (hint);
+
+	  if (filename_hint)
+	    free (filename_hint);
+	  filename_hint = savestring (hint);
+
+	  mapping_over = 4;
+	  istate = 0;
+	  goto inner;
+	}
+
+      hint = savestring (hint_text);
+      hint_len = strlen (hint);
+
+      path = get_string_value ("PATH");
+      path_index = 0;
+
+      /* Initialize the variables for each type of command word. */
+      local_index = 0;
+
+      if (varlist)
+	free (varlist);
+
+      varlist = all_visible_functions ();
+
+#if defined (ALIAS)
+      if (alias_list)
+	free (alias_list);
+
+      alias_list = all_aliases ();
+#endif /* ALIAS */
+    }
+
+  /* mapping_over says what we are currently hacking.  Note that every case
+     in this list must fall through when there are no more possibilities. */
+
+  switch (mapping_over)
+    {
+    case 0:			/* Aliases come first. */
+#if defined (ALIAS)
+      while (alias_list && alias_list[local_index])
+	{
+	  register char *alias;
+
+	  alias = alias_list[local_index++]->name;
+
+	  if (STREQN (alias, hint, hint_len))
+	    return (savestring (alias));
+	}
+#endif /* ALIAS */
+      local_index = 0;
+      mapping_over++;
+
+    case 1:			/* Then shell reserved words. */
+      {
+	while (word_token_alist[local_index].word)
+	  {
+	    register char *reserved_word;
+
+	    reserved_word = word_token_alist[local_index++].word;
+
+	    if (STREQN (reserved_word, hint, hint_len))
+	      return (savestring (reserved_word));
+	  }
+	local_index = 0;
+	mapping_over++;
+      }
+
+    case 2:			/* Then function names. */
+      while (varlist && varlist[local_index])
+	{
+	  register char *varname;
+
+	  varname = varlist[local_index++]->name;
+
+	  if (STREQN (varname, hint, hint_len))
+	    return (savestring (varname));
+	}
+      local_index = 0;
+      mapping_over++;
+
+    case 3:			/* Then shell builtins. */
+      for (; local_index < num_shell_builtins; local_index++)
+	{
+	  /* Ignore it if it doesn't have a function pointer or if it
+	     is not currently enabled. */
+	  if (!shell_builtins[local_index].function ||
+	      (shell_builtins[local_index].flags & BUILTIN_ENABLED) == 0)
+	    continue;
+
+	  if (STREQN (shell_builtins[local_index].name, hint, hint_len))
+	    {
+	      int i = local_index++;
+
+	      return (savestring (shell_builtins[i].name));
+	    }
+	}
+      local_index = 0;
+      mapping_over++;
+    }
+
+  /* Repeatedly call filename_completion_func<tion while we have
+     members of PATH left.  Question:  should we stat each file?
+     Answer: we call executable_file () on each file. */
+ outer:
+
+  istate = (val != (char *)NULL);
+
+  if (!istate)
+    {
+      char *current_path;
+
+      /* Get the next directory from the path.  If there is none, then we
+	 are all done. */
+      if (!path || !path[path_index] ||
+	  (current_path = extract_colon_unit (path, &path_index)) == 0)
+	return ((char *)NULL);
+
+      if (*current_path == 0)
+	{
+	  free (current_path);
+	  current_path = savestring (".");
+	}
+
+      if (*current_path == '~')
+	{
+	  char *t;
+
+	  t = tilde_expand (current_path);
+	  free (current_path);
+	  current_path = t;
+	}
+
+      if (filename_hint)
+	free (filename_hint);
+
+      filename_hint = xmalloc (2 + strlen (current_path) + hint_len);
+      sprintf (filename_hint, "%s/%s", current_path, hint);
+
+      free (current_path);
+    }
+
+ inner:
+  val = filename_completion_function (filename_hint, istate);
+  istate = 1;
+
+  if (!val)
+    {
+      /* If the hint text is an absolute program, then don't bother
+	 searching through PATH. */
+      if (absolute_program (hint))
+	return ((char *)NULL);
+
+      goto outer;
+    }
+  else
+    {
+      int match;
+      char *temp;
+
+      if (absolute_program (hint))
+	{
+	  match = strncmp (val, hint, hint_len) == 0;
+	  /* If we performed tilde expansion, restore the original
+	     filename. */
+	  if (*hint_text == '~')
+	    {
+	      int l, tl, vl;
+	      vl = strlen (val);
+	      tl = strlen (hint_text);
+	      l = vl - hint_len;	/* # of chars added */
+	      temp = xmalloc (l + 2 + tl);
+	      strcpy (temp, hint_text);
+	      strcpy (temp + tl, val + vl - l);
+	    }
+	  else
+	    temp = savestring (val);
+	}
+      else
+	{
+	  temp = strrchr (val, '/');
+
+	  if (temp)
+	    {
+	      temp++;
+	      match = strncmp (temp, hint, hint_len) == 0;
+	      if (match)
+		temp = savestring (temp);
+	    }
+	  else
+	    match = 0;
+	}
+
+      /* If we have found a match, and it is an executable file, return it. */
+      if (match && executable_file (val))
+	{
+	  free (val);
+	  val = "";		/* So it won't be NULL. */
+	  return (temp);
+	}
+      else
+	{
+	  free (val);
+	  goto inner;
+	}
+    }
+}
+
+static char *
+command_subst_completion_function (text, state)
+     int state;
+     char *text;
+{
+  static char **matches = (char **)NULL;
+  static char *orig_start, *filename_text = (char *)NULL;
+  static int cmd_index, start_len;
+
+  if (state == 0)
+    {
+      if (filename_text)
+	free (filename_text);
+      orig_start = text;
+      if (*text == '`')
+        text++;
+      else if (*text == '$' && text[1] == '(')
+        text += 2;
+      start_len = text - orig_start;
+      filename_text = savestring (text);
+      if (matches)
+	free (matches);
+      matches = completion_matches (filename_text, command_word_completion_function);
+      cmd_index = 0;
+    }
+
+  if (!matches || !matches[cmd_index])
+    {
+      rl_filename_quoting_desired = 0;	/* disable quoting */
+      return ((char *)NULL);
+    }
+  else
+    {
+      char *value;
+
+      value = xmalloc (1 + start_len + strlen (matches[cmd_index]));
+
+      if (start_len == 1)
+        value[0] = *orig_start;
+      else
+        strncpy (value, orig_start, start_len);
+
+      strcpy (value + start_len, matches[cmd_index]);
+
+      cmd_index++;
+      return (value);
+    }
+}
+
+/* Okay, now we write the entry_function for variable completion. */
+static char *
+variable_completion_function (text, state)
+     int state;
+     char *text;
+{
+  register SHELL_VAR *var = (SHELL_VAR *)NULL;
+  static SHELL_VAR **varlist = (SHELL_VAR **)NULL;
+  static int varlist_index;
+  static char *varname = (char *)NULL;
+  static int namelen;
+  static int first_char, first_char_loc;
+
+  if (!state)
+    {
+      if (varname)
+	free (varname);
+
+      first_char_loc = 0;
+      first_char = text[0];
+
+      if (first_char == '$')
+	first_char_loc++;
+
+      varname = savestring (text + first_char_loc);
+
+      namelen = strlen (varname);
+      if (varlist)
+	free (varlist);
+      varlist = all_visible_variables ();
+      varlist_index = 0;
+    }
+
+  while (varlist && varlist[varlist_index])
+    {
+      var = varlist[varlist_index];
+
+      /* Compare.  You can't do better than Zayre.  No text is also
+	 a match.  */
+      if (!*varname || (strncmp (varname, var->name, namelen) == 0))
+	break;
+      varlist_index++;
+    }
+
+  if (!varlist || !varlist[varlist_index])
+    {
+      return ((char *)NULL);
+    }
+  else
+    {
+      char *value = xmalloc (2 + strlen (var->name));
+
+      if (first_char_loc)
+	*value = first_char;
+
+      strcpy (&value[first_char_loc], var->name);
+
+      varlist_index++;
+      return (value);
+    }
+}
+
+/* How about a completion function for hostnames? */
+static char *
+hostname_completion_function (text, state)
+     int state;
+     char *text;
+{
+  static char **list = (char **)NULL;
+  static int list_index = 0;
+  static int first_char, first_char_loc;
+
+  /* If we don't have any state, make some. */
+  if (!state)
+    {
+      if (list)
+	free (list);
+
+      list = (char **)NULL;
+
+      first_char_loc = 0;
+      first_char = *text;
+
+      if (first_char == '@')
+	first_char_loc++;
+
+      list = hostnames_matching (&text[first_char_loc]);
+      list_index = 0;
+    }
+
+  if (list && list[list_index])
+    {
+      char *t = xmalloc (2 + strlen (list[list_index]));
+
+      *t = first_char;
+      strcpy (t + first_char_loc, list[list_index]);
+      list_index++;
+      return (t);
+    }
+  else
+    return ((char *)NULL);
+}
+
+/* History and alias expand the line. */
+static char *
+history_expand_line_internal (line)
+     char *line;
+{
+  char *new_line;
+
+  new_line = pre_process_line (line, 0, 0);
+  return new_line;
+}
+
+#if defined (ALIAS)
+/* Perform alias expansion on LINE and return the new line. */
+static char *
+alias_expand_line_internal (line)
+     char *line;
+{
+  char *alias_line;
+
+  alias_line = alias_expand (line);
+  return alias_line;
+}
+#endif
+
+/* There was an error in expansion.  Let the preprocessor print
+   the error here. */
+static void
+cleanup_expansion_error ()
+{
+  char *to_free;
+
+  fprintf (rl_outstream, "\r\n");
+  to_free = pre_process_line (rl_line_buffer, 1, 0);
+  free (to_free);
+  putc ('\r', rl_outstream);
+  rl_forced_update_display ();
+}
+
+/* If NEW_LINE differs from what is in the readline line buffer, add an
+   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;
+{
+  if (strcmp (new_line, rl_line_buffer) != 0)
+    {
+      rl_point = rl_end;
+
+      rl_add_undo (UNDO_BEGIN, 0, 0, 0);
+      rl_delete_text (0, rl_point);
+      rl_point = rl_end = 0;
+      rl_insert_text (new_line);
+      rl_add_undo (UNDO_END, 0, 0, 0);
+    }
+}
+
+/* Make NEW_LINE be the current readline line.  This frees NEW_LINE. */
+static void
+set_up_new_line (new_line)
+     char *new_line;
+{
+  int old_point = rl_point;
+  int at_end = rl_point == rl_end;
+
+  /* If the line was history and alias expanded, then make that
+     be one thing to undo. */
+  maybe_make_readline_line (new_line);
+  free (new_line);
+
+  /* Place rl_point where we think it should go. */
+  if (at_end)
+    rl_point = rl_end;
+  else if (old_point < rl_end)
+    {
+      rl_point = old_point;
+      if (!whitespace (rl_line_buffer[rl_point]))
+	rl_forward_word (1);
+    }
+}
+
+/* History expand the line. */
+static void
+history_expand_line (ignore)
+     int ignore;
+{
+  char *new_line;
+
+  new_line = history_expand_line_internal (rl_line_buffer);
+
+  if (new_line)
+    set_up_new_line (new_line);
+  else
+    cleanup_expansion_error ();
+}
+  
+/* History and alias expand the line. */
+static void
+history_and_alias_expand_line (ignore)
+     int ignore;
+{
+  char *new_line;
+
+  new_line = pre_process_line (rl_line_buffer, 0, 0);
+
+#if defined (ALIAS)
+  if (new_line)
+    {
+      char *alias_line;
+
+      alias_line = alias_expand (new_line);
+      free (new_line);
+      new_line = alias_line;
+    }
+#endif /* ALIAS */
+
+  if (new_line)
+    set_up_new_line (new_line);
+  else
+    cleanup_expansion_error ();
+}
+
+/* History and alias expand the line, then perform the shell word
+   expansions by calling expand_string. */
+static void
+shell_expand_line (ignore)
+     int ignore;
+{
+  char *new_line;
+
+  new_line = pre_process_line (rl_line_buffer, 0, 0);
+
+#if defined (ALIAS)
+  if (new_line)
+    {
+      char *alias_line;
+
+      alias_line = alias_expand (new_line);
+      free (new_line);
+      new_line = alias_line;
+    }
+#endif /* ALIAS */
+
+  if (new_line)
+    {
+      int old_point = rl_point;
+      int at_end = rl_point == rl_end;
+
+      /* If the line was history and alias expanded, then make that
+	 be one thing to undo. */
+      maybe_make_readline_line (new_line);
+      free (new_line);
+
+      /* If there is variable expansion to perform, do that as a separate
+	 operation to be undone. */
+      {
+	WORD_LIST *expanded_string;
+
+	expanded_string = expand_string (rl_line_buffer, 0);
+	if (!expanded_string)
+	  new_line = savestring ("");
+	else
+	  {
+	    new_line = string_list (expanded_string);
+	    dispose_words (expanded_string);
+	  }
+
+	maybe_make_readline_line (new_line);
+	free (new_line);
+
+	/* Place rl_point where we think it should go. */
+	if (at_end)
+	  rl_point = rl_end;
+	else if (old_point < rl_end)
+	  {
+	    rl_point = old_point;
+	    if (!whitespace (rl_line_buffer[rl_point]))
+	      rl_forward_word (1);
+	  }
+      }
+    }
+  else
+    cleanup_expansion_error ();
+}
+
+/* Filename completion ignore.  Emulates the "fignore" facility of
+   tcsh.  If FIGNORE is set, then don't match files with the
+   given suffixes.  If only one of the possibilities has an acceptable
+   suffix, delete the others, else just return and let the completer
+   signal an error.  It is called by the completer when real
+   completions are done on filenames by the completer's internal
+   function, not for completion lists (M-?) and not on "other"
+   completion types, such as hostnames or commands.
+ 
+   It is passed a NULL-terminated array of (char *)'s that must be
+   free()'d if they are deleted.  The first element (names[0]) is the
+   least-common-denominator string of the matching patterns (i.e.
+   u<TAB> produces names[0] = "und", names[1] = "under.c", names[2] =
+   "undun.c", name[3] = NULL).  */
+
+struct ign {
+  char *val;
+  int len;
+};
+
+static struct ign *ignores;	/* Store the ignore strings here */
+static int num_ignores;		/* How many are there? */
+static char *last_fignore;	/* Last value of fignore - cached for speed */
+
+static void
+setup_ignore_patterns ()
+{
+  int numitems, maxitems, ptr;
+  char *colon_bit;
+  struct ign *p;
+  
+  char *this_fignore = get_string_value ("FIGNORE");
+
+  /* If nothing has changed then just exit now. */
+  if ((this_fignore &&
+       last_fignore &&
+       strcmp (this_fignore, last_fignore) == 0) ||
+      (!this_fignore && !last_fignore))
+    {
+      return;
+    }
+
+  /* Oops.  FIGNORE has changed.  Re-parse it. */
+  num_ignores = 0;
+
+  if (ignores)
+    {
+      for (p = ignores; p->val; p++) free(p->val);
+      free (ignores);
+      ignores = (struct ign*)NULL;
+    }
+
+  if (last_fignore)
+    {
+      free (last_fignore);
+      last_fignore = (char *)NULL;
+    }
+
+  if (!this_fignore || !*this_fignore)
+    return;
+
+  last_fignore = savestring (this_fignore);
+
+  numitems = maxitems = ptr = 0;
+
+  while (colon_bit = extract_colon_unit (this_fignore, &ptr))
+    {
+      if (numitems + 1 > maxitems)
+	ignores = (struct ign *)
+	  xrealloc (ignores, (maxitems += 10) * sizeof (struct ign));
+
+      ignores[numitems].val = colon_bit;
+      ignores[numitems].len = strlen (colon_bit);
+      numitems++;
+    }
+  ignores[numitems].val = NULL;
+  num_ignores = numitems;
+}
+
+static int
+name_is_acceptable (name)
+     char *name;
+{
+  struct ign *p;
+  int nlen = strlen (name);
+
+  for (p = ignores; p->val; p++) 
+    {
+      if (nlen > p->len && p->len > 0 && 
+	  strcmp (p->val, &name[nlen - p->len]) == 0)
+	return (0);
+    }
+
+  return (1);
+}
+
+/* Internal function to test whether filenames in NAMES should be
+   ignored.  NAME_FUNC is a pointer to a function to call with each
+   name.  It returns non-zero if the name is acceptable to the particular
+   ignore function which called _ignore_names; zero if the name should
+   be removed from NAMES. */
+static void
+_ignore_names (names, name_func)
+     char **names;
+     Function *name_func;
+{
+  char **newnames;
+  int idx, nidx;
+
+  /* If there is only one completion, see if it is acceptable.  If it is
+     not, free it up.  In any case, short-circuit and return.  This is a
+     special case because names[0] is not the prefix of the list of names
+     if there is only one completion; it is the completion itself. */
+  if (names[1] == (char *)0)
+    {
+      if ((*name_func) (names[0]) == 0)
+        {
+          free (names[0]);
+          names[0] = (char *)NULL;
+        }
+      return;
+    }
+
+  /* Allocate space for array to hold list of pointers to matching
+     filenames.  The pointers are copied back to NAMES when done. */
+  for (nidx = 1; names[nidx]; nidx++)
+    ;
+  newnames = (char **)xmalloc ((nidx + 1) * (sizeof (char *)));
+
+  newnames[0] = names[0];
+  for (idx = nidx = 1; names[idx]; idx++)
+    {
+      if ((*name_func) (names[idx]))
+	newnames[nidx++] = names[idx];
+      else
+	free (names[idx]);
+    }
+
+  newnames[nidx] = (char *)NULL;
+
+  /* If none are acceptable then let the completer handle it. */
+  if (nidx == 1)
+    {
+      free (names[0]);
+      names[0] = (char *)NULL;
+      free (newnames);
+      return;
+    }
+
+  /* If only one is acceptable, copy it to names[0] and return. */
+  if (nidx == 2)
+    {
+      free (names[0]);
+      names[0] = newnames[1];
+      names[1] = (char *)NULL;
+      free (newnames);
+      return;
+    }
+      
+  /* Copy the acceptable names back to NAMES, set the new array end,
+     and return. */
+  for (nidx = 1; newnames[nidx]; nidx++)
+    names[nidx] = newnames[nidx];
+  names[nidx] = (char *)NULL;
+}
+
+static void
+filename_completion_ignore (names)
+     char **names;
+{
+  setup_ignore_patterns ();
+
+  if (num_ignores == 0)
+    return;
+
+  _ignore_names (names, name_is_acceptable);
+}
+
+/* Return 1 if NAME is a directory. */
+static int
+test_for_directory (name)
+     char *name;
+{
+  struct stat finfo;
+  char *fn;
+
+  fn = tilde_expand (name);
+  if (stat (fn, &finfo) != 0)
+    {
+      free (fn);
+      return 0;
+    }
+  free (fn);
+  return (S_ISDIR (finfo.st_mode));
+}
+
+/* Remove files from NAMES, leaving directories. */
+static void
+bash_ignore_filenames (names)
+     char **names;
+{
+  _ignore_names (names, test_for_directory);
+}
+
+/* Handle symbolic link references and other directory name
+   expansions while hacking completion. */
+static int
+bash_directory_completion_hook (dirname)
+     char **dirname;
+{
+  char *local_dirname, *t;
+  int return_value = 0;
+  WORD_LIST *wl;
+
+  local_dirname = *dirname;
+  if (strchr (local_dirname, '$') || strchr (local_dirname, '`'))
+    {
+      wl = expand_string (local_dirname, 0);
+      if (wl)
+	{
+	  *dirname = string_list (wl);
+	  /* Tell the completer to replace the directory name only if we
+	     actually expanded something. */
+	  return_value = STREQ (local_dirname, *dirname) == 0;
+	  free (local_dirname);
+	  dispose_words (wl);
+	  local_dirname = *dirname;
+	}
+      else
+	{
+	  free (local_dirname);
+	  *dirname = savestring ("");
+	  return 1;
+	}
+    }
+
+  if (!no_symbolic_links && (local_dirname[0] != '.' || local_dirname[1]))
+    {
+      char *temp1, *temp2;
+      int len1, len2;
+
+      t = get_working_directory ("symlink-hook");
+      temp1 = make_absolute (local_dirname, t);
+      free (t);
+      temp2 = canonicalize_pathname (temp1);
+      len1 = strlen (temp1);
+      if (temp1[len1 - 1] == '/')
+        {
+	  len2 = strlen (temp2);
+          temp2 = xrealloc (temp2, len2 + 2);
+          temp2[len2] = '/';
+          temp2[len2 + 1] = '\0';
+        }
+      free (local_dirname);
+      *dirname = temp2;
+      free (temp1);
+    }
+  return (return_value);
+}
+
+#if defined (DYNAMIC_HISTORY_COMPLETION)
+static char **history_completion_array = (char **)NULL;
+static int harry_size = 0;
+static int harry_len = 0;
+
+static void
+build_history_completion_array ()
+{
+  register int i;
+
+  /* First, clear out the current dynamic history completion list. */
+  if (harry_size)
+    {
+      for (i = 0; history_completion_array[i]; i++)
+	free (history_completion_array[i]);
+
+      free (history_completion_array);
+
+      history_completion_array = (char **)NULL;
+      harry_size = 0;
+      harry_len = 0;
+    }
+
+  /* Next, grovel each line of history, making each shell-sized token
+     a separate entry in the history_completion_array. */
+  {
+    HIST_ENTRY **hlist;
+
+    hlist = history_list ();
+
+    if (hlist)
+      {
+	register int j;
+
+	for (i = 0; hlist[i]; i++)
+	  {
+	    char **tokens;
+
+	    /* Separate each token, and place into an array. */
+	    tokens = history_tokenize (hlist[i]->line);
+
+	    for (j = 0; tokens && tokens[j]; j++)
+	      {
+		if (harry_len + 2 > harry_size)
+		  history_completion_array = (char **) xrealloc
+		    (history_completion_array,
+		     (harry_size += 10) * sizeof (char *));
+
+		history_completion_array[harry_len++] = tokens[j];
+		history_completion_array[harry_len] = (char *)NULL;
+	      }
+	    free (tokens);
+	  }
+
+	/* Sort the complete list of tokens. */
+	qsort (history_completion_array, harry_len, sizeof (char *),
+	       (Function *)qsort_string_compare);
+
+	/* Instead of removing the duplicate entries here, we let the
+	   code in the completer handle it. */
+      }
+  }
+}
+
+static char *
+history_completion_generator (hint_text, state)
+     char *hint_text;
+     int state;
+{
+  static int local_index = 0;
+  static char *text = (char *)NULL;
+  static int len = 0;
+
+  /* If this is the first call to the generator, then initialize the
+     list of strings to complete over. */
+  if (!state)
+    {
+      local_index = 0;
+      build_history_completion_array ();
+      text = hint_text;
+      len = strlen (text);
+    }
+
+  while (history_completion_array && history_completion_array[local_index])
+    {
+      if (strncmp (text, history_completion_array[local_index++], len) == 0)
+	return (savestring (history_completion_array[local_index - 1]));
+    }
+  return ((char *)NULL);
+}
+
+static void
+dynamic_complete_history (count, key)
+     int count, key;
+{
+  Function *orig_func;
+  CPPFunction *orig_attempt_func;
+
+  orig_func = rl_completion_entry_function;
+  orig_attempt_func = rl_attempted_completion_function;
+  rl_completion_entry_function = (Function *)history_completion_generator;
+  rl_attempted_completion_function = (CPPFunction *)NULL;
+
+  if (rl_last_func == (Function *)dynamic_complete_history)
+    rl_complete_internal ('?');
+  else
+    rl_complete_internal (TAB);
+
+  rl_completion_entry_function = orig_func;
+  rl_attempted_completion_function = orig_attempt_func;
+}
+
+#endif /* DYNAMIC_HISTORY_COMPLETION */
+
+#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
+static void
+bash_complete_username (ignore, ignore2)
+     int ignore, ignore2;
+{
+  bash_complete_username_internal (TAB);
+}
+
+static void
+bash_possible_username_completions (ignore, ignore2)
+     int ignore, ignore2;
+{
+  bash_complete_username_internal ('?');
+}
+
+static void
+bash_complete_username_internal (what_to_do)
+     int what_to_do;
+{
+  bash_specific_completion
+    (what_to_do, (Function *)username_completion_function);
+}
+
+static void
+bash_complete_filename (ignore, ignore2)
+     int ignore, ignore2;
+{
+  bash_complete_filename_internal (TAB);
+}
+
+static void
+bash_possible_filename_completions (ignore, ignore2)
+     int ignore, ignore2;
+{
+  bash_complete_filename_internal ('?');
+}
+
+static void
+bash_complete_filename_internal (what_to_do)
+     int what_to_do;
+{
+  Function  *orig_func, *orig_dir_func;
+  CPPFunction *orig_attempt_func;
+  char *orig_rl_completer_word_break_characters;
+
+  orig_func = rl_completion_entry_function;
+  orig_attempt_func = rl_attempted_completion_function;
+  orig_dir_func = rl_directory_completion_hook;
+  orig_rl_completer_word_break_characters = rl_completer_word_break_characters;
+  rl_completion_entry_function = (Function *)filename_completion_function;
+  rl_attempted_completion_function = (CPPFunction *)NULL;
+  rl_directory_completion_hook = (Function *)NULL;
+  rl_completer_word_break_characters = " \t\n\"\'";
+
+  rl_complete_internal (what_to_do);
+
+  rl_completion_entry_function = orig_func;
+  rl_attempted_completion_function = orig_attempt_func;
+  rl_directory_completion_hook = orig_dir_func;
+  rl_completer_word_break_characters = orig_rl_completer_word_break_characters;
+}
+
+static void
+bash_complete_hostname (ignore, ignore2)
+     int ignore, ignore2;
+{
+  bash_complete_hostname_internal (TAB);
+}
+
+static void
+bash_possible_hostname_completions (ignore, ignore2)
+     int ignore, ignore2;
+{
+  bash_complete_hostname_internal ('?');
+}
+
+static void
+bash_complete_variable (ignore, ignore2)
+     int ignore, ignore2;
+{
+  bash_complete_variable_internal (TAB);
+}
+
+static void
+bash_possible_variable_completions (ignore, ignore2)
+     int ignore, ignore2;
+{
+  bash_complete_variable_internal ('?');
+}
+
+static void
+bash_complete_command (ignore, ignore2)
+     int ignore, ignore2;
+{
+  bash_complete_command_internal (TAB);
+}
+
+static void
+bash_possible_command_completions (ignore, ignore2)
+     int ignore, ignore2;
+{
+  bash_complete_command_internal ('?');
+}
+
+static void
+bash_complete_hostname_internal (what_to_do)
+     int what_to_do;
+{
+  bash_specific_completion
+    (what_to_do, (Function *)hostname_completion_function);
+}
+
+static void
+bash_complete_variable_internal (what_to_do)
+     int what_to_do;
+{
+  bash_specific_completion
+    (what_to_do, (Function *)variable_completion_function);
+}
+
+static void
+bash_complete_command_internal (what_to_do)
+     int what_to_do;
+{
+  bash_specific_completion
+    (what_to_do, (Function *)command_word_completion_function);
+}
+
+static void
+bash_specific_completion (what_to_do, generator)
+     int what_to_do;
+     Function *generator;
+{
+  Function *orig_func;
+  CPPFunction *orig_attempt_func;
+
+  orig_func = rl_completion_entry_function;
+  orig_attempt_func = rl_attempted_completion_function;
+  rl_completion_entry_function = generator;
+  rl_attempted_completion_function = (CPPFunction *)NULL;
+
+  rl_complete_internal (what_to_do);
+
+  rl_completion_entry_function = orig_func;
+  rl_attempted_completion_function = orig_attempt_func;
+}
+
+#endif	/* SPECIFIC_COMPLETION_FUNCTIONS */