glob_filename (pathname, flags)
     char *pathname;
     int flags;
{
  char **result, **new_result;
  unsigned int result_size;
  char *directory_name, *filename, *dname, *fn;
  unsigned int directory_len;
  int free_dirname;			/* flag */
  int dflags, hasglob;

  result = (char **) malloc (sizeof (char *));
  result_size = 1;
  if (result == NULL)
    return (NULL);

  result[0] = NULL;

  directory_name = NULL;

  /* Find the filename.  */
  filename = strrchr (pathname, '/');
#if defined (EXTENDED_GLOB)
  if (filename && extended_glob)
    {
      fn = glob_dirscan (pathname, '/');
#if DEBUG_MATCHING
      if (fn != filename)
	fprintf (stderr, "glob_filename: glob_dirscan: fn (%s) != filename (%s)\n", fn ? fn : "(null)", filename);
#endif
      filename = fn;
    }
#endif

  if (filename == NULL)
    {
      filename = pathname;
      directory_name = "";
      directory_len = 0;
      free_dirname = 0;
    }
  else
    {
      directory_len = (filename - pathname) + 1;
      directory_name = (char *) malloc (directory_len + 1);

      if (directory_name == 0)		/* allocation failed? */
	{
	  free (result);
	  return (NULL);
	}

      bcopy (pathname, directory_name, directory_len);
      directory_name[directory_len] = '\0';
      ++filename;
      free_dirname = 1;
    }

  hasglob = 0;
  /* If directory_name contains globbing characters, then we
     have to expand the previous levels.  Just recurse.
     If glob_pattern_p returns != [0,1] we have a pattern that has backslash
     quotes but no unquoted glob pattern characters. We dequote it below. */
  if (directory_len > 0 && (hasglob = glob_pattern_p (directory_name)) == 1)
    {
      char **directories, *d, *p;
      register unsigned int i;
      int all_starstar, last_starstar;

      all_starstar = last_starstar = 0;
      d = directory_name;
      dflags = flags & ~GX_MARKDIRS;
      /* Collapse a sequence of ** patterns separated by one or more slashes
	 to a single ** terminated by a slash or NUL */
      if ((flags & GX_GLOBSTAR) && d[0] == '*' && d[1] == '*' && (d[2] == '/' || d[2] == '\0'))
	{
	  p = d;
	  while (d[0] == '*' && d[1] == '*' && (d[2] == '/' || d[2] == '\0'))
	    {
	      p = d;
	      if (d[2])
		{
		  d += 3;
		  while (*d == '/')
		    d++;
		  if (*d == 0)
		    break;
		}
	    }
	  if (*d == 0)
	    all_starstar = 1;
	  d = p;
	  dflags |= GX_ALLDIRS|GX_ADDCURDIR;
	  directory_len = strlen (d);
	}

      /* If there is a non [star][star]/ component in directory_name, we
	 still need to collapse trailing sequences of [star][star]/ into
	 a single one and note that the directory name ends with [star][star],
	 so we can compensate if filename is [star][star] */
      if ((flags & GX_GLOBSTAR) && all_starstar == 0)
	{
	  int dl, prev;
	  prev = dl = directory_len;
	  while (dl >= 4 && d[dl - 1] == '/' &&
			   d[dl - 2] == '*' &&
			   d[dl - 3] == '*' &&
			   d[dl - 4] == '/')
	    prev = dl, dl -= 3;
	  if (dl != directory_len)
	    last_starstar = 1;
	  directory_len = prev;
	}

      /* If the directory name ends in [star][star]/ but the filename is
	 [star][star], just remove the final [star][star] from the directory
	 so we don't have to scan everything twice. */
      if (last_starstar && directory_len > 4 &&
	    filename[0] == '*' && filename[1] == '*' && filename[2] == 0)
	{
	  directory_len -= 3;
	}

      if (d[directory_len - 1] == '/')
	d[directory_len - 1] = '\0';

      directories = glob_filename (d, dflags|GX_RECURSE);

      if (free_dirname)
	{
	  free (directory_name);
	  directory_name = NULL;
	}

      if (directories == NULL)
	goto memory_error;
      else if (directories == (char **)&glob_error_return)
	{
	  free ((char *) result);
	  return ((char **) &glob_error_return);
	}
      else if (*directories == NULL)
	{
	  free ((char *) directories);
	  free ((char *) result);
	  return ((char **) &glob_error_return);
	}

      /* If we have something like [star][star]/[star][star], it's no use to
         glob **, then do it again, and throw half the results away.  */
      if (all_starstar && filename[0] == '*' && filename[1] == '*' && filename[2] == 0)
	{
	  free ((char *) directories);
	  free (directory_name);
	  directory_name = NULL;
	  directory_len = 0;
	  goto only_filename;
	}

      /* We have successfully globbed the preceding directory name.
	 For each name in DIRECTORIES, call glob_vector on it and
	 FILENAME.  Concatenate the results together.  */
      for (i = 0; directories[i] != NULL; ++i)
	{
	  char **temp_results;
	  int shouldbreak;

	  shouldbreak = 0;
	  /* XXX -- we've recursively scanned any directories resulting from
	     a `**', so turn off the flag.  We turn it on again below if
	     filename is `**' */
	  /* Scan directory even on a NULL filename.  That way, `*h/'
	     returns only directories ending in `h', instead of all
	     files ending in `h' with a `/' appended. */
	  dname = directories[i];
	  dflags = flags & ~(GX_MARKDIRS|GX_ALLDIRS|GX_ADDCURDIR);
	  /* last_starstar? */
	  if ((flags & GX_GLOBSTAR) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0')
	    dflags |= GX_ALLDIRS|GX_ADDCURDIR;
	  if (dname[0] == '\0' && filename[0])
	    {
	      dflags |= GX_NULLDIR;
	      dname = ".";	/* treat null directory name and non-null filename as current directory */
	    }

	  /* Special handling for symlinks to directories with globstar on */
	  if (all_starstar && (dflags & GX_NULLDIR) == 0)
	    {
	      int dlen;

	      /* If we have a directory name that is not null (GX_NULLDIR above)
		 and is a symlink to a directory, we return the symlink if
		 we're not `descending' into it (filename[0] == 0) and return
		 glob_error_return (which causes the code below to skip the
		 name) otherwise. I should fold this into a test that does both
		 checks instead of calling stat twice. */
	      if (glob_testdir (dname, flags|GX_ALLDIRS) == -2 && glob_testdir (dname, 0) == 0)
		{
		  if (filename[0] != 0)
		    temp_results = (char **)&glob_error_return;		/* skip */
		  else
		    {
		      /* Construct array to pass to glob_dir_to_array */
		      temp_results = (char **)malloc (2 * sizeof (char *));
		      if (temp_results == NULL)
			goto memory_error;
		      temp_results[0] = (char *)malloc (1);
		      if (temp_results[0] == 0)
			{
			  free (temp_results);
			  goto memory_error;
			}
		      **temp_results = '\0';
		      temp_results[1] = NULL;
		      dflags |= GX_SYMLINK;	/* mostly for debugging */
		    }
		}
	      else
		temp_results = glob_vector (filename, dname, dflags);
	    }
	  else
	    temp_results = glob_vector (filename, dname, dflags);

	  /* Handle error cases. */
	  if (temp_results == NULL)
	    goto memory_error;
	  else if (temp_results == (char **)&glob_error_return)
	    /* This filename is probably not a directory.  Ignore it.  */
	    ;
	  else
	    {
	      char **array;
	      register unsigned int l;

	      /* If we're expanding **, we don't need to glue the directory
		 name to the results; we've already done it in glob_vector */
	      if ((dflags & GX_ALLDIRS) && filename[0] == '*' && filename[1] == '*' && (filename[2] == '\0' || filename[2] == '/'))
		{
		  /* When do we remove null elements from temp_results?  And
		     how to avoid duplicate elements in the final result? */
		  /* If (dflags & GX_NULLDIR) glob_filename potentially left a
		     NULL placeholder in the temp results just in case
		     glob_vector/glob_dir_to_array did something with it, but
		     if it didn't, and we're not supposed to be passing them
		     through for some reason ((flags & GX_NULLDIR) == 0) we
		     need to remove all the NULL elements from the beginning
		     of TEMP_RESULTS. */
		  /* If we have a null directory name and ** as the filename,
		     we have just searched for everything from the current
		     directory on down. Break now (shouldbreak = 1) to avoid
		     duplicate entries in the final result. */
#define NULL_PLACEHOLDER(x)	((x) && *(x) && **(x) == 0)
		  if ((dflags & GX_NULLDIR) && (flags & GX_NULLDIR) == 0 &&
			NULL_PLACEHOLDER (temp_results))
#undef NULL_PLACEHOLDER
		    {
		      register int i, n;
		      for (n = 0; temp_results[n] && *temp_results[n] == 0; n++)
			;
		      i = n;
		      do
			temp_results[i - n] = temp_results[i];
		      while (temp_results[i++] != 0);
		      array = temp_results;
		      shouldbreak = 1;
		    }
	          else
		    array = temp_results;
		}
	      else if (dflags & GX_SYMLINK)
		array = glob_dir_to_array (directories[i], temp_results, flags);
	      else
		array = glob_dir_to_array (directories[i], temp_results, flags);
	      l = 0;
	      while (array[l] != NULL)
		++l;

	      new_result = (char **)realloc (result, (result_size + l) * sizeof (char *));

	      if (new_result == NULL)
		{
		  for (l = 0; array[l]; ++l)
		    free (array[l]);
		  free ((char *)array);
		  goto memory_error;
		}
	      result = new_result;

	      for (l = 0; array[l] != NULL; ++l)
		result[result_size++ - 1] = array[l];

	      result[result_size - 1] = NULL;

	      /* Note that the elements of ARRAY are not freed.  */
	      if (array != temp_results)
		free ((char *) array);
	      else if ((dflags & GX_ALLDIRS) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0')
		free (temp_results);	/* expanding ** case above */

	      if (shouldbreak)
		break;
	    }
	}
      /* Free the directories.  */
      for (i = 0; directories[i]; i++)
	free (directories[i]);

      free ((char *) directories);

      return (result);
    }

only_filename:
  /* If there is only a directory name, return it. */
  if (*filename == '\0')
    {
      result = (char **) realloc ((char *) result, 2 * sizeof (char *));
      if (result == NULL)
	{
	  if (free_dirname)
	    free (directory_name);
	  return (NULL);
	}
      /* If we have a directory name with quoted characters, and we are
	 being called recursively to glob the directory portion of a pathname,
	 we need to dequote the directory name before returning it so the
	 caller can read the directory */
      if (directory_len > 0 && hasglob == 2 && (flags & GX_RECURSE) != 0)
	{
	  dequote_pathname (directory_name);
	  directory_len = strlen (directory_name);
	}

      /* We could check whether or not the dequoted directory_name is a
	 directory and return it here, returning the original directory_name
	 if not, but we don't do that. We do return the dequoted directory
	 name if we're not being called recursively and the dequoted name
	 corresponds to an actual directory. For better backwards compatibility,
	 we can return &glob_error_return unconditionally in this case. */

      if (directory_len > 0 && hasglob == 2 && (flags & GX_RECURSE) == 0)
	{
#if 1
	  dequote_pathname (directory_name);
	  if (glob_testdir (directory_name, 0) < 0)
	    {
	      if (free_dirname)
		free (directory_name);
	      return ((char **)&glob_error_return);
	    }
#else
	  return ((char **)&glob_error_return);
#endif
	}

      /* Handle GX_MARKDIRS here. */
      result[0] = (char *) malloc (directory_len + 1);
      if (result[0] == NULL)
	goto memory_error;
      bcopy (directory_name, result[0], directory_len + 1);
      if (free_dirname)
	free (directory_name);
      result[1] = NULL;
      return (result);
    }
  else
    {
      char **temp_results;

      /* There are no unquoted globbing characters in DIRECTORY_NAME.
	 Dequote it before we try to open the directory since there may
	 be quoted globbing characters which should be treated verbatim. */
      if (directory_len > 0)
	dequote_pathname (directory_name);

      /* We allocated a small array called RESULT, which we won't be using.
	 Free that memory now. */
      free (result);

      /* Just return what glob_vector () returns appended to the
	 directory name. */
      /* If flags & GX_ALLDIRS, we're called recursively */
      dflags = flags & ~GX_MARKDIRS;
      if (directory_len == 0)
	dflags |= GX_NULLDIR;
      if ((flags & GX_GLOBSTAR) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0')
	{
	  dflags |= GX_ALLDIRS|GX_ADDCURDIR;
#if 0
	  /* If we want all directories (dflags & GX_ALLDIRS) and we're not
	     being called recursively as something like `echo [star][star]/[star].o'
	     ((flags & GX_ALLDIRS) == 0), we want to prevent glob_vector from
	     adding a null directory name to the front of the temp_results
	     array.  We turn off ADDCURDIR if not called recursively and
	     dlen == 0 */
#endif
	  if (directory_len == 0 && (flags & GX_ALLDIRS) == 0)
	    dflags &= ~GX_ADDCURDIR;
	}
      temp_results = glob_vector (filename,
				  (directory_len == 0 ? "." : directory_name),
				  dflags);

      if (temp_results == NULL || temp_results == (char **)&glob_error_return)
	{
	  if (free_dirname)
	    free (directory_name);
	  QUIT;			/* XXX - shell */
	  run_pending_traps ();
	  return (temp_results);
	}

      result = glob_dir_to_array ((dflags & GX_ALLDIRS) ? "" : directory_name, temp_results, flags);

      if (free_dirname)
	free (directory_name);
      return (result);
    }

  /* We get to memory_error if the program has run out of memory, or
     if this is the shell, and we have been interrupted. */
 memory_error:
  if (result != NULL)
    {
      register unsigned int i;
      for (i = 0; result[i] != NULL; ++i)
	free (result[i]);
      free ((char *) result);
    }

  if (free_dirname && directory_name)
    free (directory_name);

  QUIT;
  run_pending_traps ();

  return (NULL);
}


bash_dequote_filename (text, quote_char)
     char *text;
     int quote_char;
{
  char *ret, *p, *r;
  int l, quoted;

  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 == '\\')
	{
	  /* 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))
	    *r++ = *p;

	  *r++ = *++p;
	  if (*p == '\0')
	    return ret;		/* XXX - was break; */
	  continue;
	}
      /* Close quote. */
      if (quoted && *p == quoted)
	{
	  quoted = 0;
	  continue;
	}
      /* Open quote. */
      if (quoted == 0 && (*p == '\'' || *p == '"'))
	{
	  quoted = *p;
	  continue;
	}
      *r++ = *p;
    }
  *r = '\0';
  return ret;
}

An issue was discovered in disable_priv_mode in shell.c in GNU Bash through 5.0 patch 11. By default, if Bash is run with its effective UID not equal to its real UID, it will drop privileges by setting its effective UID to its real UID. However, it does so incorrectly. On Linux and other systems that support "saved UID" functionality, the saved UID is not dropped. An attacker with command execution in the shell can use "enable -f" for runtime loading of a new builtin, which can be a shared object that calls setuid() and therefore regains privileges. However, binaries running with an effective UID of 0 are unaffected.


set_bash_input ()
{
  /* Make sure the fd from which we are reading input is not in
     no-delay mode. */
#if defined (BUFFERED_INPUT)
  if (interactive == 0)
    sh_unset_nodelay_mode (default_buffered_input);
  else
#endif /* !BUFFERED_INPUT */
    sh_unset_nodelay_mode (fileno (stdin));

  /* with_input_from_stdin really means `with_input_from_readline' */
  if (interactive && no_line_editing == 0)
    with_input_from_stdin ();
#if defined (BUFFERED_INPUT)
  else if (interactive == 0)
    with_input_from_buffered_stream (default_buffered_input, dollar_vars[0]);
#endif /* BUFFERED_INPUT */
  else
    with_input_from_stream (default_input, dollar_vars[0]);
}


initialize_readline ()
{
  rl_command_func_t *func;
  char kseq[2];

  if (bash_readline_initialized)
    return;

  rl_terminal_name = get_string_value ("TERM");
  rl_instream = stdin;
  rl_outstream = stderr;

  /* Allow conditional parsing of the ~/.inputrc file. */
  rl_readline_name = "Bash";

  /* Add bindable names before calling rl_initialize so they may be
     referenced in the various inputrc files. */
  rl_add_defun ("shell-expand-line", shell_expand_line, -1);
#ifdef BANG_HISTORY
  rl_add_defun ("history-expand-line", history_expand_line, -1);
  rl_add_defun ("magic-space", tcsh_magic_space, -1);
#endif

  rl_add_defun ("shell-forward-word", bash_forward_shellword, -1);
  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);

#ifdef ALIAS
  rl_add_defun ("alias-expand-line", alias_expand_line, -1);
#  ifdef BANG_HISTORY
  rl_add_defun ("history-and-alias-expand-line", history_and_alias_expand_line, -1);
#  endif
#endif

  /* 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 (BRACE_COMPLETION)
  rl_add_defun ("complete-into-braces", bash_brace_completion, -1);
#endif

#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
  rl_add_defun ("complete-filename", bash_complete_filename, -1);
  rl_add_defun ("possible-filename-completions", bash_possible_filename_completions, -1);
  rl_add_defun ("complete-username", bash_complete_username, -1);
  rl_add_defun ("possible-username-completions", bash_possible_username_completions, -1);
  rl_add_defun ("complete-hostname", bash_complete_hostname, -1);
  rl_add_defun ("possible-hostname-completions", bash_possible_hostname_completions, -1);
  rl_add_defun ("complete-variable", bash_complete_variable, -1);
  rl_add_defun ("possible-variable-completions", bash_possible_variable_completions, -1);
  rl_add_defun ("complete-command", bash_complete_command, -1);
  rl_add_defun ("possible-command-completions", bash_possible_command_completions, -1);
  rl_add_defun ("glob-complete-word", bash_glob_complete_word, -1);
  rl_add_defun ("glob-expand-word", bash_glob_expand_word, -1);
  rl_add_defun ("glob-list-expansions", bash_glob_list_expansions, -1);
#endif

  rl_add_defun ("dynamic-complete-history", dynamic_complete_history, -1);
  rl_add_defun ("dabbrev-expand", bash_dabbrev_expand, -1);

  /* Bind defaults before binding our custom shell keybindings. */
  if (RL_ISSTATE(RL_STATE_INITIALIZED) == 0)
    rl_initialize ();

  /* Bind up our special shell functions. */
  rl_bind_key_if_unbound_in_map (CTRL('E'), shell_expand_line, emacs_meta_keymap);

#ifdef BANG_HISTORY
  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]",
     so it is not necessary to allow C-M-j for context switching.  Turn
     off this occasionally confusing behaviour. */
  kseq[0] = CTRL('J');
  kseq[1] = '\0';
  func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
  if (func == rl_vi_editing_mode)
    rl_unbind_key_in_map (CTRL('J'), emacs_meta_keymap);
  kseq[0] = CTRL('M');
  func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
  if (func == rl_vi_editing_mode)
    rl_unbind_key_in_map (CTRL('M'), emacs_meta_keymap);
#if defined (VI_MODE)
  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)
  rl_bind_key_if_unbound_in_map ('{', bash_brace_completion, emacs_meta_keymap); /*}*/
#endif /* BRACE_COMPLETION */

#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
  rl_bind_key_if_unbound_in_map ('/', bash_complete_filename, emacs_meta_keymap);
  rl_bind_key_if_unbound_in_map ('/', bash_possible_filename_completions, emacs_ctlx_keymap);

  /* Have to jump through hoops here because there is a default binding for
     M-~ (rl_tilde_expand) */
  kseq[0] = '~';
  kseq[1] = '\0';
  func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
  if (func == 0 || func == rl_tilde_expand)
    rl_bind_keyseq_in_map (kseq, bash_complete_username, emacs_meta_keymap);

  rl_bind_key_if_unbound_in_map ('~', bash_possible_username_completions, emacs_ctlx_keymap);

  rl_bind_key_if_unbound_in_map ('@', bash_complete_hostname, emacs_meta_keymap);
  rl_bind_key_if_unbound_in_map ('@', bash_possible_hostname_completions, emacs_ctlx_keymap);

  rl_bind_key_if_unbound_in_map ('$', bash_complete_variable, emacs_meta_keymap);
  rl_bind_key_if_unbound_in_map ('$', bash_possible_variable_completions, emacs_ctlx_keymap);

  rl_bind_key_if_unbound_in_map ('!', bash_complete_command, emacs_meta_keymap);
  rl_bind_key_if_unbound_in_map ('!', bash_possible_command_completions, emacs_ctlx_keymap);

  rl_bind_key_if_unbound_in_map ('g', bash_glob_complete_word, emacs_meta_keymap);
  rl_bind_key_if_unbound_in_map ('*', bash_glob_expand_word, emacs_ctlx_keymap);
  rl_bind_key_if_unbound_in_map ('g', bash_glob_list_expansions, emacs_ctlx_keymap);

#endif /* SPECIFIC_COMPLETION_FUNCTIONS */

  kseq[0] = TAB;
  kseq[1] = '\0';
  func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
  if (func == 0 || func == rl_tab_insert)
    rl_bind_key_in_map (TAB, dynamic_complete_history, emacs_meta_keymap);

  /* 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;

  /* Tell the filename completer we want a chance to ignore some names. */
  rl_ignore_some_completions_function = filename_completion_ignore;

  /* Bind C-xC-e to invoke emacs and run result as commands. */
  rl_bind_key_if_unbound_in_map (CTRL ('E'), emacs_edit_and_execute_command, emacs_ctlx_keymap);
#if defined (VI_MODE)
  rl_bind_key_if_unbound_in_map ('v', vi_edit_and_execute_command, vi_movement_keymap);
#  if defined (ALIAS)
  rl_bind_key_if_unbound_in_map ('@', posix_edit_macros, vi_movement_keymap);
#  endif

  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);
#endif

  rl_completer_quote_characters = "'\"";

  /* This sets rl_completer_word_break_characters and rl_special_prefixes
     to the appropriate values, depending on whether or not hostname
     completion is enabled. */
  enable_hostname_completion (perform_hostname_completion);

  /* characters that need to be quoted when appearing in filenames. */
  rl_filename_quote_characters = default_filename_quote_characters;
  set_filename_bstab (rl_filename_quote_characters);

  rl_filename_quoting_function = bash_quote_filename;
  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()
     should already have called posix_readline_initialize() when
     posixly_correct was set. */
  if (posixly_correct)
    posix_readline_initialize (1);
#endif

  bash_readline_initialized = 1;
}


split_ignorespec (s, ip)
     char *s;
     int *ip;
{
  char *t;
  int n, i;

  if (s == 0)
    return 0;

  i = *ip;
  if (s[i] == 0)
    return 0;

  n = skip_to_delim (s, i, ":", SD_NOJMP|SD_EXTGLOB|SD_GLOB);
  t = substring (s, i, n);

  if (s[n] == ':')
    n++;
  *ip = n;
  return t;
}


uidget ()
{
  uid_t u;

  u = getuid ();
  if (current_user.uid != u)
    {
      FREE (current_user.user_name);
      FREE (current_user.shell);
      FREE (current_user.home_dir);
      current_user.user_name = current_user.shell = current_user.home_dir = (char *)NULL;
    }
  current_user.uid = u;
  current_user.gid = getgid ();
  current_user.euid = geteuid ();
  current_user.egid = getegid ();

  /* See whether or not we are running setuid or setgid. */
  return (current_user.uid != current_user.euid) ||
	   (current_user.gid != current_user.egid);
}


start_debugger ()
{
#if defined (DEBUGGER) && defined (DEBUGGER_START_FILE)
  int old_errexit;
  int r;

  old_errexit = exit_immediately_on_error;
  exit_immediately_on_error = 0;

  r = force_execute_file (DEBUGGER_START_FILE, 1);
  if (r < 0)
    {
      internal_warning (_("cannot start debugger; debugging mode disabled"));
      debugging_mode = 0;
    }
  error_trace_mode = function_trace_mode = debugging_mode;

  set_shellopts ();
  set_bashopts ();

  exit_immediately_on_error += old_errexit;
#endif
}


edit_and_execute_command (count, c, editing_mode, edit_command)
     int count, c, editing_mode;
     char *edit_command;
{
  char *command, *metaval;
  int r, rrs, metaflag;
  sh_parser_state_t ps;

  rrs = rl_readline_state;
  saved_command_line_count = current_command_line_count;

  /* Accept the current line. */
  rl_newline (1, c);

  if (rl_explicit_arg)
    {
      command = (char *)xmalloc (strlen (edit_command) + 8);
      sprintf (command, "%s %d", 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'). */
      /* 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 ();
      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++;
      using_history ();
      command = savestring (edit_command);
    }

  metaval = rl_variable_value ("input-meta");
  metaflag = RL_BOOLEAN_VARIABLE_VALUE (metaval);

  if (rl_deprep_term_function)
    (*rl_deprep_term_function) ();
  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 (rl_prep_term_function)
    (*rl_prep_term_function) (metaflag);

  current_command_line_count = saved_command_line_count;

  /* Now erase the contents of the current line and undo the effects of the
     rl_accept_line() above.  We don't even want to make the text we just
     executed available for undoing. */
  rl_line_buffer[0] = '\0';	/* XXX */
  rl_point = rl_end = 0;
  rl_done = 0;
  rl_readline_state = rrs;

#if defined (VI_MODE)
  if (editing_mode == VI_EDITING_MODE)
    rl_vi_insertion_mode (1, c);
#endif

  rl_forced_update_display ();

  return r;
}


_cygwin32_check_tmp ()
{
  struct stat sb;

  if (stat ("/tmp", &sb) < 0)
    internal_warning (_("could not find /tmp, please create!"));
  else
    {
      if (S_ISDIR (sb.st_mode) == 0)
	internal_warning (_("/tmp must be a valid directory name"));
    }
}


shell_reinitialize ()
{
  /* The default shell prompts. */
  primary_prompt = PPROMPT;
  secondary_prompt = SPROMPT;

  /* Things that get 1. */
  current_command_number = 1;

  /* We have decided that the ~/.bashrc file should not be executed
     for the invocation of each shell script.  If the variable $ENV
     (or $BASH_ENV) is set, its value is used as the name of a file
     to source. */
  no_rc = no_profile = 1;

  /* Things that get 0. */
  login_shell = make_login_shell = interactive = executing = 0;
  debugging = do_version = line_number = last_command_exit_value = 0;
  forced_interactive = interactive_shell = 0;
  subshell_environment = running_in_background = 0;
  expand_aliases = 0;
  bash_argv_initialized = 0;

  /* XXX - should we set jobs_m_flag to 0 here? */

#if defined (HISTORY)
  bash_history_reinit (enable_history_list = 0);
#endif /* HISTORY */

#if defined (RESTRICTED_SHELL)
  restricted = 0;
#endif /* RESTRICTED_SHELL */

  /* Ensure that the default startup file is used.  (Except that we don't
     execute this file for reinitialized shells). */
  bashrc_file = DEFAULT_BASHRC;

  /* Delete all variables and functions.  They will be reinitialized when
     the environment is parsed. */
  delete_all_contexts (shell_variables);
  delete_all_variables (shell_functions);

  reinit_special_variables ();

#if defined (READLINE)
  bashline_reinitialize ();
#endif

  shell_reinitialized = 1;
}


bash_kill_shellword (count, key)
     int count, key;
{
  int p;

  if (count < 0)
    return (bash_backward_kill_shellword (-count, key));

  p = rl_point;
  bash_forward_shellword (count, key);

  if (rl_point != p)
    rl_kill_text (p, rl_point);

  rl_point = p;
  if (rl_editing_mode == EMACS_EDITING_MODE)	/* 1 == emacs_mode */
    rl_mark = rl_point;

  return 0;
}


shell_initialize ()
{
  char hostname[256];
  int should_be_restricted;

  /* Line buffer output for stderr and stdout. */
  if (shell_initialized == 0)
    {
      sh_setlinebuf (stderr);
      sh_setlinebuf (stdout);
    }

  /* Sort the array of shell builtins so that the binary search in
     find_shell_builtin () works correctly. */
  initialize_shell_builtins ();

  /* Initialize the trap signal handlers before installing our own
     signal handlers.  traps.c:restore_original_signals () is responsible
     for restoring the original default signal handlers.  That function
     is called when we make a new child. */
  initialize_traps ();
  initialize_signals (0);

  /* It's highly unlikely that this will change. */
  if (current_host_name == 0)
    {
      /* Initialize current_host_name. */
      if (gethostname (hostname, 255) < 0)
	current_host_name = "??host??";
      else
	current_host_name = savestring (hostname);
    }

  /* Initialize the stuff in current_user that comes from the password
     file.  We don't need to do this right away if the shell is not
     interactive. */
  if (interactive_shell)
    get_current_user_info ();

  /* Initialize our interface to the tilde expander. */
  tilde_initialize ();

#if defined (RESTRICTED_SHELL)
  should_be_restricted = shell_is_restricted (shell_name);
#endif

  /* Initialize internal and environment variables.  Don't import shell
     functions from the environment if we are running in privileged or
     restricted mode or if the shell is running setuid. */
#if defined (RESTRICTED_SHELL)
  initialize_shell_variables (shell_environment, privileged_mode||restricted||should_be_restricted||running_setuid);
#else
  initialize_shell_variables (shell_environment, privileged_mode||running_setuid);
#endif

  /* Initialize the data structures for storing and running jobs. */
  initialize_job_control (jobs_m_flag);

  /* Initialize input streams to null. */
  initialize_bash_input ();

  initialize_flags ();

  /* Initialize the shell options.  Don't import the shell options
     from the environment variables $SHELLOPTS or $BASHOPTS if we are
     running in privileged or restricted mode or if the shell is running
     setuid. */
#if defined (RESTRICTED_SHELL)
  initialize_shell_options (privileged_mode||restricted||should_be_restricted||running_setuid);
  initialize_bashopts (privileged_mode||restricted||should_be_restricted||running_setuid);
#else
  initialize_shell_options (privileged_mode||running_setuid);
  initialize_bashopts (privileged_mode||running_setuid);
#endif
}


bash_default_completion (text, start, end, qc, compflags)
     const char *text;
     int start, end, qc, compflags;
{
  char **matches, *t;

  matches = (char **)NULL;

  /* New posix-style command substitution or variable name? */
  if (*text == '$')
    {
      if (qc != '\'' && text[1] == '(') /* ) */
	matches = rl_completion_matches (text, command_subst_completion_function);
      else
	{
	  matches = rl_completion_matches (text, variable_completion_function);
	  /* If a single match, see if it expands to a directory name and append
	     a slash if it does.  This requires us to expand the variable name,
	     so we don't want to display errors if the variable is unset.  This
	     can happen with dynamic variables whose value has never been
	     requested. */
	  if (matches && matches[0] && matches[1] == 0)
	    {
	      t = savestring (matches[0]);
	      bash_filename_stat_hook (&t);
	      /* doesn't use test_for_directory because that performs tilde
		 expansion */
	      if (file_isdir (t))
		rl_completion_append_character = '/';
	      free (t);
	    }
	}
    }

  /* If the word starts in `~', and there is no slash in the word, then
     try completing this word as a username. */
  if (matches == 0 && *text == '~' && mbschr (text, '/') == 0)
    matches = rl_completion_matches (text, rl_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 == 0 && perform_hostname_completion && *text == '@')
    matches = rl_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 == 0 && (compflags & DEFCOMP_CMDPOS))
    {
      /* If END == START and text[0] == 0, we are trying to complete an empty
	 command word. */
      if (no_empty_command_completion && end == start && text[0] == '\0')
	{
	  matches = (char **)NULL;
	  rl_ignore_some_completions_function = bash_ignore_everything;
	}
      else
	{
#define CMD_IS_DIR(x)	(absolute_pathname(x) == 0 && absolute_program(x) == 0 && *(x) != '~' && test_for_directory (x))

	  dot_in_path = 0;
	  matches = rl_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 == (char **)NULL)
	    rl_ignore_some_completions_function = bash_ignore_filenames;
	  else if (matches[1] == 0 && CMD_IS_DIR(matches[0]) && dot_in_path == 0)
	    /* If we found a single match, without looking in the current
	       directory (because it's not in $PATH), but the found name is
	       also a command in the current directory, suppress appending any
	       terminating character, since it's ambiguous. */
	    {
	      rl_completion_suppress_append = 1;
	      rl_filename_completion_desired = 0;
	    }
	  else if (matches[0] && matches[1] && STREQ (matches[0], matches[1]) && CMD_IS_DIR (matches[0]))
	    /* 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
	       will distill them all down to one.  We turn on
	       rl_completion_suppress_append for the same reason as above.
	       Remember: we only care if there's eventually a single unique
	       completion.  If there are multiple completions this won't
	       make a difference and the problem won't occur. */
	    {
	      rl_completion_suppress_append = 1;
	      rl_filename_completion_desired = 0;
	    }
	}
    }

  /* This could be a globbing pattern, so try to expand it using pathname
     expansion. */
  if (!matches && completion_glob_pattern ((char *)text))
    {
      matches = rl_completion_matches (text, glob_complete_word);
      /* A glob expression that matches more than one filename is problematic.
	 If we match more than one filename, punt. */
      if (matches && matches[1] && rl_completion_type == TAB)
	{
	  strvec_dispose (matches);
	  matches = (char **)0;
	}
      else if (matches && matches[1] && rl_completion_type == '!')
	{
	  rl_completion_suppress_append = 1;
	  rl_filename_completion_desired = 0;
	}
    }

  return (matches);
}


glob_testdir (dir, flags)
     char *dir;
     int flags;
{
  struct stat finfo;
  int r;

/*itrace("glob_testdir: testing %s" flags = %d, dir, flags);*/
#if defined (HAVE_LSTAT)
  r = (flags & GX_ALLDIRS) ? lstat (dir, &finfo) : stat (dir, &finfo);
#else
  r = stat (dir, &finfo);
#endif
  if (r < 0)
    return (-1);

#if defined (S_ISLNK)
  if (S_ISLNK (finfo.st_mode))
    return (-2);
#endif

  if (S_ISDIR (finfo.st_mode) == 0)
    return (-1);

  return (0);
}


glob_name_is_acceptable (name)
     const char *name;
{
  struct ign *p;
  int flags;

  /* . and .. are never matched */
  if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
    return (0);

  flags = FNM_PATHNAME | FNMATCH_EXTFLAG | FNMATCH_NOCASEGLOB;
  for (p = globignore.ignores; p->val; p++)
    {
      if (strmatch (p->val, (char *)name, flags) != FNM_NOMATCH)
	return (0);
    }
  return (1);
}


main (argc, argv, env)
     int argc;
     char **argv, **env;
#endif /* !NO_MAIN_ENV_ARG */
{
  register int i;
  int code, old_errexit_flag;
#if defined (RESTRICTED_SHELL)
  int saverst;
#endif
  volatile int locally_skip_execution;
  volatile int arg_index, top_level_arg_index;
#ifdef __OPENNT
  char **env;

  env = environ;
#endif /* __OPENNT */

  USE_VAR(argc);
  USE_VAR(argv);
  USE_VAR(env);
  USE_VAR(code);
  USE_VAR(old_errexit_flag);
#if defined (RESTRICTED_SHELL)
  USE_VAR(saverst);
#endif

  /* Catch early SIGINTs. */
  code = setjmp_nosigs (top_level);
  if (code)
    exit (2);

  xtrace_init ();

#if defined (USING_BASH_MALLOC) && defined (DEBUG) && !defined (DISABLE_MALLOC_WRAPPERS)
  malloc_set_register (1);	/* XXX - change to 1 for malloc debugging */
#endif

  check_dev_tty ();

#ifdef __CYGWIN__
  _cygwin32_check_tmp ();
#endif /* __CYGWIN__ */

  /* Wait forever if we are debugging a login shell. */
  while (debugging_login_shell) sleep (3);

  set_default_locale ();

  running_setuid = uidget ();

  if (getenv ("POSIXLY_CORRECT") || getenv ("POSIX_PEDANTIC"))
    posixly_correct = 1;

#if defined (USE_GNU_MALLOC_LIBRARY)
  mcheck (programming_error, (void (*) ())0);
#endif /* USE_GNU_MALLOC_LIBRARY */

  if (setjmp_sigs (subshell_top_level))
    {
      argc = subshell_argc;
      argv = subshell_argv;
      env = subshell_envp;
      sourced_env = 0;
    }

  shell_reinitialized = 0;

  /* Initialize `local' variables for all `invocations' of main (). */
  arg_index = 1;
  if (arg_index > argc)
    arg_index = argc;
  command_execution_string = shell_script_filename = (char *)NULL;
  want_pending_command = locally_skip_execution = read_from_stdin = 0;
  default_input = stdin;
#if defined (BUFFERED_INPUT)
  default_buffered_input = -1;
#endif

  /* Fix for the `infinite process creation' bug when running shell scripts
     from startup files on System V. */
  login_shell = make_login_shell = 0;

  /* If this shell has already been run, then reinitialize it to a
     vanilla state. */
  if (shell_initialized || shell_name)
    {
      /* Make sure that we do not infinitely recurse as a login shell. */
      if (*shell_name == '-')
	shell_name++;

      shell_reinitialize ();
      if (setjmp_nosigs (top_level))
	exit (2);
    }

  shell_environment = env;
  set_shell_name (argv[0]);
  shell_start_time = NOW;	/* NOW now defined in general.h */

  /* Parse argument flags from the input line. */

  /* Find full word arguments first. */
  arg_index = parse_long_options (argv, arg_index, argc);

  if (want_initial_help)
    {
      show_shell_usage (stdout, 1);
      exit (EXECUTION_SUCCESS);
    }

  if (do_version)
    {
      show_shell_version (1);
      exit (EXECUTION_SUCCESS);
    }

  echo_input_at_read = verbose_flag;	/* --verbose given */

  /* All done with full word options; do standard shell option parsing.*/
  this_command_name = shell_name;	/* for error reporting */
  arg_index = parse_shell_options (argv, arg_index, argc);

  /* If user supplied the "--login" (or -l) flag, then set and invert
     LOGIN_SHELL. */
  if (make_login_shell)
    {
      login_shell++;
      login_shell = -login_shell;
    }

  set_login_shell ("login_shell", login_shell != 0);

  if (dump_po_strings)
    dump_translatable_strings = 1;

  if (dump_translatable_strings)
    read_but_dont_execute = 1;

  if (running_setuid && privileged_mode == 0)
    disable_priv_mode ();

  /* Need to get the argument to a -c option processed in the
     above loop.  The next arg is a command to execute, and the
     following args are $0...$n respectively. */
  if (want_pending_command)
    {
      command_execution_string = argv[arg_index];
      if (command_execution_string == 0)
	{
	  report_error (_("%s: option requires an argument"), "-c");
	  exit (EX_BADUSAGE);
	}
      arg_index++;
    }
  this_command_name = (char *)NULL;

  /* First, let the outside world know about our interactive status.
     A shell is interactive if the `-i' flag was given, or if all of
     the following conditions are met:
	no -c command
	no arguments remaining or the -s flag given
	standard input is a terminal
	standard error is a terminal
     Refer to Posix.2, the description of the `sh' utility. */

  if (forced_interactive ||		/* -i flag */
      (!command_execution_string &&	/* No -c command and ... */
       wordexp_only == 0 &&		/* No --wordexp and ... */
       ((arg_index == argc) ||		/*   no remaining args or... */
	read_from_stdin) &&		/*   -s flag with args, and */
       isatty (fileno (stdin)) &&	/* Input is a terminal and */
       isatty (fileno (stderr))))	/* error output is a terminal. */
    init_interactive ();
  else
    init_noninteractive ();

  /*
   * Some systems have the bad habit of starting login shells with lots of open
   * file descriptors.  For instance, most systems that have picked up the
   * pre-4.0 Sun YP code leave a file descriptor open each time you call one
   * of the getpw* functions, and it's set to be open across execs.  That
   * means one for login, one for xterm, one for shelltool, etc.  There are
   * also systems that open persistent FDs to other agents or files as part
   * of process startup; these need to be set to be close-on-exec.
   */
  if (login_shell && interactive_shell)
    {
      for (i = 3; i < 20; i++)
	SET_CLOSE_ON_EXEC (i);
    }

  /* If we're in a strict Posix.2 mode, turn on interactive comments,
     alias expansion in non-interactive shells, and other Posix.2 things. */
  if (posixly_correct)
    {
      bind_variable ("POSIXLY_CORRECT", "y", 0);
      sv_strict_posix ("POSIXLY_CORRECT");
    }

  /* Now we run the shopt_alist and process the options. */
  if (shopt_alist)
    run_shopt_alist ();

  /* From here on in, the shell must be a normal functioning shell.
     Variables from the environment are expected to be set, etc. */
  shell_initialize ();

  set_default_lang ();
  set_default_locale_vars ();

  /*
   * M-x term -> TERM=eterm-color INSIDE_EMACS='251,term:0.96' (eterm)
   * M-x shell -> TERM='dumb' INSIDE_EMACS='25.1,comint' (no line editing)
   *
   * Older versions of Emacs may set EMACS to 't' or to something like
   * '22.1 (term:0.96)' instead of (or in addition to) setting INSIDE_EMACS.
   * They may set TERM to 'eterm' instead of 'eterm-color'.  They may have
   * a now-obsolete command that sets neither EMACS nor INSIDE_EMACS:
   * M-x terminal -> TERM='emacs-em7955' (line editing)
   */
  if (interactive_shell)
    {
      char *term, *emacs, *inside_emacs;
      int emacs_term, in_emacs;

      term = get_string_value ("TERM");
      emacs = get_string_value ("EMACS");
      inside_emacs = get_string_value ("INSIDE_EMACS");

      if (inside_emacs)
	{
	  emacs_term = strstr (inside_emacs, ",term:") != 0;
	  in_emacs = 1;
	}
      else if (emacs)
	{
	  /* Infer whether we are in an older Emacs. */
	  emacs_term = strstr (emacs, " (term:") != 0;
	  in_emacs = emacs_term || STREQ (emacs, "t");
	}
      else
	in_emacs = emacs_term = 0;

      /* Not sure any emacs terminal emulator sets TERM=emacs any more */
      no_line_editing |= STREQ (term, "emacs");
      no_line_editing |= in_emacs && STREQ (term, "dumb");

      /* running_under_emacs == 2 for `eterm' */
      running_under_emacs = in_emacs || STREQN (term, "emacs", 5);
      running_under_emacs += emacs_term && STREQN (term, "eterm", 5);

      if (running_under_emacs)
	gnu_error_format = 1;
    }

  top_level_arg_index = arg_index;
  old_errexit_flag = exit_immediately_on_error;

  /* Give this shell a place to longjmp to before executing the
     startup files.  This allows users to press C-c to abort the
     lengthy startup. */
  code = setjmp_sigs (top_level);
  if (code)
    {
      if (code == EXITPROG || code == ERREXIT)
	exit_shell (last_command_exit_value);
      else
	{
#if defined (JOB_CONTROL)
	  /* Reset job control, since run_startup_files turned it off. */
	  set_job_control (interactive_shell);
#endif
	  /* Reset value of `set -e', since it's turned off before running
	     the startup files. */
	  exit_immediately_on_error += old_errexit_flag;
	  locally_skip_execution++;
	}
    }

  arg_index = top_level_arg_index;

  /* Execute the start-up scripts. */

  if (interactive_shell == 0)
    {
      unbind_variable ("PS1");
      unbind_variable ("PS2");
      interactive = 0;
#if 0
      /* This has already been done by init_noninteractive */
      expand_aliases = posixly_correct;
#endif
    }
  else
    {
      change_flag ('i', FLAG_ON);
      interactive = 1;
    }

#if defined (RESTRICTED_SHELL)
  /* Set restricted_shell based on whether the basename of $0 indicates that
     the shell should be restricted or if the `-r' option was supplied at
     startup. */
  restricted_shell = shell_is_restricted (shell_name);

  /* If the `-r' option is supplied at invocation, make sure that the shell
     is not in restricted mode when running the startup files. */
  saverst = restricted;
  restricted = 0;
#endif

  /* Set positional parameters before running startup files. top_level_arg_index
     holds the index of the current argument before setting the positional
     parameters, so any changes performed in the startup files won't affect
     later option processing. */
  if (wordexp_only)
    ;			/* nothing yet */
  else if (command_execution_string)
    arg_index = bind_args (argv, arg_index, argc, 0);	/* $0 ... $n */
  else if (arg_index != argc && read_from_stdin == 0)
    {
      shell_script_filename = argv[arg_index++];
      arg_index = bind_args (argv, arg_index, argc, 1);	/* $1 ... $n */
    }
  else
    arg_index = bind_args (argv, arg_index, argc, 1);	/* $1 ... $n */

  /* The startup files are run with `set -e' temporarily disabled. */
  if (locally_skip_execution == 0 && running_setuid == 0)
    {
      old_errexit_flag = exit_immediately_on_error;
      exit_immediately_on_error = 0;

      run_startup_files ();
      exit_immediately_on_error += old_errexit_flag;
    }

  /* If we are invoked as `sh', turn on Posix mode. */
  if (act_like_sh)
    {
      bind_variable ("POSIXLY_CORRECT", "y", 0);
      sv_strict_posix ("POSIXLY_CORRECT");
    }

#if defined (RESTRICTED_SHELL)
  /* Turn on the restrictions after executing the startup files.  This
     means that `bash -r' or `set -r' invoked from a startup file will
     turn on the restrictions after the startup files are executed. */
  restricted = saverst || restricted;
  if (shell_reinitialized == 0)
    maybe_make_restricted (shell_name);
#endif /* RESTRICTED_SHELL */

#if defined (WORDEXP_OPTION)
  if (wordexp_only)
    {
      startup_state = 3;
      last_command_exit_value = run_wordexp (argv[top_level_arg_index]);
      exit_shell (last_command_exit_value);
    }
#endif

  cmd_init ();		/* initialize the command object caches */
  uwp_init ();

  if (command_execution_string)
    {
      startup_state = 2;

      if (debugging_mode)
	start_debugger ();

#if defined (ONESHOT)
      executing = 1;
      run_one_command (command_execution_string);
      exit_shell (last_command_exit_value);
#else /* ONESHOT */
      with_input_from_string (command_execution_string, "-c");
      goto read_and_execute;
#endif /* !ONESHOT */
    }

  /* Get possible input filename and set up default_buffered_input or
     default_input as appropriate. */
  if (shell_script_filename)
    open_shell_script (shell_script_filename);
  else if (interactive == 0)
    {
      /* In this mode, bash is reading a script from stdin, which is a
	 pipe or redirected file. */
#if defined (BUFFERED_INPUT)
      default_buffered_input = fileno (stdin);	/* == 0 */
#else
      setbuf (default_input, (char *)NULL);
#endif /* !BUFFERED_INPUT */
      read_from_stdin = 1;
    }
  else if (top_level_arg_index == argc)		/* arg index before startup files */
    /* "If there are no operands and the -c option is not specified, the -s
       option shall be assumed." */
    read_from_stdin = 1;

  set_bash_input ();

  if (debugging_mode && locally_skip_execution == 0 && running_setuid == 0 && (reading_shell_script || interactive_shell == 0))
    start_debugger ();

  /* Do the things that should be done only for interactive shells. */
  if (interactive_shell)
    {
      /* Set up for checking for presence of mail. */
      reset_mail_timer ();
      init_mail_dates ();

#if defined (HISTORY)
      /* Initialize the interactive history stuff. */
      bash_initialize_history ();
      /* Don't load the history from the history file if we've already
	 saved some lines in this session (e.g., by putting `history -s xx'
	 into one of the startup files). */
      if (shell_initialized == 0 && history_lines_this_session == 0)
	load_history ();
#endif /* HISTORY */

      /* Initialize terminal state for interactive shells after the
	 .bash_profile and .bashrc are interpreted. */
      get_tty_state ();
    }

#if !defined (ONESHOT)
 read_and_execute:
#endif /* !ONESHOT */

  shell_initialized = 1;

  if (pretty_print_mode && interactive_shell)
    {
      internal_warning (_("pretty-printing mode ignored in interactive shells"));
      pretty_print_mode = 0;
    }
  if (pretty_print_mode)
    exit_shell (pretty_print_loop ());

  /* Read commands until exit condition. */
  reader_loop ();
  exit_shell (last_command_exit_value);
}


parse_long_options (argv, arg_start, arg_end)
     char **argv;
     int arg_start, arg_end;
{
  int arg_index, longarg, i;
  char *arg_string;

  arg_index = arg_start;
  while ((arg_index != arg_end) && (arg_string = argv[arg_index]) &&
	 (*arg_string == '-'))
    {
      longarg = 0;

      /* Make --login equivalent to -login. */
      if (arg_string[1] == '-' && arg_string[2])
	{
	  longarg = 1;
	  arg_string++;
	}

      for (i = 0; long_args[i].name; i++)
	{
	  if (STREQ (arg_string + 1, long_args[i].name))
	    {
	      if (long_args[i].type == Int)
		*long_args[i].int_value = 1;
	      else if (argv[++arg_index] == 0)
		{
		  report_error (_("%s: option requires an argument"), long_args[i].name);
		  exit (EX_BADUSAGE);
		}
	      else
		*long_args[i].char_value = argv[arg_index];

	      break;
	    }
	}
      if (long_args[i].name == 0)
	{
	  if (longarg)
	    {
	      report_error (_("%s: invalid option"), argv[arg_index]);
	      show_shell_usage (stderr, 0);
	      exit (EX_BADUSAGE);
	    }
	  break;		/* No such argument.  Maybe flag arg. */
	}

      arg_index++;
    }

  return (arg_index);
}


restore_tilde (val, directory_part)
     char *val, *directory_part;
{
  int l, vl, dl2, xl;
  char *dh2, *expdir, *ret, *v;

  vl = strlen (val);

  /* We need to duplicate the expansions readline performs on the directory
     portion before passing it to our completion function. */
  dh2 = directory_part ? bash_dequote_filename (directory_part, 0) : 0;
  bash_directory_expansion (&dh2);
  dl2 = strlen (dh2);

  expdir = bash_tilde_expand (directory_part, 0);
  xl = strlen (expdir);
  if (*directory_part == '~' && STREQ (directory_part, expdir))
    {
      /* tilde expansion failed, so what should we return? we use what the
	 user typed. */
      v = mbschr (val, '/');
      vl = STRLEN (v);
      ret = (char *)xmalloc (xl + vl + 2);
      strcpy (ret, directory_part);
      if (v && *v)
	strcpy (ret + xl, v);

      free (dh2);
      free (expdir);

      return ret;
    }
  free (expdir);

  /*
     dh2 = unexpanded but dequoted tilde-prefix
     dl2 = length of tilde-prefix
     expdir = tilde-expanded tilde-prefix
     xl = length of expanded tilde-prefix
     l = length of remainder after tilde-prefix
  */
  l = (vl - xl) + 1;
  if (l <= 0)
    {
      free (dh2);
      return (savestring (val));		/* XXX - just punt */
    }

  ret = (char *)xmalloc (dl2 + 2 + l);
  strcpy (ret, dh2);
  strcpy (ret + dl2, val + xl);

  free (dh2);
  return (ret);
}


bash_directory_completion_hook (dirname)
     char **dirname;
{
  char *local_dirname, *new_dirname, *t;
  int return_value, should_expand_dirname, nextch, closer;
  WORD_LIST *wl;

  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 = '`';
    }

  if (should_expand_dirname && directory_exists (local_dirname, 1))
    should_expand_dirname = 0;

  if (should_expand_dirname)
    {
      new_dirname = savestring (local_dirname);
      wl = expand_prompt_string (new_dirname, 0, W_NOCOMSUB|W_NOPROCSUB|W_COMPLETE);	/* does the right thing */
      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);
	  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);
	    }
	}
      else
	{
	  free (new_dirname);
	  free (local_dirname);
	  *dirname = (char *)xmalloc (1);
	  **dirname = '\0';
	  return 1;
	}
    }
  else
    {
      /* Dequote the filename even if we don't expand it. */
      new_dirname = bash_dequote_filename (local_dirname, rl_completion_quote_character);
      return_value = STREQ (local_dirname, new_dirname) == 0;
      free (local_dirname);
      local_dirname = *dirname = new_dirname;
    }

  /* no_symbolic_links == 0 -> use (default) logical view of the file system.
     local_dirname[0] == '.' && local_dirname[1] == '/' means files in the
     current directory (./).
     local_dirname[0] == '.' && local_dirname[1] == 0 means relative pathnames
     in the current directory (e.g., lib/sh).
     XXX - should we do spelling correction on these? */

  /* This is test as it was in bash-4.2: skip relative pathnames in current
     directory.  Change test to
      (local_dirname[0] != '.' || (local_dirname[1] && local_dirname[1] != '/'))
     if we want to skip paths beginning with ./ also. */
  if (no_symbolic_links == 0 && (local_dirname[0] != '.' || local_dirname[1]))
    {
      char *temp1, *temp2;
      int len1, len2;

      /* If we have a relative path
      		(local_dirname[0] != '/' && local_dirname[0] != '.')
	 that is canonical after appending it to the current directory, then
	 	temp1 = temp2+'/'
	 That is,
	 	strcmp (temp1, temp2) == 0
	 after adding a slash to temp2 below.  It should be safe to not
	 change those.
      */
      t = get_working_directory ("symlink-hook");
      temp1 = make_absolute (local_dirname, t);
      free (t);
      temp2 = sh_canonpath (temp1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);

      /* Try spelling correction if initial canonicalization fails.  Make
	 sure we are set to replace the directory name with the results so
	 subsequent directory checks don't fail. */
      if (temp2 == 0 && dircomplete_spelling && dircomplete_expand)
	{
	  temp2 = dirspell (temp1);
	  if (temp2)
	    {
	      free (temp1);
	      temp1 = temp2;
	      temp2 = sh_canonpath (temp1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
	      return_value |= temp2 != 0;
	    }
	}
      /* If we can't canonicalize, bail. */
      if (temp2 == 0)
	{
	  free (temp1);
	  return return_value;
	}
      len1 = strlen (temp1);
      if (temp1[len1 - 1] == '/')
	{
	  len2 = strlen (temp2);
	  if (len2 > 2)		/* don't append `/' to `/' or `//' */
	    {
	      temp2 = (char *)xrealloc (temp2, len2 + 2);
	      temp2[len2] = '/';
	      temp2[len2 + 1] = '\0';
	    }
	}

      /* dircomplete_expand_relpath == 0 means we want to leave relative
	 pathnames that are unchanged by canonicalization alone.
	 *local_dirname != '/' && *local_dirname != '.' == relative pathname
	 (consistent with general.c:absolute_pathname())
	 temp1 == temp2 (after appending a slash to temp2) means the pathname
	 is not changed by canonicalization as described above. */
      if (dircomplete_expand_relpath || ((local_dirname[0] != '/' && local_dirname[0] != '.') && STREQ (temp1, temp2) == 0))
	return_value |= STREQ (local_dirname, temp2) == 0;
      free (local_dirname);
      *dirname = temp2;
      free (temp1);
    }

  return (return_value);
}


get_current_user_info ()
{
  struct passwd *entry;

  /* Don't fetch this more than once. */
  if (current_user.user_name == 0)
    {
#if defined (__TANDEM)
      entry = getpwnam (getlogin ());
#else
      entry = getpwuid (current_user.uid);
#endif
      if (entry)
	{
	  current_user.user_name = savestring (entry->pw_name);
	  current_user.shell = (entry->pw_shell && entry->pw_shell[0])
				? savestring (entry->pw_shell)
				: savestring ("/bin/sh");
	  current_user.home_dir = savestring (entry->pw_dir);
	}
      else
	{
	  current_user.user_name = _("I have no name!");
	  current_user.user_name = savestring (current_user.user_name);
	  current_user.shell = savestring ("/bin/sh");
	  current_user.home_dir = savestring ("/");
	}
#if defined (HAVE_GETPWENT)
      endpwent ();
#endif
    }
}


setup_ignore_patterns (ivp)
     struct ignorevar *ivp;
{
  int numitems, maxitems, ptr;
  char *colon_bit, *this_ignoreval;
  struct ign *p;

  this_ignoreval = get_string_value (ivp->varname);

  /* If nothing has changed then just exit now. */
  if ((this_ignoreval && ivp->last_ignoreval && STREQ (this_ignoreval, ivp->last_ignoreval)) ||
      (!this_ignoreval && !ivp->last_ignoreval))
    return;

  /* Oops.  The ignore variable has changed.  Re-parse it. */
  ivp->num_ignores = 0;

  if (ivp->ignores)
    {
      for (p = ivp->ignores; p->val; p++)
	free(p->val);
      free (ivp->ignores);
      ivp->ignores = (struct ign *)NULL;
    }

  if (ivp->last_ignoreval)
    {
      free (ivp->last_ignoreval);
      ivp->last_ignoreval = (char *)NULL;
    }

  if (this_ignoreval == 0 || *this_ignoreval == '\0')
    return;

  ivp->last_ignoreval = savestring (this_ignoreval);

  numitems = maxitems = ptr = 0;

#if 0
  while (colon_bit = extract_colon_unit (this_ignoreval, &ptr))
#else
  while (colon_bit = split_ignorespec (this_ignoreval, &ptr))
#endif
    {
      if (numitems + 1 >= maxitems)
	{
	  maxitems += 10;
	  ivp->ignores = (struct ign *)xrealloc (ivp->ignores, maxitems * sizeof (struct ign));
	}
      ivp->ignores[numitems].val = colon_bit;
      ivp->ignores[numitems].len = strlen (colon_bit);
      ivp->ignores[numitems].flags = 0;
      if (ivp->item_func)
	(*ivp->item_func) (&ivp->ignores[numitems]);
      numitems++;
    }
  ivp->ignores[numitems].val = (char *)NULL;
  ivp->num_ignores = numitems;
}


bash_directory_completion_matches (text)
     const char *text;
{
  char **m1;
  char *dfn;
  int qc;

  qc = rl_dispatching ? rl_completion_quote_character : 0;
  /* If rl_completion_found_quote != 0, rl_completion_matches will call the
     filename dequoting function, causing the directory name to be dequoted
     twice. */
  if (rl_dispatching && rl_completion_found_quote == 0)
    dfn = bash_dequote_filename ((char *)text, qc);
  else
    dfn = (char *)text;
  m1 = rl_completion_matches (dfn, rl_filename_completion_function);
  if (dfn != text)
    free (dfn);

  if (m1 == 0 || m1[0] == 0)
    return m1;
  /* 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_progcomp_ignore_filenames (m1);
  return m1;
}


bash_servicename_completion_function (text, state)
     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;
  char *value;
  char **alist, *aentry;
  int afound;

  if (state == 0)
    {
      FREE (sname);

      sname = savestring (text);
      snamelen = strlen (sname);
      setservent (0);
    }

  while (srvent = getservent ())
    {
      afound = 0;
      if (snamelen == 0 || (STREQN (sname, srvent->s_name, snamelen)))
	break;
      /* Not primary, check aliases */
      for (alist = srvent->s_aliases; *alist; alist++)
	{
	  aentry = *alist;
	  if (STREQN (sname, aentry, snamelen))
	    {
	      afound = 1;
	      break;
	    }
	}

      if (afound)
	break;
    }

  if (srvent == 0)
    {
      endservent ();
      return ((char *)NULL);
    }

  value = afound ? savestring (aentry) : savestring (srvent->s_name);
  return value;
#endif
}


unbind_args ()
{
  remember_args ((WORD_LIST *)NULL, 1);
  pop_args ();				/* Reset BASH_ARGV and BASH_ARGC */
}


glob_vector (pat, dir, flags)
     char *pat;
     char *dir;
     int flags;
{
  DIR *d;
  register struct dirent *dp;
  struct globval *lastlink, *e, *dirlist;
  register struct globval *nextlink;
  register char *nextname, *npat, *subdir;
  unsigned int count;
  int lose, skip, ndirs, isdir, sdlen, add_current, patlen;
  register char **name_vector;
  register unsigned int i;
  int mflags;		/* Flags passed to strmatch (). */
  int pflags;		/* flags passed to sh_makepath () */
  int hasglob;		/* return value from glob_pattern_p */
  int nalloca;
  struct globval *firstmalloc, *tmplink;
  char *convfn;

  lastlink = 0;
  count = lose = skip = add_current = 0;

  firstmalloc = 0;
  nalloca = 0;

  name_vector = NULL;

/*itrace("glob_vector: pat = `%s' dir = `%s' flags = 0x%x", pat, dir, flags);*/
  /* If PAT is empty, skip the loop, but return one (empty) filename. */
  if (pat == 0 || *pat == '\0')
    {
      if (glob_testdir (dir, 0) < 0)
	return ((char **) &glob_error_return);

      nextlink = (struct globval *)alloca (sizeof (struct globval));
      if (nextlink == NULL)
	return ((char **) NULL);

      nextlink->next = (struct globval *)0;
      nextname = (char *) malloc (1);
      if (nextname == 0)
	lose = 1;
      else
	{
	  lastlink = nextlink;
	  nextlink->name = nextname;
	  nextname[0] = '\0';
	  count = 1;
	}

      skip = 1;
    }

  patlen = (pat && *pat) ? strlen (pat) : 0;

  /* If the filename pattern (PAT) does not contain any globbing characters,
     or contains a pattern with only backslash escapes (hasglob == 2),
     we can dispense with reading the directory, and just see if there is
     a filename `DIR/PAT'.  If there is, and we can access it, just make the
     vector to return and bail immediately. */
  hasglob = 0;
  if (skip == 0 && (hasglob = glob_pattern_p (pat)) == 0 || hasglob == 2)
    {
      int dirlen;
      struct stat finfo;

      if (glob_testdir (dir, 0) < 0)
	return ((char **) &glob_error_return);

      dirlen = strlen (dir);
      nextname = (char *)malloc (dirlen + patlen + 2);
      npat = (char *)malloc (patlen + 1);
      if (nextname == 0 || npat == 0)
	{
	  FREE (nextname);
	  FREE (npat);
	  lose = 1;
	}
      else
	{
	  strcpy (npat, pat);
	  dequote_pathname (npat);

	  strcpy (nextname, dir);
	  nextname[dirlen++] = '/';
	  strcpy (nextname + dirlen, npat);

	  if (GLOB_TESTNAME (nextname) >= 0)
	    {
	      free (nextname);
	      nextlink = (struct globval *)alloca (sizeof (struct globval));
	      if (nextlink)
		{
		  nextlink->next = (struct globval *)0;
		  lastlink = nextlink;
		  nextlink->name = npat;
		  count = 1;
		}
	      else
		{
		  free (npat);
		  lose = 1;
		}
	    }
	  else
	    {
	      free (nextname);
	      free (npat);
	    }
	}

      skip = 1;
    }

  if (skip == 0)
    {
      /* Open the directory, punting immediately if we cannot.  If opendir
	 is not robust (i.e., it opens non-directories successfully), test
	 that DIR is a directory and punt if it's not. */
#if defined (OPENDIR_NOT_ROBUST)
      if (glob_testdir (dir, 0) < 0)
	return ((char **) &glob_error_return);
#endif

      d = opendir (dir);
      if (d == NULL)
	return ((char **) &glob_error_return);

      /* Compute the flags that will be passed to strmatch().  We don't
	 need to do this every time through the loop. */
      mflags = (noglob_dot_filenames ? FNM_PERIOD : 0) | FNM_PATHNAME;

#ifdef FNM_CASEFOLD
      if (glob_ignore_case)
	mflags |= FNM_CASEFOLD;
#endif

      if (extended_glob)
	mflags |= FNM_EXTMATCH;

      add_current = ((flags & (GX_ALLDIRS|GX_ADDCURDIR)) == (GX_ALLDIRS|GX_ADDCURDIR));

      /* Scan the directory, finding all names that match.
	 For each name that matches, allocate a struct globval
	 on the stack and store the name in it.
	 Chain those structs together; lastlink is the front of the chain.  */
      while (1)
	{
	  /* Make globbing interruptible in the shell. */
	  if (interrupt_state || terminating_signal)
	    {
	      lose = 1;
	      break;
	    }
	  else if (signal_is_pending (SIGINT))	/* XXX - make SIGINT traps responsive */
	    {
	      lose = 1;
	      break;
	    }

	  dp = readdir (d);
	  if (dp == NULL)
	    break;

	  /* If this directory entry is not to be used, try again. */
	  if (REAL_DIR_ENTRY (dp) == 0)
	    continue;

#if 0
	  if (dp->d_name == 0 || *dp->d_name == 0)
	    continue;
#endif

#if HANDLE_MULTIBYTE
	  if (MB_CUR_MAX > 1 && mbskipname (pat, dp->d_name, flags))
	    continue;
	  else
#endif
	  if (skipname (pat, dp->d_name, flags))
	    continue;

	  /* If we're only interested in directories, don't bother with files */
	  if (flags & (GX_MATCHDIRS|GX_ALLDIRS))
	    {
	      pflags = (flags & GX_ALLDIRS) ? MP_RMDOT : 0;
	      if (flags & GX_NULLDIR)
		pflags |= MP_IGNDOT;
	      subdir = sh_makepath (dir, dp->d_name, pflags);
	      isdir = glob_testdir (subdir, flags);
	      if (isdir < 0 && (flags & GX_MATCHDIRS))
		{
		  free (subdir);
		  continue;
		}
	    }

	  if (flags & GX_ALLDIRS)
	    {
	      if (isdir == 0)
		{
		  dirlist = finddirs (pat, subdir, (flags & ~GX_ADDCURDIR), &e, &ndirs);
		  if (dirlist == &finddirs_error_return)
		    {
		      free (subdir);
		      lose = 1;
		      break;
		    }
		  if (ndirs)		/* add recursive directories to list */
		    {
		      if (firstmalloc == 0)
		        firstmalloc = e;
		      e->next = lastlink;
		      lastlink = dirlist;
		      count += ndirs;
		    }
		}

	      /* XXX - should we even add this if it's not a directory? */
	      nextlink = (struct globval *) malloc (sizeof (struct globval));
	      if (firstmalloc == 0)
		firstmalloc = nextlink;
	      sdlen = strlen (subdir);
	      nextname = (char *) malloc (sdlen + 1);
	      if (nextlink == 0 || nextname == 0)
		{
		  FREE (nextlink);
		  FREE (nextname);
		  free (subdir);
		  lose = 1;
		  break;
		}
	      nextlink->next = lastlink;
	      lastlink = nextlink;
	      nextlink->name = nextname;
	      bcopy (subdir, nextname, sdlen + 1);
	      free (subdir);
	      ++count;
	      continue;
	    }
	  else if (flags & GX_MATCHDIRS)
	    free (subdir);

	  convfn = fnx_fromfs (dp->d_name, D_NAMLEN (dp));
	  if (strmatch (pat, convfn, mflags) != FNM_NOMATCH)
	    {
	      if (nalloca < ALLOCA_MAX)
		{
		  nextlink = (struct globval *) alloca (sizeof (struct globval));
		  nalloca += sizeof (struct globval);
		}
	      else
		{
		  nextlink = (struct globval *) malloc (sizeof (struct globval));
		  if (firstmalloc == 0)
		    firstmalloc = nextlink;
		}

	      nextname = (char *) malloc (D_NAMLEN (dp) + 1);
	      if (nextlink == 0 || nextname == 0)
		{
		  FREE (nextlink);
		  FREE (nextname);
		  lose = 1;
		  break;
		}
	      nextlink->next = lastlink;
	      lastlink = nextlink;
	      nextlink->name = nextname;
	      bcopy (dp->d_name, nextname, D_NAMLEN (dp) + 1);
	      ++count;
	    }
	}

      (void) closedir (d);
    }

  /* compat: if GX_ADDCURDIR, add the passed directory also.  Add an empty
     directory name as a placeholder if GX_NULLDIR (in which case the passed
     directory name is "."). */
  if (add_current)
    {
      sdlen = strlen (dir);
      nextname = (char *)malloc (sdlen + 1);
      nextlink = (struct globval *) malloc (sizeof (struct globval));
      if (nextlink == 0 || nextname == 0)
	{
	  FREE (nextlink);
	  FREE (nextname);
	  lose = 1;
	}
      else
	{
	  nextlink->name = nextname;
	  nextlink->next = lastlink;
	  lastlink = nextlink;
	  if (flags & GX_NULLDIR)
	    nextname[0] = '\0';
	  else
	    bcopy (dir, nextname, sdlen + 1);
	  ++count;
	}
    }

  if (lose == 0)
    {
      name_vector = (char **) malloc ((count + 1) * sizeof (char *));
      lose |= name_vector == NULL;
    }

  /* Have we run out of memory?	 */
  if (lose)
    {
      tmplink = 0;

      /* Here free the strings we have got.  */
      while (lastlink)
	{
	  /* Since we build the list in reverse order, the first N entries
	     will be allocated with malloc, if firstmalloc is set, from
	     lastlink to firstmalloc. */
	  if (firstmalloc)
	    {
	      if (lastlink == firstmalloc)
		firstmalloc = 0;
	      tmplink = lastlink;
	    }
	  else
	    tmplink = 0;
	  free (lastlink->name);
	  lastlink = lastlink->next;
	  FREE (tmplink);
	}

      /* Don't call QUIT; here; let higher layers deal with it. */

      return ((char **)NULL);
    }

  /* Copy the name pointers from the linked list into the vector.  */
  for (tmplink = lastlink, i = 0; i < count; ++i)
    {
      name_vector[i] = tmplink->name;
      tmplink = tmplink->next;
    }

  name_vector[count] = NULL;

  /* If we allocated some of the struct globvals, free them now. */
  if (firstmalloc)
    {
      tmplink = 0;
      while (lastlink)
	{
	  tmplink = lastlink;
	  if (lastlink == firstmalloc)
	    lastlink = firstmalloc = 0;
	  else
	    lastlink = lastlink->next;
	  free (tmplink);
	}
    }

  return (name_vector);
}


ignore_glob_matches (names)
     char **names;
{
  if (globignore.num_ignores == 0)
    return;

  ignore_globbed_names (names, glob_name_is_acceptable);
}


command_word_completion_function (hint_text, state)
     const 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 char *fnhint = (char *)NULL;
  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 mapping_over, local_index, searching_path, hint_is_dir;
  static int old_glob_ignore_case, globpat;
  static SHELL_VAR **varlist = (SHELL_VAR **)NULL;
#if defined (ALIAS)
  static alias_t **alias_list = (alias_t **)NULL;
#endif /* ALIAS */
  char *temp, *cval;

  /* We have to map over the possibilities for command words.  If we have
     no state, then make one just for that purpose. */
  if (state == 0)
    {
      rl_filename_stat_hook = bash_command_name_stat_hook;

      if (dequoted_hint && dequoted_hint != hint)
	free (dequoted_hint);
      if (hint)
	free (hint);

      mapping_over = searching_path = 0;
      hint_is_dir = CMD_IS_DIR (hint_text);
      val = (char *)NULL;

      temp = rl_variable_value ("completion-ignore-case");
      igncase = RL_BOOLEAN_VARIABLE_VALUE (temp);

      if (glob_matches)
	{
	  free (glob_matches);
	  glob_matches = (char **)NULL;
	}

      globpat = completion_glob_pattern ((char *)hint_text);

      /* 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 (globpat || absolute_program (hint_text))
	{
	  /* Perform tilde expansion on what's passed, so we don't end up
	     passing filenames with tildes directly to stat().  The rest of
	     the shell doesn't do variable expansion on the word following
	     the tilde, so we don't do it here even if direxpand is set. */
	  if (*hint_text == '~')
	    {
	      hint = bash_tilde_expand (hint_text, 0);
	      directory_part = savestring (hint_text);
	      temp = strchr (directory_part, '/');
	      if (temp)
		*temp = 0;
	      else
		{
		  free (directory_part);
		  directory_part = (char *)NULL;
		}
	    }
	  else if (dircomplete_expand)
	    {
	      hint = savestring (hint_text);
	      bash_directory_completion_hook (&hint);
	    }
	  else
	    hint = savestring (hint_text);

	  dequoted_hint = hint;
	  /* If readline's completer found a quote character somewhere, but
	     didn't set the quote character, there must have been a quote
	     character embedded in the filename.  It can't be at the start of
	     the filename, so we need to dequote the filename before we look
	     in the file system for it. */
	  if (rl_completion_found_quote && rl_completion_quote_character == 0)
	    {
	      dequoted_hint = bash_dequote_filename (hint, 0);
	      free (hint);
	      hint = dequoted_hint;
	    }
	  hint_len = strlen (hint);

	  if (filename_hint)
	    free (filename_hint);

	  fnhint = filename_hint = savestring (hint);

	  istate = 0;

	  if (globpat)
	    {
	      mapping_over = 5;
	      goto globword;
	    }
	  else
	    {
	     if (dircomplete_expand && path_dot_or_dotdot (filename_hint))
		{
		  dircomplete_expand = 0;
		  set_directory_hook ();
		  dircomplete_expand = 1;
		}
	      mapping_over = 4;
	      goto inner;
	    }
	}

      dequoted_hint = hint = savestring (hint_text);
      hint_len = strlen (hint);

      if (rl_completion_found_quote && rl_completion_quote_character == 0)
	dequoted_hint = bash_dequote_filename (hint, 0);

      path = get_string_value ("PATH");
      path_index = dot_in_path = 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 (igncase == 0 && (STREQN (alias, hint, hint_len)))
	    return (savestring (alias));
	  else if (igncase && strncasecmp (alias, hint, hint_len) == 0)
	    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;

	  /* Honor completion-ignore-case for shell function names. */
	  if (igncase == 0 && (STREQN (varname, hint, hint_len)))
	    return (savestring (varname));
	  else if (igncase && strncasecmp (varname, hint, hint_len) == 0)
	    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++;
    }

globword:
  /* Limited support for completing command words with globbing chars.  Only
     a single match (multiple matches that end up reducing the number of
     characters in the common prefix are bad) will ever be returned on
     regular completion. */
  if (globpat)
    {
      if (state == 0)
	{
	  glob_ignore_case = igncase;
	  glob_matches = shell_glob_filename (hint);
	  glob_ignore_case = old_glob_ignore_case;

	  if (GLOB_FAILED (glob_matches) || glob_matches == 0)
	    {
	      glob_matches = (char **)NULL;
	      return ((char *)NULL);
	    }

	  local_index = 0;

	  if (glob_matches[1] && rl_completion_type == TAB)	/* multiple matches are bad */
	    return ((char *)NULL);
	}

      while (val = glob_matches[local_index++])
        {
	  if (executable_or_directory (val))
	    {
	      if (*hint_text == '~' && directory_part)
		{
		  temp = maybe_restore_tilde (val, directory_part);
		  free (val);
		  val = temp;
		}
	      return (val);
	    }
	  free (val);
        }

      glob_ignore_case = old_glob_ignore_case;
      return ((char *)NULL);
    }

  /* If the text passed is a directory in the current directory, return it
     as a possible match.  Executables in directories in the current
     directory can be specified using relative pathnames and successfully
     executed even when `.' is not in $PATH. */
  if (hint_is_dir)
    {
      hint_is_dir = 0;	/* only return the hint text once */
      return (savestring (hint_text));
    }

  /* Repeatedly call filename_completion_function 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 == 0)
    {
      char *current_path;

      /* Get the next directory from the path.  If there is none, then we
	 are all done. */
      if (path == 0 || path[path_index] == 0 ||
	  (current_path = extract_colon_unit (path, &path_index)) == 0)
	return ((char *)NULL);

      searching_path = 1;
      if (*current_path == 0)
	{
	  free (current_path);
	  current_path = savestring (".");
	}

      if (*current_path == '~')
	{
	  char *t;

	  t = bash_tilde_expand (current_path, 0);
	  free (current_path);
	  current_path = t;
	}

      if (current_path[0] == '.' && current_path[1] == '\0')
	dot_in_path = 1;

      if (fnhint && fnhint != filename_hint)
	free (fnhint);
      if (filename_hint)
	free (filename_hint);

      filename_hint = sh_makepath (current_path, hint, 0);
      /* Need a quoted version (though it doesn't matter much in most
	 cases) because rl_filename_completion_function dequotes the
	 filename it gets, assuming that it's been quoted as part of
	 the input line buffer. */
      if (strpbrk (filename_hint, "\"'\\"))
	fnhint = sh_backslash_quote (filename_hint, filename_bstab, 0);
      else
	fnhint = filename_hint;
      free (current_path);		/* XXX */
    }

 inner:
  val = rl_filename_completion_function (fnhint, istate);
  if (mapping_over == 4 && dircomplete_expand)
    set_directory_hook ();

  istate = 1;

  if (val == 0)
    {
      /* 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, freetemp;

      if (absolute_program (hint))
	{
	  if (igncase == 0)
	    match = strncmp (val, hint, hint_len) == 0;
	  else
	    match = strncasecmp (val, hint, hint_len) == 0;

	  /* If we performed tilde expansion, restore the original
	     filename. */
	  if (*hint_text == '~')
	    temp = maybe_restore_tilde (val, directory_part);
	  else
	    temp = savestring (val);
	  freetemp = 1;
	}
      else
	{
	  temp = strrchr (val, '/');

	  if (temp)
	    {
	      temp++;
	      if (igncase == 0)
		freetemp = match = strncmp (temp, hint, hint_len) == 0;
	      else
		freetemp = match = strncasecmp (temp, hint, hint_len) == 0;
	      if (match)
		temp = savestring (temp);
	    }
	  else
	    freetemp = match = 0;
	}

      /* If we have found a match, and it is an executable file, return it.
	 We don't return directory names when searching $PATH, since the
	 bash execution code won't find executables in directories which
	 appear in directories in $PATH when they're specified using
	 relative pathnames.  */
#if 0
      /* If we're not searching $PATH and we have a relative pathname, we
	 need to re-canonicalize it before testing whether or not it's an
	 executable or a directory so the shell treats .. relative to $PWD
	 according to the physical/logical option.  The shell already
	 canonicalizes the directory name in order to tell readline where
	 to look, so not doing it here will be inconsistent. */
      /* XXX -- currently not used -- will introduce more inconsistency,
	 since shell does not canonicalize ../foo before passing it to
	 shell_execve().  */
      if (match && searching_path == 0 && *val == '.')
	{
	  char *t, *t1;

	  t = get_working_directory ("command-word-completion");
	  t1 = make_absolute (val, t);
	  free (t);
	  cval = sh_canonpath (t1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
	}
      else
#endif
	cval = val;

      if (match && executable_completion ((searching_path ? val : cval), searching_path))
	{
	  if (cval != val)
	    free (cval);
	  free (val);
	  val = "";		/* So it won't be NULL. */
	  return (temp);
	}
      else
	{
	  if (freetemp)
	    free (temp);
	  if (cval != val)
	    free (cval);
	  free (val);
	  goto inner;
	}
    }
}


bind_keyseq_to_unix_command (line)
     char *line;
{
  Keymap kmap, cmd_xmap;
  char *kseq, *value;
  int i, kstart;

  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
     generate an error for anything else. */
  i = isolate_sequence (line, 0, 1, &kstart);
  if (i < 0)
    return -1;

  /* Create the key sequence string to pass to rl_generic_bind */
  kseq = substring (line, kstart, i);

  for ( ; line[i] && line[i] != ':'; i++)
    ;
  if (line[i] != ':')
    {
      builtin_error (_("%s: missing colon separator"), line);
      FREE (kseq);
      return -1;
    }

  i = isolate_sequence (line, i + 1, 0, &kstart);
  if (i < 0)
    {
      FREE (kseq);
      return -1;
    }

  /* Create the value string containing the command to execute. */
  value = substring (line, kstart, i);

  /* Save the command to execute and the key sequence in the CMD_XMAP */
  cmd_xmap = get_cmd_xmap_from_keymap (kmap);
  rl_generic_bind (ISMACR, kseq, value, cmd_xmap);

  /* and bind the key sequence in the current keymap to a function that
     understands how to execute from CMD_XMAP */
  rl_bind_keyseq_in_map (kseq, bash_execute_unix_command, kmap);

  free (kseq);
  return 0;
}


run_shopt_alist ()
{
  register int i;

  for (i = 0; i < shopt_ind; i++)
    if (shopt_setopt (shopt_alist[i].word, (shopt_alist[i].token == '-')) != EXECUTION_SUCCESS)
      exit (EX_BADUSAGE);
  free (shopt_alist);
  shopt_alist = 0;
  shopt_ind = shopt_len = 0;
}


udequote_pathname (pathname)
     char *pathname;
{
  register int i, j;

  for (i = j = 0; pathname && pathname[i]; )
    {
      if (pathname[i] == '\\')
	i++;

      pathname[j++] = pathname[i++];

      if (pathname[i - 1] == 0)
	break;
    }
  if (pathname)
    pathname[j] = '\0';
}


print_unix_command_map ()
{
  Keymap save, cmd_xmap;

  save = rl_get_keymap ();
  cmd_xmap = get_cmd_xmap_from_keymap (save);
  rl_set_keymap (cmd_xmap);
  rl_macro_dumper (1);
  rl_set_keymap (save);
  return 0;
}


execute_env_file (env_file)
      char *env_file;
{
  char *fn;

  if (env_file && *env_file)
    {
      fn = expand_string_unsplit_to_string (env_file, Q_DOUBLE_QUOTES);
      if (fn && *fn)
	maybe_execute_file (fn, 1);
      FREE (fn);
    }
}


parse_shell_options (argv, arg_start, arg_end)
     char **argv;
     int arg_start, arg_end;
{
  int arg_index;
  int arg_character, on_or_off, next_arg, i;
  char *o_option, *arg_string;

  arg_index = arg_start;
  while (arg_index != arg_end && (arg_string = argv[arg_index]) &&
	 (*arg_string == '-' || *arg_string == '+'))
    {
      /* There are flag arguments, so parse them. */
      next_arg = arg_index + 1;

      /* A single `-' signals the end of options.  From the 4.3 BSD sh.
	 An option `--' means the same thing; this is the standard
	 getopt(3) meaning. */
      if (arg_string[0] == '-' &&
	   (arg_string[1] == '\0' ||
	     (arg_string[1] == '-' && arg_string[2] == '\0')))
	return (next_arg);

      i = 1;
      on_or_off = arg_string[0];
      while (arg_character = arg_string[i++])
	{
	  switch (arg_character)
	    {
	    case 'c':
	      want_pending_command = 1;
	      break;

	    case 'l':
	      make_login_shell = 1;
	      break;

	    case 's':
	      read_from_stdin = 1;
	      break;

	    case 'o':
	      o_option = argv[next_arg];
	      if (o_option == 0)
		{
		  list_minus_o_opts (-1, (on_or_off == '-') ? 0 : 1);
		  break;
		}
	      if (set_minus_o_option (on_or_off, o_option) != EXECUTION_SUCCESS)
		exit (EX_BADUSAGE);
	      next_arg++;
	      break;

	    case 'O':
	      /* Since some of these can be overridden by the normal
		 interactive/non-interactive shell initialization or
		 initializing posix mode, we save the options and process
		 them after initialization. */
	      o_option = argv[next_arg];
	      if (o_option == 0)
		{
		  shopt_listopt (o_option, (on_or_off == '-') ? 0 : 1);
		  break;
		}
	      add_shopt_to_alist (o_option, on_or_off);
	      next_arg++;
	      break;

	    case 'D':
	      dump_translatable_strings = 1;
	      break;

	    default:
	      if (change_flag (arg_character, on_or_off) == FLAG_ERROR)
		{
		  report_error (_("%c%c: invalid option"), on_or_off, arg_character);
		  show_shell_usage (stderr, 0);
		  exit (EX_BADUSAGE);
		}
	    }
	}
      /* Can't do just a simple increment anymore -- what about
	 "bash -abouo emacs ignoreeof -hP"? */
      arg_index = next_arg;
    }

  return (arg_index);
}


dequote_pathname (pathname)
     char *pathname;
{
  if (MB_CUR_MAX > 1)
    wdequote_pathname (pathname);
  else
    udequote_pathname (pathname);
}


quote_string_for_globbing (pathname, qflags)
     const char *pathname;
     int qflags;
{
  char *temp;
  register int i, j;
  int cclass, collsym, equiv, c, last_was_backslash;
  int savei, savej;

  temp = (char *)xmalloc (2 * strlen (pathname) + 1);

  if ((qflags & QGLOB_CVTNULL) && QUOTED_NULL (pathname))
    {
      temp[0] = '\0';
      return temp;
    }

  cclass = collsym = equiv = last_was_backslash = 0;
  for (i = j = 0; pathname[i]; i++)
    {
      /* Fix for CTLESC at the end of the string? */
      if (pathname[i] == CTLESC && pathname[i+1] == '\0')
	{
	  temp[j++] = pathname[i++];
	  break;
	}
      /* If we are parsing regexp, turn CTLESC CTLESC into CTLESC. It's not an
	 ERE special character, so we should just be able to pass it through. */
      else if ((qflags & (QGLOB_REGEXP|QGLOB_CTLESC)) && pathname[i] == CTLESC && (pathname[i+1] == CTLESC || pathname[i+1] == CTLNUL))
	{
	  i++;
	  temp[j++] = pathname[i];
	  continue;
	}
      else if (pathname[i] == CTLESC)
	{
	  if ((qflags & QGLOB_FILENAME) && pathname[i+1] == '/')
	    continue;
	  /* What to do if preceding char is backslash? */
	  if (pathname[i+1] != CTLESC && (qflags & QGLOB_REGEXP) && ere_char (pathname[i+1]) == 0)
	    continue;
	  temp[j++] = '\\';
	  i++;
	  if (pathname[i] == '\0')
	    break;
	}
      else if ((qflags & QGLOB_REGEXP) && (i == 0 || pathname[i-1] != CTLESC) && pathname[i] == '[')	/*]*/
	{
	  temp[j++] = pathname[i++];	/* open bracket */
	  savej = j;
	  savei = i;
	  c = pathname[i++];	/* c == char after open bracket */
	  if (c == '^')		/* ignore pattern negation */
	    {
	      temp[j++] = c;
	      c = pathname[i++];
	    }
	  if (c == ']')		/* ignore right bracket if first char */
	    {
	      temp[j++] = c;
	      c = pathname[i++];
	    }
	  do
	    {
	      if (c == 0)
		goto endpat;
	      else if (c == CTLESC)
		{
		  /* skip c, check for EOS, let assignment at end of loop */
		  /* pathname[i] == backslash-escaped character */
		  if (pathname[i] == 0)
		    goto endpat;
		  temp[j++] = pathname[i++];
		}
	      else if (c == '[' && pathname[i] == ':')
		{
		  temp[j++] = c;
		  temp[j++] = pathname[i++];
		  cclass = 1;
		}
	      else if (cclass && c == ':' && pathname[i] == ']')
		{
		  temp[j++] = c;
		  temp[j++] = pathname[i++];
		  cclass = 0;
		}
	      else if (c == '[' && pathname[i] == '=')
		{
		  temp[j++] = c;
		  temp[j++] = pathname[i++];
		  if (pathname[i] == ']')
		    temp[j++] = pathname[i++];		/* right brack can be in equiv */
		  equiv = 1;
		}
	      else if (equiv && c == '=' && pathname[i] == ']')
		{
		  temp[j++] = c;
		  temp[j++] = pathname[i++];
		  equiv = 0;
		}
	      else if (c == '[' && pathname[i] == '.')
		{
		  temp[j++] = c;
		  temp[j++] = pathname[i++];
		  if (pathname[i] == ']')
		    temp[j++] = pathname[i++];		/* right brack can be in collsym */
		  collsym = 1;
		}
	      else if (collsym && c == '.' && pathname[i] == ']')
		{
		  temp[j++] = c;
		  temp[j++] = pathname[i++];
		  collsym = 0;
		}
	      else
		temp[j++] = c;
	    }
	  while (((c = pathname[i++]) != ']') && c != 0);

	  /* If we don't find the closing bracket before we hit the end of
	     the string, rescan string without treating it as a bracket
	     expression (has implications for backslash and special ERE
	     chars) */
	  if (c == 0)
	    {
	      i = savei - 1;	/* -1 for autoincrement above */
	      j = savej;
	      continue;
	    }

	  temp[j++] = c;	/* closing right bracket */
	  i--;			/* increment will happen above in loop */
	  continue;		/* skip double assignment below */
	}
      else if (pathname[i] == '\\' && (qflags & QGLOB_REGEXP) == 0)
	{
	  /* XXX - if not quoting regexp, use backslash as quote char. Should
	     we just pass it through without treating it as special? That is
	     what ksh93 seems to do. */

	  /* If we want to pass through backslash unaltered, comment out these
	     lines. */
	  temp[j++] = '\\';

	  i++;
	  if (pathname[i] == '\0')
	    break;
	  /* If we are turning CTLESC CTLESC into CTLESC, we need to do that
	     even when the first CTLESC is preceded by a backslash. */
	  if ((qflags & QGLOB_CTLESC) && pathname[i] == CTLESC && (pathname[i+1] == CTLESC || pathname[i+1] == CTLNUL))
	    i++;	/* skip over the CTLESC */
	}
      else if (pathname[i] == '\\' && (qflags & QGLOB_REGEXP))
        last_was_backslash = 1;
      temp[j++] = pathname[i];
    }
endpat:
  temp[j] = '\0';

  return (temp);
}


add_shopt_to_alist (opt, on_or_off)
     char *opt;
     int on_or_off;
{
  if (shopt_ind >= shopt_len)
    {
      shopt_len += 8;
      shopt_alist = (STRING_INT_ALIST *)xrealloc (shopt_alist, shopt_len * sizeof (shopt_alist[0]));
    }
  shopt_alist[shopt_ind].word = opt;
  shopt_alist[shopt_ind].token = on_or_off;
  shopt_ind++;
}


bash_forward_shellword (count, key)
     int count, key;
{
  size_t slen;
  int c, p;
  DECLARE_MBSTATE;

  if (count < 0)
    return (bash_backward_shellword (-count, key));

  /* The tricky part of this is deciding whether or not the first character
     we're on is an unquoted metacharacter.  Not completely handled yet. */
  /* XXX - need to test this stuff with backslash-escaped shell
     metacharacters and unclosed single- and double-quoted strings. */

  p = rl_point;
  slen = rl_end;

  while (count)
    {
      if (p == rl_end)
	{
	  rl_point = rl_end;
	  return 0;
	}

      /* Are we in a quoted string?  If we are, move to the end of the quoted
         string and continue the outer loop. We only want quoted strings, not
         backslash-escaped characters, but char_is_quoted doesn't
         differentiate. */
      if (char_is_quoted (rl_line_buffer, p) && p > 0 && rl_line_buffer[p-1] != '\\')
	{
	  do
	    ADVANCE_CHAR (rl_line_buffer, slen, p);
	  while (p < rl_end && char_is_quoted (rl_line_buffer, p));
	  count--;
	  continue;
	}

      /* Rest of code assumes we are not in a quoted string. */
      /* Move forward until we hit a non-metacharacter. */
      while (p < rl_end && (c = rl_line_buffer[p]) && WORDDELIM (c))
	{
	  switch (c)
	    {
	    default:
	      ADVANCE_CHAR (rl_line_buffer, slen, p);
	      continue;		/* straight back to loop, don't increment p */
	    case '\\':
	      if (p < rl_end && rl_line_buffer[p])
		ADVANCE_CHAR (rl_line_buffer, slen, p);
	      break;
	    case '\'':
	      p = skip_to_delim (rl_line_buffer, ++p, "'", SD_NOJMP);
	      break;
	    case '"':
	      p = skip_to_delim (rl_line_buffer, ++p, "\"", SD_NOJMP);
	      break;
	    }

	  if (p < rl_end)
	    p++;
	}

      if (rl_line_buffer[p] == 0 || p == rl_end)
        {
	  rl_point = rl_end;
	  rl_ding ();
	  return 0;
        }

      /* Now move forward until we hit a non-quoted metacharacter or EOL */
      while (p < rl_end && (c = rl_line_buffer[p]) && WORDDELIM (c) == 0)
	{
	  switch (c)
	    {
	    default:
	      ADVANCE_CHAR (rl_line_buffer, slen, p);
	      continue;		/* straight back to loop, don't increment p */
	    case '\\':
	      if (p < rl_end && rl_line_buffer[p])
		ADVANCE_CHAR (rl_line_buffer, slen, p);
	      break;
	    case '\'':
	      p = skip_to_delim (rl_line_buffer, ++p, "'", SD_NOJMP);
	      break;
	    case '"':
	      p = skip_to_delim (rl_line_buffer, ++p, "\"", SD_NOJMP);
	      break;
	    }

	  if (p < rl_end)
	    p++;
	}

      if (p == rl_end || rl_line_buffer[p] == 0)
	{
	  rl_point = rl_end;
	  return (0);
	}

      count--;
    }

  rl_point = p;
  return (0);
}


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;
}


run_one_command (command)
     char *command;
{
  int code;

  code = setjmp_nosigs (top_level);

  if (code != NOT_JUMPED)
    {
#if defined (PROCESS_SUBSTITUTION)
      unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */
      switch (code)
	{
	  /* Some kind of throw to top_level has occurred. */
	case FORCE_EOF:
	  return last_command_exit_value = 127;
	case ERREXIT:
	case EXITPROG:
	  return last_command_exit_value;
	case DISCARD:
	  return last_command_exit_value = 1;
	default:
	  command_error ("run_one_command", CMDERR_BADJUMP, code, 0);
	}
    }
   return (parse_and_execute (savestring (command), "-c", SEVAL_NOHIST));
}


wdequote_pathname (pathname)
     char *pathname;
{
  mbstate_t ps;
  size_t len, n;
  wchar_t *wpathname;
  int i, j;
  wchar_t *orig_wpathname;

  if (mbsmbchar (pathname) == 0)
    {
      udequote_pathname (pathname);
      return;
    }

  len = strlen (pathname);
  /* Convert the strings into wide characters.  */
  n = xdupmbstowcs (&wpathname, NULL, pathname);
  if (n == (size_t) -1)
    {
      /* Something wrong.  Fall back to single-byte */
      udequote_pathname (pathname);
      return;
    }
  orig_wpathname = wpathname;

  for (i = j = 0; wpathname && wpathname[i]; )
    {
      if (wpathname[i] == L'\\')
	i++;

      wpathname[j++] = wpathname[i++];

      if (wpathname[i - 1] == L'\0')
	break;
    }
  if (wpathname)
    wpathname[j] = L'\0';

  /* Convert the wide character string into unibyte character set. */
  memset (&ps, '\0', sizeof(mbstate_t));
  n = wcsrtombs(pathname, (const wchar_t **)&wpathname, len, &ps);
  pathname[len] = '\0';

  /* Can't just free wpathname here; wcsrtombs changes it in many cases. */
  free (orig_wpathname);
}


ignore_globbed_names (names, name_func)
     char **names;
     sh_ignore_func_t *name_func;
{
  char **newnames;
  int n, i;

  for (i = 0; names[i]; i++)
    ;
  newnames = strvec_create (i + 1);

  for (n = i = 0; names[i]; i++)
    {
      if ((*name_func) (names[i]))
	newnames[n++] = names[i];
      else
	free (names[i]);
    }

  newnames[n] = (char *)NULL;

  if (n == 0)
    {
      names[0] = (char *)NULL;
      free (newnames);
      return;
    }

  /* Copy the acceptable names from NEWNAMES back to NAMES and set the
     new array end. */
  for (n = 0; newnames[n]; n++)
    names[n] = newnames[n];
  names[n] = (char *)NULL;
  free (newnames);
}