Imported from ../bash-2.05.tar.gz.
[platform/upstream/bash.git] / execute_cmd.c
index 5f57840..79abc28 100644 (file)
@@ -6,7 +6,7 @@
 
    Bash is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 1, or (at your option)
+   the Free Software Foundation; either version 2, or (at your option)
    any later version.
 
    Bash is distributed in the hope that it will be useful, but WITHOUT
@@ -16,7 +16,7 @@
 
    You should have received a copy of the GNU General Public License
    along with Bash; see the file COPYING.  If not, write to the Free
-   Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
 #include "config.h"
 
 #if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
 #include <stdio.h>
 #include <ctype.h>
 #include "bashtypes.h"
-#include <sys/file.h>
+#ifndef _MINIX
+#  include <sys/file.h>
+#endif
 #include "filecntl.h"
 #include "posixstat.h"
 #include <signal.h>
-#include <sys/param.h>
+#ifndef _MINIX
+#  include <sys/param.h>
+#endif
 
 #if defined (HAVE_UNISTD_H)
 #  include <unistd.h>
 #  include <limits.h>
 #endif
 
-/* Some systems require this, mostly for the definition of `struct timezone'.
-   For example, Dynix/ptx has that definition in <time.h> rather than
-   sys/time.h */
-#if defined (TIME_WITH_SYS_TIME)
-#  include <sys/time.h>
-#  include <time.h>
-#else
-#  if defined (HAVE_SYS_TIME_H)
-#    include <sys/time.h>
-#  else 
-#    include <time.h>
-#  endif
-#endif
+#include "posixtime.h"
 
-#if defined (HAVE_SYS_RESOURCE_H)
+#if defined (HAVE_SYS_RESOURCE_H) && !defined (RLIMTYPE)
 #  include <sys/resource.h>
 #endif
 
@@ -78,10 +70,16 @@ extern int errno;
 #include "hashlib.h"
 #include "jobs.h"
 #include "execute_cmd.h"
+#include "findcmd.h"
+#include "redir.h"
 #include "trap.h"
 #include "pathexp.h"
 #include "hashcmd.h"
 
+#if defined (COND_COMMAND)
+#  include "test.h"
+#endif
+
 #include "builtins/common.h"
 #include "builtins/builtext.h" /* list of builtins */
 
@@ -121,20 +119,23 @@ extern int close ();
 /* Static functions defined and used in this file. */
 static void close_pipes (), do_piping (), bind_lastarg ();
 static void cleanup_redirects ();
-static void add_undo_close_redirect (), add_exec_redirect ();
-static int add_undo_redirect ();
-static int do_redirection_internal (), do_redirections ();
-static int expandable_redirection_filename ();
-static char *find_user_command_internal (), *find_user_command_in_path ();
-static char *find_in_path_element (), *find_absolute_program ();
 
 static int execute_for_command ();
 #if defined (SELECT_COMMAND)
 static int execute_select_command ();
 #endif
+#if defined (DPAREN_ARITHMETIC)
+static int execute_arith_command ();
+#endif
+#if defined (COND_COMMAND)
+static int execute_cond_command ();
+#endif
 #if defined (COMMAND_TIMING)
 static int time_command ();
 #endif
+#if defined (ARITH_FOR_COMMAND)
+static int execute_arith_for_command ();
+#endif
 static int execute_case_command ();
 static int execute_while_command (), execute_until_command ();
 static int execute_while_or_until ();
@@ -148,6 +149,8 @@ static void execute_disk_command ();
 static int execute_connection ();
 static int execute_intern_function ();
 
+static int execute_in_subshell ();
+
 /* The line number that the currently executing function starts on. */
 static int function_line_number;
 
@@ -165,21 +168,6 @@ struct stat SB;            /* used for debugging */
 
 static int special_builtin_failed;
 
-/* Spare redirector used when translating [N]>&WORD or [N]<&WORD to a new
-   redirection and when creating the redirection undo list. */
-static REDIRECTEE rd;
-
-/* Set to errno when a here document cannot be created for some reason.
-   Used to print a reasonable error message. */
-static int heredoc_errno;
-
-/* The file name which we would try to execute, except that it isn't
-   possible to execute it.  This is the first file that matches the
-   name that we are looking for while we are searching $PATH for a
-   suitable one to execute.  If we cannot find a suitable executable
-   file, then we use this one. */
-static char *file_to_lose_on;
-
 /* For catching RETURN in a function. */
 int return_catch_flag;
 int return_catch_value;
@@ -201,9 +189,8 @@ REDIRECT *exec_redirection_undo_list = (REDIRECT *)NULL;
    environment. */
 int subshell_environment;
 
-/* Non-zero if we should stat every command found in the hash table to
-   make sure it still exists. */
-int check_hashed_filenames;
+/* Currently-executing shell function. */
+SHELL_VAR *this_shell_function;
 
 struct fd_bitmap *current_fds_to_close = (struct fd_bitmap *)NULL;
 
@@ -296,7 +283,10 @@ execute_command (command)
   discard_unwind_frame ("execute-command");
 
 #if defined (PROCESS_SUBSTITUTION)
-  unlink_fifo_list ();
+  /* don't unlink fifos if we're in a shell function; wait until the function
+     returns. */
+  if (variable_context == 0)
+    unlink_fifo_list ();
 #endif /* PROCESS_SUBSTITUTION */
 
   return (result);
@@ -310,9 +300,18 @@ shell_control_structure (type)
   switch (type)
     {
     case cm_for:
+#if defined (ARITH_FOR_COMMAND)
+    case cm_arith_for:
+#endif
 #if defined (SELECT_COMMAND)
     case cm_select:
 #endif
+#if defined (DPAREN_ARITHMETIC)
+    case cm_arith:
+#endif
+#if defined (COND_COMMAND)
+    case cm_cond:
+#endif
     case cm_case:
     case cm_while:
     case cm_until:
@@ -345,7 +344,7 @@ cleanup_func_redirects (list)
 }
 #endif
 
-static void
+void
 dispose_exec_redirects ()
 {
   if (exec_redirection_undo_list)
@@ -384,41 +383,6 @@ open_files ()
   fprintf (stderr, "\n");
 }
 
-static int
-stdin_redirects (redirs)
-     REDIRECT *redirs;
-{
-  REDIRECT *rp;
-  int n;
-
-  for (n = 0, rp = redirs; rp; rp = rp->next)
-    switch (rp->instruction)
-      {
-      case r_input_direction:
-      case r_inputa_direction:
-      case r_input_output:
-      case r_reading_until:
-      case r_deblank_reading_until:
-       n++;
-        break;
-      case r_duplicating_input:
-      case r_duplicating_input_word:
-      case r_close_this:
-       n += (rp->redirector == 0);
-        break;
-      case r_output_direction:
-      case r_appending_to:
-      case r_duplicating_output:
-      case r_err_and_out:
-      case r_output_force:
-      case r_duplicating_output_word:
-       break;
-      }
-
-  return n;
-}
-
-
 static void
 async_redirect_stdin ()
 {
@@ -458,7 +422,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 {
   int exec_result, invert, ignore_return, was_debug_trap;
   REDIRECT *my_undo_list, *exec_undo_list;
-  pid_t last_pid;
+  volatile pid_t last_pid;
 
   if (command == 0 || breaking || continuing || read_but_dont_execute)
     return (EXECUTION_SUCCESS);
@@ -481,8 +445,11 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
   /* If a command was being explicitly run in a subshell, or if it is
      a shell control-structure, and it has a pipe, then we do the command
      in a subshell. */
+  if (command->type == cm_subshell && (command->flags & CMD_NO_FORK))
+    return (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
 
-  if ((command->flags & (CMD_WANT_SUBSHELL|CMD_FORCE_SUBSHELL)) ||
+  if (command->type == cm_subshell ||
+      (command->flags & (CMD_WANT_SUBSHELL|CMD_FORCE_SUBSHELL)) ||
       (shell_control_structure (command->type) &&
        (pipe_out != NO_PIPE || pipe_in != NO_PIPE || asynchronous)))
     {
@@ -493,128 +460,8 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
       paren_pid = make_child (savestring (make_command_string (command)),
                              asynchronous);
       if (paren_pid == 0)
-       {
-         int user_subshell, return_code, function_value, should_redir_stdin;
-
-         should_redir_stdin = (asynchronous && (command->flags & CMD_STDIN_REDIR) &&
-                                 pipe_in == NO_PIPE &&
-                                 stdin_redirects (command->redirects) == 0);
-
-         user_subshell = (command->flags & CMD_WANT_SUBSHELL) != 0;
-         command->flags &= ~(CMD_FORCE_SUBSHELL | CMD_WANT_SUBSHELL | CMD_INVERT_RETURN);
-
-         /* If a command is asynchronous in a subshell (like ( foo ) & or
-            the special case of an asynchronous GROUP command where the
-            the subshell bit is turned on down in case cm_group: below),
-            turn off `asynchronous', so that two subshells aren't spawned.
-
-            This seems semantically correct to me.  For example,
-            ( foo ) & seems to say ``do the command `foo' in a subshell
-            environment, but don't wait for that subshell to finish'',
-            and "{ foo ; bar } &" seems to me to be like functions or
-            builtins in the background, which executed in a subshell
-            environment.  I just don't see the need to fork two subshells. */
-
-         /* Don't fork again, we are already in a subshell.  A `doubly
-            async' shell is not interactive, however. */
-         if (asynchronous)
-           {
-#if defined (JOB_CONTROL)
-             /* If a construct like ( exec xxx yyy ) & is given while job
-                control is active, we want to prevent exec from putting the
-                subshell back into the original process group, carefully
-                undoing all the work we just did in make_child. */
-             original_pgrp = -1;
-#endif /* JOB_CONTROL */
-             interactive_shell = 0;
-             expand_aliases = 0;
-             asynchronous = 0;
-           }
-
-         /* Subshells are neither login nor interactive. */
-         login_shell = interactive = 0;
-
-         subshell_environment = user_subshell ? SUBSHELL_PAREN : SUBSHELL_ASYNC;
-
-         reset_terminating_signals ();         /* in shell.c */
-         /* Cancel traps, in trap.c. */
-         restore_original_signals ();
-         if (asynchronous)
-           setup_async_signals ();
-
-#if defined (JOB_CONTROL)
-         set_sigchld_handler ();
-#endif /* JOB_CONTROL */
-
-         set_sigint_handler ();
-
-#if defined (JOB_CONTROL)
-         /* Delete all traces that there were any jobs running.  This is
-            only for subshells. */
-         without_job_control ();
-#endif /* JOB_CONTROL */
-         do_piping (pipe_in, pipe_out);
-
-         /* If this is a user subshell, set a flag if stdin was redirected.
-            This is used later to decide whether to redirect fd 0 to
-            /dev/null for async commands in the subshell.  This adds more
-            sh compatibility, but I'm not sure it's the right thing to do. */
-         if (user_subshell)
-           {
-             stdin_redir = stdin_redirects (command->redirects);
-             restore_default_signal (0);
-           }
-
-         if (fds_to_close)
-           close_fd_bitmap (fds_to_close);
-
-         /* If this is an asynchronous command (command &), we want to
-            redirect the standard input from /dev/null in the absence of
-            any specific redirection involving stdin. */
-         if (should_redir_stdin && stdin_redir == 0)
-           async_redirect_stdin ();
-
-         /* Do redirections, then dispose of them before recursive call. */
-         if (command->redirects)
-           {
-             if (do_redirections (command->redirects, 1, 0, 0) != 0)
-               exit (EXECUTION_FAILURE);
-
-             dispose_redirects (command->redirects);
-             command->redirects = (REDIRECT *)NULL;
-           }
-
-         /* If this is a simple command, tell execute_disk_command that it
-            might be able to get away without forking and simply exec.
-            This means things like ( sleep 10 ) will only cause one fork. */
-         if (user_subshell && command->type == cm_simple)
-           {
-             command->flags |= CMD_NO_FORK;
-             command->value.Simple->flags |= CMD_NO_FORK;
-           }
-
-         /* If we're inside a function while executing this subshell, we
-            need to handle a possible `return'. */
-         function_value = 0;
-         if (return_catch_flag)
-           function_value = setjmp (return_catch);
-
-         if (function_value)
-           return_code = return_catch_value;
-         else
-           return_code = execute_command_internal
-             (command, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
-
-         /* If we were explicitly placed in a subshell with (), we need
-            to do the `shell cleanup' things, such as running traps[0]. */
-         if (user_subshell && signal_is_trapped (0))
-           {
-             last_command_exit_value = return_code;
-             return_code = run_exit_trap ();
-           }
-
-         exit (return_code);
-       }
+       exit (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
+       /* NOTREACHED */
       else
        {
          close_pipes (pipe_in, pipe_out);
@@ -638,11 +485,13 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 
              /* If we have to, invert the return value. */
              if (invert)
-               return ((last_command_exit_value == EXECUTION_SUCCESS)
-                         ? EXECUTION_FAILURE
-                         : EXECUTION_SUCCESS);
+               exec_result = ((last_command_exit_value == EXECUTION_SUCCESS)
+                               ? EXECUTION_FAILURE
+                               : EXECUTION_SUCCESS);
              else
-               return (last_command_exit_value);
+               exec_result = last_command_exit_value;
+
+             return (last_command_exit_value = exec_result);
            }
          else
            {
@@ -769,14 +618,24 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
               commands, this causes the last simple command in
               the function to be waited for twice. */
              exec_result = wait_for (last_made_pid);
+#if defined (RECYCLES_PIDS)
+             /* LynxOS, for one, recycles pids very quickly -- so quickly
+                that a new process may have the same pid as the last one
+                created.  This has been reported to fix the problem. */
+             if (exec_result == 0)
+               last_made_pid = NO_PID;
+#endif
          }
       }
 
       if (was_debug_trap)
-       run_debug_trap ();
+       {
+         last_command_exit_value = exec_result;
+         run_debug_trap ();
+       }
 
       if (ignore_return == 0 && invert == 0 &&
-          ((posixly_correct && interactive == 0 && special_builtin_failed) ||
+         ((posixly_correct && interactive == 0 && special_builtin_failed) ||
           (exit_immediately_on_error && (exec_result != EXECUTION_SUCCESS))))
        {
          last_command_exit_value = exec_result;
@@ -792,6 +651,14 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
       exec_result = execute_for_command (command->value.For);
       break;
 
+#if defined (ARITH_FOR_COMMAND)
+    case cm_arith_for:
+      if (ignore_return)
+       command->value.ArithFor->flags |= CMD_IGNORE_RETURN;
+      exec_result = execute_arith_for_command (command->value.ArithFor);
+      break;
+#endif
+
 #if defined (SELECT_COMMAND)
     case cm_select:
       if (ignore_return)
@@ -873,14 +740,29 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
                                        pipe_in, pipe_out, fds_to_close);
       break;
 
+#if defined (DPAREN_ARITHMETIC)
+    case cm_arith:
+      if (ignore_return)
+       command->value.Arith->flags |= CMD_IGNORE_RETURN;
+      exec_result = execute_arith_command (command->value.Arith);
+      break;
+#endif
+
+#if defined (COND_COMMAND)
+    case cm_cond:
+      if (ignore_return)
+       command->value.Cond->flags |= CMD_IGNORE_RETURN;
+      exec_result = execute_cond_command (command->value.Cond);
+      break;
+#endif
+    
     case cm_function_def:
       exec_result = execute_intern_function (command->value.Function_def->name,
                                             command->value.Function_def->command);
       break;
 
     default:
-      programming_error
-       ("execute_command: bad command type `%d'", command->type);
+      command_error ("execute_command", CMDERR_BADTYPE, command->type, 0);
     }
 
   if (my_undo_list)
@@ -909,74 +791,12 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 }
 
 #if defined (COMMAND_TIMING)
-#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
-static struct timeval *
-difftimeval (d, t1, t2)
-     struct timeval *d, *t1, *t2;
-{
-  d->tv_sec = t2->tv_sec - t1->tv_sec;
-  d->tv_usec = t2->tv_usec - t1->tv_usec;
-  if (d->tv_usec < 0)
-    {
-      d->tv_usec += 1000000;
-      d->tv_sec -= 1;
-      if (d->tv_sec < 0)               /* ??? -- BSD/OS does this */
-       d->tv_sec = 0;
-    }
-  return d;
-}
-
-static struct timeval *
-addtimeval (d, t1, t2)
-     struct timeval *d, *t1, *t2;
-{
-  d->tv_sec = t1->tv_sec + t2->tv_sec;
-  d->tv_usec = t1->tv_usec + t2->tv_usec;
-  if (d->tv_usec > 1000000)
-    {
-      d->tv_usec -= 1000000;
-      d->tv_sec += 1;
-    }
-  return d;
-}
-
-/* Do "cpu = ((user + sys) * 10000) / real;" with timevals.
-   Barely-tested code from Deven T. Corzine <deven@ties.org>. */
-static int
-timeval_to_cpu (rt, ut, st)
-     struct timeval *rt, *ut, *st;     /* real, user, sys */
-{
-  struct timeval t1, t2;
-  register int i;
-
-  addtimeval (&t1, ut, st);
-  t2.tv_sec = rt->tv_sec;
-  t2.tv_usec = rt->tv_usec;
-
-  for (i = 0; i < 6; i++)
-    {
-      if ((t1.tv_sec > 99999999) || (t2.tv_sec > 99999999))
-       break;
-      t1.tv_sec *= 10;
-      t1.tv_sec += t1.tv_usec / 100000;
-      t1.tv_usec *= 10;
-      t1.tv_usec %= 1000000;
-      t2.tv_sec *= 10;
-      t2.tv_sec += t2.tv_usec / 100000;
-      t2.tv_usec *= 10;
-      t2.tv_usec %= 1000000;
-    }
-  for (i = 0; i < 4; i++)
-    {
-      if (t1.tv_sec < 100000000)
-       t1.tv_sec *= 10;
-      else
-        t2.tv_sec /= 10;
-    }
 
-  return ((t2.tv_sec == 0) ? 0 : t1.tv_sec / t2.tv_sec);
-}  
-#endif /* HAVE_GETRUSAGE && HAVE_GETTIMEOFDAY */
+#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
+extern struct timeval *difftimeval();
+extern struct timeval *addtimeval();
+extern int timeval_to_cpu();
+#endif
 
 #define POSIX_TIMEFORMAT "real %2R\nuser %2U\nsys %2S"
 #define BASH_TIMEFORMAT  "\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS"
@@ -1009,7 +829,7 @@ mkfmt (buf, prec, lng, sec, sec_fraction)
       while (min /= 10);
       aind++;
       while (abuf[aind])
-        buf[ind++] = abuf[aind++];
+       buf[ind++] = abuf[aind++];
       buf[ind++] = 'm';
     }
 
@@ -1068,7 +888,8 @@ print_formatted_time (fp, format, rs, rsf, us, usf, ss, ssf, cpu)
 {
   int prec, lng, len;
   char *str, *s, ts[32];
-  int sum, sum_frac;
+  long sum;
+  int sum_frac;
   int sindex, ssize;
 
   len = strlen (format);
@@ -1079,16 +900,16 @@ print_formatted_time (fp, format, rs, rsf, us, usf, ss, ssf, cpu)
   for (s = format; *s; s++)
     {
       if (*s != '%' || s[1] == '\0')
-        {
-          RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64);
-          str[sindex++] = *s;
-        }
+       {
+         RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64);
+         str[sindex++] = *s;
+       }
       else if (s[1] == '%')
-        {
-          s++;
-          RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64);
-          str[sindex++] = *s;
-        }
+       {
+         s++;
+         RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64);
+         str[sindex++] = *s;
+       }
       else if (s[1] == 'P')
        {
          s++;
@@ -1182,6 +1003,9 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
   rv = execute_command_internal (command, asynchronous, pipe_in, pipe_out, fds_to_close);
   command->flags = old_flags;
 
+  rs = us = ss = 0L;
+  rsf = usf = ssf = cpu = 0;
+
 #if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
   gettimeofday (&after, &dtz);
   getrusage (RUSAGE_SELF, &selfa);
@@ -1232,6 +1056,165 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
 }
 #endif /* COMMAND_TIMING */
 
+/* Execute a command that's supposed to be in a subshell.  This must be
+   called after make_child and we must be running in the child process.
+   The caller will return or exit() immediately with the value this returns. */
+static int
+execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
+     COMMAND *command;
+     int asynchronous;
+     int pipe_in, pipe_out;
+     struct fd_bitmap *fds_to_close;
+{
+  int user_subshell, return_code, function_value, should_redir_stdin, invert;
+  int ois;
+  COMMAND *tcom;
+
+  should_redir_stdin = (asynchronous && (command->flags & CMD_STDIN_REDIR) &&
+                         pipe_in == NO_PIPE &&
+                         stdin_redirects (command->redirects) == 0);
+
+  invert = (command->flags & CMD_INVERT_RETURN) != 0;
+  user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0);
+
+  command->flags &= ~(CMD_FORCE_SUBSHELL | CMD_WANT_SUBSHELL | CMD_INVERT_RETURN);
+
+  /* If a command is asynchronous in a subshell (like ( foo ) & or
+     the special case of an asynchronous GROUP command where the
+     the subshell bit is turned on down in case cm_group: below),
+     turn off `asynchronous', so that two subshells aren't spawned.
+
+     This seems semantically correct to me.  For example,
+     ( foo ) & seems to say ``do the command `foo' in a subshell
+     environment, but don't wait for that subshell to finish'',
+     and "{ foo ; bar ; } &" seems to me to be like functions or
+     builtins in the background, which executed in a subshell
+     environment.  I just don't see the need to fork two subshells. */
+
+  /* Don't fork again, we are already in a subshell.  A `doubly
+     async' shell is not interactive, however. */
+  if (asynchronous)
+    {
+#if defined (JOB_CONTROL)
+      /* If a construct like ( exec xxx yyy ) & is given while job
+        control is active, we want to prevent exec from putting the
+        subshell back into the original process group, carefully
+        undoing all the work we just did in make_child. */
+      original_pgrp = -1;
+#endif /* JOB_CONTROL */
+      ois = interactive_shell;
+      interactive_shell = 0;
+      /* This test is to prevent alias expansion by interactive shells that
+        run `(command) &' but to allow scripts that have enabled alias
+        expansion with `shopt -s expand_alias' to continue to expand
+        aliases. */
+      if (ois != interactive_shell)
+       expand_aliases = 0;
+      asynchronous = 0;
+    }
+
+  /* Subshells are neither login nor interactive. */
+  login_shell = interactive = 0;
+
+  subshell_environment = user_subshell ? SUBSHELL_PAREN : SUBSHELL_ASYNC;
+
+  reset_terminating_signals ();                /* in sig.c */
+  /* Cancel traps, in trap.c. */
+  restore_original_signals ();
+  if (asynchronous)
+    setup_async_signals ();
+
+#if defined (JOB_CONTROL)
+  set_sigchld_handler ();
+#endif /* JOB_CONTROL */
+
+  set_sigint_handler ();
+
+#if defined (JOB_CONTROL)
+  /* Delete all traces that there were any jobs running.  This is
+     only for subshells. */
+  without_job_control ();
+#endif /* JOB_CONTROL */
+
+  if (fds_to_close)
+    close_fd_bitmap (fds_to_close);
+
+  do_piping (pipe_in, pipe_out);
+
+  /* If this is a user subshell, set a flag if stdin was redirected.
+     This is used later to decide whether to redirect fd 0 to
+     /dev/null for async commands in the subshell.  This adds more
+     sh compatibility, but I'm not sure it's the right thing to do. */
+  if (user_subshell)
+    {
+      stdin_redir = stdin_redirects (command->redirects);
+      restore_default_signal (0);
+    }
+
+  /* If this is an asynchronous command (command &), we want to
+     redirect the standard input from /dev/null in the absence of
+     any specific redirection involving stdin. */
+  if (should_redir_stdin && stdin_redir == 0)
+    async_redirect_stdin ();
+
+  /* Do redirections, then dispose of them before recursive call. */
+  if (command->redirects)
+    {
+      if (do_redirections (command->redirects, 1, 0, 0) != 0)
+       exit (invert ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
+
+      dispose_redirects (command->redirects);
+      command->redirects = (REDIRECT *)NULL;
+    }
+
+  tcom = (command->type == cm_subshell) ? command->value.Subshell->command : command;
+
+  /* If this is a simple command, tell execute_disk_command that it
+     might be able to get away without forking and simply exec.
+     This means things like ( sleep 10 ) will only cause one fork.
+     If we're timing the command or inverting its return value, however,
+     we cannot do this optimization. */
+  if (user_subshell && (tcom->type == cm_simple || tcom->type == cm_subshell) &&
+      ((tcom->flags & CMD_TIME_PIPELINE) == 0) &&
+      ((tcom->flags & CMD_INVERT_RETURN) == 0))
+    {
+      tcom->flags |= CMD_NO_FORK;
+      if (tcom->type == cm_simple)
+       tcom->value.Simple->flags |= CMD_NO_FORK;
+    }
+
+  invert = (tcom->flags & CMD_INVERT_RETURN) != 0;
+  tcom->flags &= ~CMD_INVERT_RETURN;
+
+  /* If we're inside a function while executing this subshell, we
+     need to handle a possible `return'. */
+  function_value = 0;
+  if (return_catch_flag)
+    function_value = setjmp (return_catch);
+
+  if (function_value)
+    return_code = return_catch_value;
+  else
+    return_code = execute_command_internal
+      (tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
+
+  /* If we are asked to, invert the return value. */
+  if (invert)
+    return_code = (return_code == EXECUTION_SUCCESS) ? EXECUTION_FAILURE
+                                                    : EXECUTION_SUCCESS;
+
+  /* If we were explicitly placed in a subshell with (), we need
+     to do the `shell cleanup' things, such as running traps[0]. */
+  if (user_subshell && signal_is_trapped (0))
+    {
+      last_command_exit_value = return_code;
+      return_code = run_exit_trap ();
+    }
+
+  return (return_code);
+  /* NOTREACHED */
+}
+
 static int
 execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
      COMMAND *command;
@@ -1297,7 +1280,7 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
       fd_bitmap->bitmap[fildes[0]] = 1;
 
       /* In case there are pipe or out-of-processes errors, we
-         want all these file descriptors to be closed when
+        want all these file descriptors to be closed when
         unwind-protects are run, and the storage used for the
         bitmaps freed up. */
       begin_unwind_frame ("pipe-file-descriptors");
@@ -1484,7 +1467,7 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
       break;
 
     default:
-      programming_error ("execute_connection: bad connector `%d'", command->value.Connection->connector);
+      command_error ("execute_connection", CMDERR_BADCONN, command->value.Connection->connector, 0);
       jump_to_top_level (DISCARD);
       exec_result = EXECUTION_FAILURE;
     }
@@ -1492,24 +1475,13 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
   return exec_result;
 }
 
-#if defined (JOB_CONTROL)
-#  define REAP() \
-       do \
-         { \
-           if (!interactive_shell) \
-             reap_dead_jobs (); \
-         } \
-       while (0)
-#else /* !JOB_CONTROL */
-#  define REAP() \
-       do \
-         { \
-           if (!interactive_shell) \
-             cleanup_dead_jobs (); \
-         } \
-       while (0)
-#endif /* !JOB_CONTROL */
-
+#define REAP() \
+  do \
+    { \
+      if (!interactive_shell) \
+       reap_dead_jobs (); \
+    } \
+  while (0)
 
 /* Execute a FOR command.  The syntax is: FOR word_desc IN word_list;
    DO command; DONE */
@@ -1528,10 +1500,10 @@ execute_for_command (for_command)
   if (check_identifier (for_command->name, 1) == 0)
     {
       if (posixly_correct && interactive_shell == 0)
-        {
-          last_command_exit_value = EX_USAGE;
-          jump_to_top_level (EXITPROG);
-        }
+       {
+         last_command_exit_value = EX_USAGE;
+         jump_to_top_level (EXITPROG);
+       }
       return (EXECUTION_FAILURE);
     }
 
@@ -1560,9 +1532,9 @@ execute_for_command (for_command)
       QUIT;
       this_command_name = (char *)NULL;
       v = bind_variable (identifier, list->word->word);
-      if (readonly_p (v))
+      if (readonly_p (v) || noassign_p (v))
        {
-         if (interactive_shell == 0 && posixly_correct)
+         if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
            {
              last_command_exit_value = EXECUTION_FAILURE;
              jump_to_top_level (FORCE_EOF);
@@ -1615,6 +1587,107 @@ execute_for_command (for_command)
   return (retval);
 }
 
+#if defined (ARITH_FOR_COMMAND)
+/* Execute an arithmetic for command.  The syntax is
+
+       for (( init ; step ; test ))
+       do
+               body
+       done
+
+   The execution should be exactly equivalent to
+
+       eval \(\( init \)\)
+       while eval \(\( test \)\) ; do
+               body;
+               eval \(\( step \)\)
+       done
+*/
+static long
+eval_arith_for_expr (l, okp)
+     WORD_LIST *l;
+     int *okp;
+{
+  WORD_LIST *new;
+  long expresult;
+
+  new = expand_words_no_vars (l);
+  if (echo_command_at_execute)
+    xtrace_print_arith_cmd (new);
+  expresult = evalexp (new->word->word, okp);
+  dispose_words (new);
+  return (expresult);
+}
+
+static int
+execute_arith_for_command (arith_for_command)
+     ARITH_FOR_COM *arith_for_command;
+{
+  long expresult;
+  int expok, result, body_status;
+
+  body_status = EXECUTION_SUCCESS;
+  loop_level++;
+
+  if (arith_for_command->flags & CMD_IGNORE_RETURN)
+    arith_for_command->action->flags |= CMD_IGNORE_RETURN;
+
+  this_command_name = "((";    /* )) for expression error messages */
+
+  if (variable_context)
+    line_number = arith_for_command->line - function_line_number;
+
+  /* Evaluate the initialization expression. */
+  expresult = eval_arith_for_expr (arith_for_command->init, &expok);
+  if (expok == 0)
+    return (EXECUTION_FAILURE);
+
+  while (1)
+    {
+      /* Evaluate the test expression. */
+      expresult = eval_arith_for_expr (arith_for_command->test, &expok);
+      if (expok == 0)
+       {
+         body_status = EXECUTION_FAILURE;
+         break;
+       }
+      REAP ();
+      if (expresult == 0)
+       break;
+
+      /* Execute the body of the arithmetic for command. */
+      QUIT;
+      body_status = execute_command (arith_for_command->action);
+      QUIT;
+
+      /* Handle any `break' or `continue' commands executed by the body. */
+      if (breaking)
+       {
+         breaking--;
+         break;
+       }
+
+      if (continuing)
+       {
+         continuing--;
+         if (continuing)
+           break;
+       }
+
+      /* Evaluate the step expression. */
+      expresult = eval_arith_for_expr (arith_for_command->step, &expok);
+      if (expok == 0)
+       {
+         body_status = EXECUTION_FAILURE;
+         break;
+       }
+    }
+
+  loop_level--;
+  return (body_status);
+}
+#endif
+
 #if defined (SELECT_COMMAND)
 static int LINES, COLS, tabsize;
 
@@ -1716,15 +1789,16 @@ print_select_list (list, list_len, max_elem_len, indices_len)
 /* Print the elements of LIST, one per line, preceded by an index from 1 to
    LIST_LEN.  Then display PROMPT and wait for the user to enter a number.
    If the number is between 1 and LIST_LEN, return that selection.  If EOF
-   is read, return a null string.  If a blank line is entered, the loop is
-   executed again. */
+   is read, return a null string.  If a blank line is entered, or an invalid
+   number is entered, the loop is executed again. */
 static char *
 select_query (list, list_len, prompt)
      WORD_LIST *list;
      int list_len;
      char *prompt;
 {
-  int max_elem_len, indices_len, len, reply;
+  int max_elem_len, indices_len, len;
+  long reply;
   WORD_LIST *l;
   char *repl_string, *t;
 
@@ -1747,7 +1821,7 @@ select_query (list, list_len, prompt)
     {
       len = STRLEN (l->word->word);
       if (len > max_elem_len)
-        max_elem_len = len;
+       max_elem_len = len;
     }
   indices_len = NUMBER_LEN (list_len);
   max_elem_len += indices_len + RP_SPACE_LEN + 2;
@@ -1767,7 +1841,8 @@ select_query (list, list_len, prompt)
       repl_string = get_string_value ("REPLY");
       if (*repl_string == 0)
        continue;
-      reply = atoi (repl_string);
+      if (legal_number (repl_string, &reply) == 0)
+       return "";
       if (reply < 1 || reply > list_len)
        return "";
 
@@ -1832,9 +1907,9 @@ execute_select_command (select_command)
        break;
 
       v = bind_variable (identifier, selection);
-      if (readonly_p (v))
+      if (readonly_p (v) || noassign_p (v))
        {
-         if (interactive_shell == 0 && posixly_correct)
+         if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
            {
              last_command_exit_value = EXECUTION_FAILURE;
              jump_to_top_level (FORCE_EOF);
@@ -1849,12 +1924,12 @@ execute_select_command (select_command)
       return_val = setjmp (return_catch);
 
       if (return_val)
-        {
+       {
          retval = return_catch_value;
          break;
-        }
+       }
       else
-        retval = execute_command (select_command->action);
+       retval = execute_command (select_command->action);
 
       REAP ();
       QUIT;
@@ -1864,6 +1939,13 @@ execute_select_command (select_command)
          breaking--;
          break;
        }
+
+      if (continuing)
+       {
+         continuing--;
+         if (continuing)
+           break;
+       }
     }
 
   loop_level--;
@@ -1895,7 +1977,7 @@ execute_case_command (case_command)
       case_command->word->word = word;
     }
 
-  wlist = expand_word_no_split (case_command->word, 0);
+  wlist = expand_word_unsplit (case_command->word, 0);
   word = wlist ? string_list (wlist) : savestring ("");
   dispose_words (wlist);
 
@@ -1924,7 +2006,7 @@ execute_case_command (case_command)
          es = expand_word_leave_quoted (list->word, 0);
 
          if (es && es->word && es->word->word && *(es->word->word))
-           pattern = quote_string_for_globbing (es->word->word, 1);
+           pattern = quote_string_for_globbing (es->word->word, QGLOB_CVTNULL);
          else
            {
              pattern = xmalloc (1);
@@ -1934,7 +2016,7 @@ execute_case_command (case_command)
          /* Since the pattern does not undergo quote removal (as per
             Posix.2, section 3.9.4.3), the fnmatch () call must be able
             to recognize backslashes as escape characters. */
-         match = fnmatch (pattern, word, 0) != FNM_NOMATCH;
+         match = fnmatch (pattern, word, FNMATCH_EXTFLAG) != FNM_NOMATCH;
          free (pattern);
 
          dispose_words (es);
@@ -2061,16 +2143,145 @@ execute_if_command (if_command)
     }
 }
 
-static void
-bind_lastarg (arg)
-     char *arg;
+#if defined (DPAREN_ARITHMETIC)
+static int
+execute_arith_command (arith_command)
+     ARITH_COM *arith_command;
 {
-  SHELL_VAR *var;
+  int result, expok, expresult;
+  WORD_LIST *new, *p, *printit;
+  WORD_DESC *w;
 
-  if (arg == 0)
-    arg = "";
-  var = bind_variable ("_", arg);
-  var->attributes &= ~att_exported;
+  result = 0;
+
+  this_command_name = "((";    /* )) */
+  /* If we're in a function, update the line number information. */
+  if (variable_context)
+    line_number = arith_command->line - function_line_number;
+
+  new = expand_words (arith_command->exp);
+
+  /* If we're tracing, make a new word list with `((' at the front and `))'
+     at the back and print it. */
+  if (echo_command_at_execute)
+    xtrace_print_arith_cmd (new);
+
+  result = evalexp (new->word->word, &expok);
+  dispose_words (new);
+
+  if (expok == 0)
+    return (EXECUTION_FAILURE);
+
+  return (result == 0 ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
+}
+#endif /* DPAREN_ARITHMETIC */
+
+#if defined (COND_COMMAND)
+
+static char *nullstr = "";
+
+static int
+execute_cond_node (cond)
+     COND_COM *cond;
+{
+  int result, invert, patmatch, flags;
+  char *arg1, *arg2, *print2;
+
+  invert = (cond->flags & CMD_INVERT_RETURN);
+
+  if (cond->type == COND_EXPR)
+    result = execute_cond_node (cond->left);
+  else if (cond->type == COND_OR)
+    {
+      result = execute_cond_node (cond->left);
+      if (result != EXECUTION_SUCCESS)
+       result = execute_cond_node (cond->right);
+    }
+  else if (cond->type == COND_AND)
+    {
+      result = execute_cond_node (cond->left);
+      if (result == EXECUTION_SUCCESS)
+       result = execute_cond_node (cond->right);
+    }
+  else if (cond->type == COND_UNARY)
+    {
+      arg1 = cond_expand_word (cond->left->op, 0);
+      if (arg1 == 0)
+       arg1 = nullstr;
+      if (echo_command_at_execute)
+       xtrace_print_cond_term (cond->type, invert, cond->op, arg1, (char *)NULL);
+      result = unary_test (cond->op->word, arg1) ? EXECUTION_SUCCESS : EXECUTION_FAILURE;
+      if (arg1 != nullstr)
+       free (arg1);
+    }
+  else if (cond->type == COND_BINARY)
+    {
+      patmatch = ((cond->op->word[1] == '=') && (cond->op->word[2] == '\0') &&
+                 (cond->op->word[0] == '!' || cond->op->word[0] == '=') ||
+                 (cond->op->word[0] == '=' && cond->op->word[1] == '\0'));
+
+      arg1 = cond_expand_word (cond->left->op, 0);
+      if (arg1 == 0)
+       arg1 = nullstr;
+      arg2 = cond_expand_word (cond->right->op, patmatch);
+      if (arg2 == 0)
+       arg2 = nullstr;
+
+      if (echo_command_at_execute)
+       xtrace_print_cond_term (cond->type, invert, cond->op, arg1, arg2);
+
+      result = binary_test (cond->op->word, arg1, arg2, TEST_PATMATCH|TEST_ARITHEXP)
+                               ? EXECUTION_SUCCESS
+                               : EXECUTION_FAILURE;
+      if (arg1 != nullstr)
+       free (arg1);
+      if (arg2 != nullstr)
+       free (arg2);
+    }
+  else
+    {
+      command_error ("execute_cond_node", CMDERR_BADTYPE, cond->type, 0);
+      jump_to_top_level (DISCARD);
+      result = EXECUTION_FAILURE;
+    }
+
+  if (invert)
+    result = (result == EXECUTION_SUCCESS) ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
+
+  return result;
+}
+
+static int
+execute_cond_command (cond_command)
+     COND_COM *cond_command;
+{
+  int result;
+
+  result = EXECUTION_SUCCESS;
+
+  this_command_name = "[[";
+  /* If we're in a function, update the line number information. */
+  if (variable_context)
+    line_number = cond_command->line - function_line_number;
+
+#if 0
+  debug_print_cond_command (cond_command);
+#endif
+  last_command_exit_value = result = execute_cond_node (cond_command);  
+  return (result);
+}
+#endif /* COND_COMMAND */
+
+static void
+bind_lastarg (arg)
+     char *arg;
+{
+  SHELL_VAR *var;
+
+  if (arg == 0)
+    arg = "";
+  var = bind_variable ("_", arg);
+  VUNSETATTR (var, att_exported);
 }
 
 /* Execute a null command.  Fork a subshell if the command uses pipes or is
@@ -2079,7 +2290,8 @@ bind_lastarg (arg)
 static int
 execute_null_command (redirects, pipe_in, pipe_out, async, old_last_command_subst_pid)
      REDIRECT *redirects;
-     int pipe_in, pipe_out, async, old_last_command_subst_pid;
+     int pipe_in, pipe_out, async;
+     pid_t old_last_command_subst_pid;
 {
   if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
     {
@@ -2138,13 +2350,13 @@ fix_assignment_words (words)
   if (words == 0)
     return;
 
-  b = builtin_address_internal (words->word->word);
+  b = builtin_address_internal (words->word->word, 0);
   if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
     return;
 
   for (w = words; w; w = w->next)
     if (w->word->flags & W_ASSIGNMENT)
-      w->word->flags |= W_NOSPLIT;
+      w->word->flags |= (W_NOSPLIT|W_NOGLOB);
 }
 
 /* The meaty part of all the executions.  We have to start hacking the
@@ -2158,13 +2370,14 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
 {
   WORD_LIST *words, *lastword;
   char *command_line, *lastarg, *temp;
-  int first_word_quoted, result, builtin_is_special;
-  pid_t old_last_command_subst_pid;
+  int first_word_quoted, result, builtin_is_special, already_forked, dofork;
+  pid_t old_last_command_subst_pid, old_last_async_pid;
   Function *builtin;
   SHELL_VAR *func;
 
   result = EXECUTION_SUCCESS;
   special_builtin_failed = builtin_is_special = 0;
+  command_line = (char *)0;
 
   /* If we're in a function, update the line number information. */
   if (variable_context)
@@ -2173,13 +2386,69 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
   /* Remember what this command line looks like at invocation. */
   command_string_index = 0;
   print_simple_command (simple_command);
-  command_line = xmalloc (1 + strlen (the_printed_command));
-  strcpy (command_line, the_printed_command);  /* XXX memory leak on errors */
 
   first_word_quoted =
     simple_command->words ? (simple_command->words->word->flags & W_QUOTED): 0;
 
   old_last_command_subst_pid = last_command_subst_pid;
+  old_last_async_pid = last_asynchronous_pid;
+
+  already_forked = dofork = 0;
+
+  /* If we're in a pipeline or run in the background, set DOFORK so we
+     make the child early, before word expansion.  This keeps assignment
+     statements from affecting the parent shell's environment when they
+     should not. */
+  dofork = pipe_in != NO_PIPE || pipe_out != NO_PIPE || async;
+
+  /* Something like `%2 &' should restart job 2 in the background, not cause
+     the shell to fork here. */
+  if (dofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE &&
+       simple_command->words && simple_command->words->word &&
+       simple_command->words->word->word &&
+       (simple_command->words->word->word[0] == '%'))
+    dofork = 0;
+
+  if (dofork)
+    {
+      /* XXX memory leak if expand_words() error causes a jump_to_top_level */
+      command_line = savestring (the_printed_command);
+
+      /* Do this now, because execute_disk_command will do it anyway in the
+        vast majority of cases. */
+      maybe_make_export_env ();
+
+      if (make_child (command_line, async) == 0)
+       {
+         already_forked = 1;
+         simple_command->flags |= CMD_NO_FORK;
+
+         subshell_environment = (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
+                                       ? (SUBSHELL_PIPE|SUBSHELL_FORK)
+                                       : (SUBSHELL_ASYNC|SUBSHELL_FORK);
+
+         /* We need to do this before piping to handle some really
+            pathological cases where one of the pipe file descriptors
+            is < 2. */
+         if (fds_to_close)
+           close_fd_bitmap (fds_to_close);
+
+         do_piping (pipe_in, pipe_out);
+         pipe_in = pipe_out = -1;
+
+         last_asynchronous_pid = old_last_async_pid;
+       }
+      else
+       {
+         close_pipes (pipe_in, pipe_out);
+#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
+         unlink_fifo_list ();
+#endif
+         command_line = (char *)NULL;      /* don't free this. */
+         bind_lastarg ((char *)NULL);
+         return (result);
+       }
+    }
 
   /* If we are re-running this as the result of executing the `command'
      builtin, do not expand the command words a second time. */
@@ -2199,11 +2468,17 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
   if (words == 0)
     {
       result = execute_null_command (simple_command->redirects,
-                                    pipe_in, pipe_out, async,
+                                    pipe_in, pipe_out,
+                                    already_forked ? 0 : async,
                                     old_last_command_subst_pid);
-      FREE (command_line);
-      bind_lastarg ((char *)NULL);
-      return (result);
+      if (already_forked)
+       exit (result);
+      else
+       {
+         bind_lastarg ((char *)NULL);
+         set_pipestatus_from_exit (result);
+         return (result);
+       }
     }
 
   lastarg = (char *)NULL;
@@ -2243,7 +2518,7 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
 
 #if defined (JOB_CONTROL)
   /* Is this command a job control related thing? */
-  if (words->word->word[0] == '%')
+  if (words->word->word[0] == '%' && already_forked == 0)
     {
       this_command_name = async ? "bg" : "fg";
       last_shell_builtin = this_shell_builtin;
@@ -2255,7 +2530,7 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
   /* One other possiblilty.  The user may want to resume an existing job.
      If they do, find out whether this word is a candidate for a running
      job. */
-  if (job_control && async == 0 &&
+  if (job_control && already_forked == 0 && async == 0 &&
        !first_word_quoted &&
        !words->next &&
        words->word->word[0] &&
@@ -2328,37 +2603,25 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
 
   if (builtin || func)
     {
-      if ((pipe_in != NO_PIPE) || (pipe_out != NO_PIPE) || async)
+      if (already_forked)
        {
-         if (make_child (command_line, async) == 0)
-           {
-             /* reset_terminating_signals (); */       /* XXX */
-             /* Cancel traps, in trap.c. */
-             restore_original_signals ();
-
-             if (async)
-               {
-                 if ((simple_command->flags & CMD_STDIN_REDIR) &&
-                       pipe_in == NO_PIPE &&
-                       (stdin_redirects (simple_command->redirects) == 0))
-                   async_redirect_stdin ();
-                 setup_async_signals ();
-               }
+         /* reset_terminating_signals (); */   /* XXX */
+         /* Cancel traps, in trap.c. */
+         restore_original_signals ();
 
-             execute_subshell_builtin_or_function
-               (words, simple_command->redirects, builtin, func,
-                pipe_in, pipe_out, async, fds_to_close,
-                simple_command->flags);
-           }
-         else
+         if (async)
            {
-             close_pipes (pipe_in, pipe_out);
-#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
-             unlink_fifo_list ();
-#endif
-             command_line = (char *)NULL;      /* don't free this. */
-             goto return_result;
+             if ((simple_command->flags & CMD_STDIN_REDIR) &&
+                   pipe_in == NO_PIPE &&
+                   (stdin_redirects (simple_command->redirects) == 0))
+               async_redirect_stdin ();
+             setup_async_signals ();
            }
+
+         execute_subshell_builtin_or_function
+           (words, simple_command->redirects, builtin, func,
+            pipe_in, pipe_out, async, fds_to_close,
+            simple_command->flags);
        }
       else
        {
@@ -2369,9 +2632,9 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
            {
              if (result > EX_SHERRBASE)
                {
-                 result = builtin_status (result);
-                 if (builtin_is_special)
-                   special_builtin_failed = 1;
+                 result = builtin_status (result);
+                 if (builtin_is_special)
+                   special_builtin_failed = 1;
                }
              /* In POSIX mode, if there are assignment statements preceding
                 a special builtin, they persist after the builtin
@@ -2384,13 +2647,18 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
              if (result == EX_USAGE)
                result = EX_BADUSAGE;
              else if (result > EX_SHERRBASE)
-               result = EXECUTION_FAILURE;
+               result = EXECUTION_FAILURE;
            }
 
+         set_pipestatus_from_exit (result);
+
          goto return_result;
        }
     }
 
+  if (command_line == 0)
+    command_line = savestring (the_printed_command);
+
   execute_disk_command (words, simple_command->redirects, command_line,
                        pipe_in, pipe_out, async, fds_to_close,
                        simple_command->flags);
@@ -2468,14 +2736,26 @@ execute_builtin (builtin, words, flags, subshell)
            add_unwind_protect (dispose_builtin_env, (char *)NULL);
          dispose_used_env_vars ();
        }
-#if 0
-      else
-       builtin_env = (char **)NULL;
-#endif
+      /* Otherwise we inherit builtin_env from our caller. */
+    }
+
+  /* `return' does a longjmp() back to a saved environment in execute_function.
+     If a variable assignment list preceded the command, and the shell is
+     running in POSIX mode, we need to merge that into the shell_variables
+     table, since `return' is a POSIX special builtin. */
+  if (posixly_correct && subshell == 0 && builtin == return_builtin && temporary_env)
+    {
+      begin_unwind_frame ("return_temp_env");
+      add_unwind_protect (merge_temporary_env, (char *)NULL);
     }
 
   result = ((*builtin) (words->next));
 
+  /* This shouldn't happen, but in case `return' comes back instead of
+     longjmp'ing, we need to unwind. */
+  if (posixly_correct && subshell == 0 && builtin == return_builtin && temporary_env)
+    discard_unwind_frame ("return_temp_env");
+
   if (subshell == 0 && (builtin == source_builtin || builtin == eval_builtin))
     {
       /* In POSIX mode, if any variable assignments precede the `.' or
@@ -2483,8 +2763,8 @@ execute_builtin (builtin, words, flags, subshell)
         and `eval' are special builtins. */
       if (posixly_correct && builtin_env)
        merge_builtin_env ();
-      dispose_builtin_env ();
-      discard_unwind_frame ("builtin_env");
+
+      run_unwind_frame ("builtin_env");
     }
 
   if (eval_unwind)
@@ -2506,6 +2786,7 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
   int return_val, result;
   COMMAND *tc, *fc;
   char *debug_trap;
+  SHELL_VAR *old_shell_function;
 
   tc = (COMMAND *)copy_command (function_cell (var));
   if (tc && (flags & CMD_IGNORE_RETURN))
@@ -2520,9 +2801,13 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
       unwind_protect_int (return_catch_flag);
       unwind_protect_jmp_buf (return_catch);
       add_unwind_protect (dispose_command, (char *)tc);
+      unwind_protect_pointer (this_shell_function);
       unwind_protect_int (loop_level);
     }
 
+  this_shell_function = var;
+  make_funcname_visible (1);
+
   debug_trap = (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
                        ? trap_list[DEBUG_TRAP]
                        : (char *)NULL;
@@ -2531,8 +2816,12 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
       if (subshell == 0)
        {
          debug_trap = savestring (debug_trap);
+         /* XXX order is important here!  unwind-protect commands are run
+            in reverse order of registering.  If this causes problems,
+            take out the xfree unwind-protect and live with the small
+            memory leak. */
+         add_unwind_protect (xfree, debug_trap);
          add_unwind_protect (set_debug_trap, debug_trap);
-         /* XXX - small memory leak here -- hard to fix */
        }
       restore_default_signal (DEBUG_TRAP);
     }
@@ -2542,14 +2831,19 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
   if (temporary_env)
     {
       function_env = copy_array (temporary_env);
+      /* In POSIX mode, variable assignments preceding function names are
+        supposed to persist in the environment after the function returns,
+        as if a special builtin command had been executed. */
       if (subshell == 0)
-       add_unwind_protect (dispose_function_env, (char *)NULL);
+       {
+         if (posixly_correct)
+           add_unwind_protect (merge_function_env, (char *)NULL);
+         else
+           add_unwind_protect (dispose_function_env, (char *)NULL);
+       }
       dispose_used_env_vars ();
     }
-#if 0
-  else
-    function_env = (char **)NULL;
-#endif
+    /* Otherwise, we inherit function_env from our caller. */
 
   remember_args (words->next, 1);
 
@@ -2582,9 +2876,34 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
   if (subshell == 0)
     run_unwind_frame ("function_calling");
 
+  if (variable_context == 0 || this_shell_function == 0)
+    make_funcname_visible (0);
+
   return (result);
 }
 
+/* A convenience routine for use by other parts of the shell to execute
+   a particular shell function. */
+int
+execute_shell_function (var, words)
+     SHELL_VAR *var;
+     WORD_LIST *words;
+{
+  int ret;
+  struct fd_bitmap *bitmap;
+
+  bitmap = new_fd_bitmap (FD_BITMAP_DEFAULT_SIZE);
+  begin_unwind_frame ("execute-shell-function");
+  add_unwind_protect (dispose_fd_bitmap, (char *)bitmap);
+      
+  ret = execute_function (var, words, 0, bitmap, 0, 0);
+
+  dispose_fd_bitmap (bitmap);
+  discard_unwind_frame ("execute-shell-function");
+
+  return ret;
+}
+
 /* Execute a shell builtin or function in a subshell environment.  This
    routine does not return; it only calls exit().  If BUILTIN is non-null,
    it points to a function to call to execute a shell builtin; otherwise
@@ -2604,6 +2923,12 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
      int flags;
 {
   int result, r;
+#if defined (JOB_CONTROL)
+  int jobs_hack;
+
+  jobs_hack = (builtin == jobs_builtin) &&
+               ((subshell_environment & SUBSHELL_ASYNC) == 0 || pipe_out != NO_PIPE);
+#endif
 
   /* A subshell is neither a login shell nor interactive. */
   login_shell = interactive = 0;
@@ -2618,8 +2943,7 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
      the shell itself. */
 
   /* Allow the output of `jobs' to be piped. */
-  if (builtin == jobs_builtin && !async &&
-      (pipe_out != NO_PIPE || pipe_in != NO_PIPE))
+  if (jobs_hack)
     kill_current_pipeline ();
   else
     without_job_control ();
@@ -2629,11 +2953,11 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
 
   set_sigint_handler ();
 
-  do_piping (pipe_in, pipe_out);
-
   if (fds_to_close)
     close_fd_bitmap (fds_to_close);
 
+  do_piping (pipe_in, pipe_out);
+
   if (do_redirections (redirects, 1, 0, 0) != 0)
     exit (EXECUTION_FAILURE);
 
@@ -2648,12 +2972,12 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
       else if (result)
        exit (EXECUTION_FAILURE);
       else
-        {
-          r = execute_builtin (builtin, words, flags, 1);
-          if (r == EX_USAGE)
-            r = EX_BADUSAGE;
-          exit (r);
-        }
+       {
+         r = execute_builtin (builtin, words, flags, 1);
+         if (r == EX_USAGE)
+           r = EX_BADUSAGE;
+         exit (r);
+       }
     }
   else
     exit (execute_function (var, words, flags, fds_to_close, async, 1));
@@ -2679,6 +3003,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
 {
   int result;
   REDIRECT *saved_undo_list;
+  Function *saved_this_shell_builtin;
 
   if (do_redirections (redirects, 1, 1, 0) != 0)
     {
@@ -2688,6 +3013,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
       return (EX_REDIRFAIL);   /* was EXECUTION_FAILURE */
     }
 
+  saved_this_shell_builtin = this_shell_builtin;
   saved_undo_list = redirection_undo_list;
 
   /* Calling the "exec" builtin changes redirections forever. */
@@ -2713,6 +3039,21 @@ execute_builtin_or_function (words, builtin, var, redirects,
   else
     result = execute_function (var, words, flags, fds_to_close, 0, 0);
 
+  /* If we are executing the `command' builtin, but this_shell_builtin is
+     set to `exec_builtin', we know that we have something like
+     `command exec [redirection]', since otherwise `exec' would have
+     overwritten the shell and we wouldn't get here.  In this case, we
+     want to behave as if the `command' builtin had not been specified
+     and preserve the redirections. */
+  if (builtin == command_builtin && this_shell_builtin == exec_builtin)
+    {
+      if (saved_undo_list)
+       dispose_redirects (saved_undo_list);
+      redirection_undo_list = exec_redirection_undo_list;
+      saved_undo_list = exec_redirection_undo_list = (REDIRECT *)NULL;      
+      discard_unwind_frame ("saved_redirects");
+    }
+
   if (saved_undo_list)
     {
       redirection_undo_list = saved_undo_list;
@@ -2731,6 +3072,10 @@ execute_builtin_or_function (words, builtin, var, redirects,
 void
 setup_async_signals ()
 {
+#if defined (__BEOS__)
+  set_signal_handler (SIGHUP, SIG_IGN);        /* they want csh-like behavior */
+#endif
+
 #if defined (JOB_CONTROL)
   if (job_control == 0)
 #endif
@@ -2770,7 +3115,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
 {
   char *pathname, *command, **args;
   int nofork;
-  int pid;
+  pid_t pid;
 
   nofork = (cmdflags & CMD_NO_FORK);  /* Don't fork, just exec, if no pipes */
   pathname = words->word->word;
@@ -2793,7 +3138,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
       put_command_name_into_env (command);
     }
 
-  /* We have to make the child before we check for the non-existance
+  /* We have to make the child before we check for the non-existence
      of COMMAND, since we want the error messages to be redirected. */
   /* If we can get away without forking and there are no pipes to deal with,
      don't bother to fork, just directly exec the command. */
@@ -2818,8 +3163,8 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
       restore_original_signals ();
 
       /* restore_original_signals may have undone the work done
-         by make_child to ensure that SIGINT and SIGQUIT are ignored
-         in asynchronous children. */
+        by make_child to ensure that SIGINT and SIGQUIT are ignored
+        in asynchronous children. */
       if (async)
        {
          if ((cmdflags & CMD_STDIN_REDIR) &&
@@ -2829,6 +3174,14 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
          setup_async_signals ();
        }
 
+      /* This functionality is now provided by close-on-exec of the
+        file descriptors manipulated by redirection and piping.
+        Some file descriptors still need to be closed in all children
+        because of the way bash does pipes; fds_to_close is a
+        bitmap of all such file descriptors. */
+      if (fds_to_close)
+       close_fd_bitmap (fds_to_close);
+
       do_piping (pipe_in, pipe_out);
 
       if (async)
@@ -2839,14 +3192,6 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
 
       subshell_environment = SUBSHELL_FORK;
 
-      /* This functionality is now provided by close-on-exec of the
-        file descriptors manipulated by redirection and piping.
-        Some file descriptors still need to be closed in all children
-        because of the way bash does pipes; fds_to_close is a
-        bitmap of all such file descriptors. */
-      if (fds_to_close)
-       close_fd_bitmap (fds_to_close);
-
       if (redirects && (do_redirections (redirects, 1, 0, 0) != 0))
        {
 #if defined (PROCESS_SUBSTITUTION)
@@ -2893,6 +3238,24 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
 
    The word immediately following the #! is the interpreter to execute.
    A single argument to the interpreter is allowed. */
+
+/* CPP defines to decide whether a particular index into the #! line
+   corresponds to a valid interpreter name or argument character, or
+   whitespace.  The MSDOS define is to allow \r to be treated the same
+   as \n. */
+
+#if !defined (MSDOS)
+#  define STRINGCHAR(ind) \
+    (!whitespace (sample[ind]) && sample[ind] != '\n' && ind < sample_len)
+#  define WHITECHAR(ind) \
+    (whitespace (sample[ind]) && sample[ind] != '\n' && ind < sample_len)
+#else  /* MSDOS */
+#  define STRINGCHAR(ind) \
+    (!whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r' && ind < sample_len)
+#  define WHITECHAR(ind) \
+    (whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r' && ind < sample_len)
+#endif /* MSDOS */
+
 static int
 execute_shell_script (sample, sample_len, command, args, env)
      unsigned char *sample;
@@ -2908,37 +3271,24 @@ execute_shell_script (sample, sample_len, command, args, env)
   for (i = 2; whitespace (sample[i]) && i < sample_len; i++)
     ;
 
-  for (start = i;
-       !whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
-       i++)
+  for (start = i; STRINGCHAR(i); i++)
     ;
 
-  larry = i - start;
-  execname = xmalloc (1 + larry);
-  strncpy (execname, (char *)(sample + start), larry);
-  execname[larry] = '\0';
+  execname = substring ((char *)sample, start, i);
   size_increment = 1;
 
   /* Now the argument, if any. */
-  firstarg = (char *)NULL;
-  for (start = i;
-       whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
-       i++)
+  for (firstarg = (char *)NULL, start = i; WHITECHAR(i); i++)
     ;
 
   /* If there is more text on the line, then it is an argument for the
      interpreter. */
-  if (i < sample_len && sample[i] != '\n' && !whitespace (sample[i]))
+
+  if (STRINGCHAR(i))  
     {
-      for (start = i;
-          !whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
-          i++)
+      for (start = i; STRINGCHAR(i); i++)
        ;
-      larry = i - start;
-      firstarg = xmalloc (1 + larry);
-      strncpy (firstarg, (char *)(sample + start), larry);
-      firstarg[larry] = '\0';
-
+      firstarg = substring ((char *)sample, start, i);
       size_increment = 2;
     }
 
@@ -2962,6 +3312,9 @@ execute_shell_script (sample, sample_len, command, args, env)
 
   return (shell_execve (execname, args, env));
 }
+#undef STRINGCHAR
+#undef WHITECHAR
+
 #endif /* !HAVE_HASH_BANG_EXEC */
 
 static void
@@ -2989,21 +3342,21 @@ initialize_subshell ()
   reset_shell_options ();
   reset_shopt_options ();
 
+  /* Zero out builtin_env, since this could be a shell script run from a
+     sourced file with a temporary environment supplied to the `source/.'
+     builtin.  Such variables are not supposed to be exported (empirical
+     testing with sh and ksh). */
+  builtin_env = 0;
+
+  clear_unwind_protect_list (0);
+
+  /* We're no longer inside a shell function. */
+  variable_context = return_catch_flag = 0;
+
   /* If we're not interactive, close the file descriptor from which we're
      reading the current shell script. */
-#if defined (BUFFERED_INPUT)
-  if (interactive_shell == 0 && default_buffered_input >= 0)
-    {
-      close_buffered_fd (default_buffered_input);
-      default_buffered_input = bash_input.location.buffered_fd = -1;
-    }
-#else
-  if (interactive_shell == 0 && default_input)
-    {
-      fclose (default_input);
-      default_input = (FILE *)NULL;
-    }
-#endif
+  if (interactive_shell == 0)
+    unset_bash_input (1);
 }
 
 #if defined (HAVE_SETOSTYPE) && defined (_POSIX_SOURCE)
@@ -3012,6 +3365,20 @@ initialize_subshell ()
 #  define SETOSTYPE(x)
 #endif
 
+#define READ_SAMPLE_BUF(file, buf, len) \
+  do \
+    { \
+      fd = open(file, O_RDONLY); \
+      if (fd >= 0) \
+       { \
+         len = read (fd, (char *)buf, 80); \
+         close (fd); \
+       } \
+      else \
+       len = -1; \
+    } \
+  while (0)
+      
 /* Call execve (), handling interpreting shell scripts, and handling
    exec failures. */
 int
@@ -3021,20 +3388,31 @@ shell_execve (command, args, env)
 {
   struct stat finfo;
   int larray, i, fd;
+  unsigned char sample[80];
+  int sample_len;
 
   SETOSTYPE (0);               /* Some systems use for USG/POSIX semantics */
   execve (command, args, env);
+  i = errno;                   /* error from execve() */
   SETOSTYPE (1);
 
   /* If we get to this point, then start checking out the file.
      Maybe it is something we can hack ourselves. */
-  if (errno != ENOEXEC)
+  if (i != ENOEXEC)
     {
-      i = errno;
       if ((stat (command, &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
        internal_error ("%s: is a directory", command);
       else
        {
+#if defined (HAVE_HASH_BANG_EXEC)
+         READ_SAMPLE_BUF (command, sample, sample_len);
+         if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
+           {
+             errno = i;
+             sys_error ("%s: bad interpreter", command);
+             return (EX_NOEXEC);
+           }
+#endif
          errno = i;
          file_error (command);
        }
@@ -3044,41 +3422,37 @@ shell_execve (command, args, env)
   /* This file is executable.
      If it begins with #!, then help out people with losing operating
      systems.  Otherwise, check to see if it is a binary file by seeing
-     if the first line (or up to 80 characters) are in the ASCII set.
-     Execute the contents as shell commands. */
-  fd = open (command, O_RDONLY);
-  if (fd >= 0)
-    {
-      unsigned char sample[80];
-      int sample_len;
-
-      sample_len = read (fd, (char *)sample, 80);
-      close (fd);
+     if the contents of the first line (or up to 80 characters) are in the
+     ASCII set.  If it's a text file, execute the contents as shell commands,
+     otherwise return 126 (EX_BINARY_FILE). */
+  READ_SAMPLE_BUF (command, sample, sample_len);
 
-      if (sample_len == 0)
-       return (EXECUTION_SUCCESS);
+  if (sample_len == 0)
+    return (EXECUTION_SUCCESS);
 
-      /* Is this supposed to be an executable script?
-        If so, the format of the line is "#! interpreter [argument]".
-        A single argument is allowed.  The BSD kernel restricts
-        the length of the entire line to 32 characters (32 bytes
-        being the size of the BSD exec header), but we allow 80
-        characters. */
-      if (sample_len > 0)
-       {
+  /* Is this supposed to be an executable script?
+     If so, the format of the line is "#! interpreter [argument]".
+     A single argument is allowed.  The BSD kernel restricts
+     the length of the entire line to 32 characters (32 bytes
+     being the size of the BSD exec header), but we allow 80
+     characters. */
+  if (sample_len > 0)
+    {
 #if !defined (HAVE_HASH_BANG_EXEC)
-         if (sample[0] == '#' && sample[1] == '!')
-           return (execute_shell_script (sample, sample_len, command, args, env));
-         else
+      if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
+       return (execute_shell_script (sample, sample_len, command, args, env));
+      else
 #endif
-         if (check_binary_file (sample, sample_len))
-           {
-             internal_error ("%s: cannot execute binary file", command);
-             return (EX_BINARY_FILE);
-           }
+      if (check_binary_file (sample, sample_len))
+       {
+         internal_error ("%s: cannot execute binary file", command);
+         return (EX_BINARY_FILE);
        }
     }
 
+  /* We have committed to attempting to execute the contents of this file
+     as shell commands. */
+
   initialize_subshell ();
 
   set_sigint_handler ();
@@ -3140,9 +3514,10 @@ execute_intern_function (name, function)
     }
 
   var = find_function (name->word);
-  if (var && readonly_p (var))
+  if (var && (readonly_p (var) || noassign_p (var)))
     {
-      internal_error ("%s: readonly function", var->name);
+      if (readonly_p (var))
+       internal_error ("%s: readonly function", var->name);
       return (EXECUTION_FAILURE);
     }
 
@@ -3188,7 +3563,7 @@ do_piping (pipe_in, pipe_out)
       if (dup2 (pipe_in, 0) < 0)
        sys_error ("cannot duplicate fd %d to fd 0", pipe_in);
       if (pipe_in > 0)
-        close (pipe_in);
+       close (pipe_in);
     }
   if (pipe_out != NO_PIPE)
     {
@@ -3200,1224 +3575,9 @@ do_piping (pipe_in, pipe_out)
            close (pipe_out);
        }
       else
-       if (dup2 (1, 2) < 0)
-         sys_error ("cannot duplicate fd 1 to fd 2");
-    }
-}
-
-static void
-redirection_error (temp, error)
-     REDIRECT *temp;
-     int error;
-{
-  char *filename;
-
-  if (expandable_redirection_filename (temp))
-    {
-      if (posixly_correct && !interactive_shell)
-        disallow_filename_globbing++;
-      filename = redirection_expand (temp->redirectee.filename);
-      if (posixly_correct && !interactive_shell)
-        disallow_filename_globbing--;
-      if (filename == 0)
-       filename = savestring (temp->redirectee.filename->word);
-      if (filename == 0)
-       {
-         filename = xmalloc (1);
-         filename[0] = '\0';
-       }
-    }
-  else
-    filename = itos (temp->redirectee.dest);
-
-  switch (error)
-    {
-    case AMBIGUOUS_REDIRECT:
-      internal_error ("%s: ambiguous redirect", filename);
-      break;
-
-    case NOCLOBBER_REDIRECT:
-      internal_error ("%s: cannot overwrite existing file", filename);
-      break;
-
-#if defined (RESTRICTED_SHELL)
-    case RESTRICTED_REDIRECT:
-      internal_error ("%s: restricted: cannot redirect output", filename);
-      break;
-#endif /* RESTRICTED_SHELL */
-
-    case HEREDOC_REDIRECT:
-      internal_error ("cannot create temp file for here document: %s", strerror (heredoc_errno));
-      break;
-
-    default:
-      internal_error ("%s: %s", filename, strerror (error));
-      break;
-    }
-
-  FREE (filename);
-}
-
-/* Perform the redirections on LIST.  If FOR_REAL, then actually make
-   input and output file descriptors, otherwise just do whatever is
-   neccessary for side effecting.  INTERNAL says to remember how to
-   undo the redirections later, if non-zero.  If SET_CLEXEC is non-zero,
-   file descriptors opened in do_redirection () have their close-on-exec
-   flag set. */
-static int
-do_redirections (list, for_real, internal, set_clexec)
-     REDIRECT *list;
-     int for_real, internal, set_clexec;
-{
-  int error;
-  REDIRECT *temp;
-
-  if (internal)
-    {
-      if (redirection_undo_list)
-       {
-         dispose_redirects (redirection_undo_list);
-         redirection_undo_list = (REDIRECT *)NULL;
-       }
-      if (exec_redirection_undo_list)
-       dispose_exec_redirects ();
-    }
-
-  for (temp = list; temp; temp = temp->next)
-    {
-      error = do_redirection_internal (temp, for_real, internal, set_clexec);
-      if (error)
        {
-         redirection_error (temp, error);
-         return (error);
+         if (dup2 (1, 2) < 0)
+           sys_error ("cannot duplicate fd 1 to fd 2");
        }
     }
-  return (0);
-}
-
-/* Return non-zero if the redirection pointed to by REDIRECT has a
-   redirectee.filename that can be expanded. */
-static int
-expandable_redirection_filename (redirect)
-     REDIRECT *redirect;
-{
-  switch (redirect->instruction)
-    {
-    case r_output_direction:
-    case r_appending_to:
-    case r_input_direction:
-    case r_inputa_direction:
-    case r_err_and_out:
-    case r_input_output:
-    case r_output_force:
-    case r_duplicating_input_word:
-    case r_duplicating_output_word:
-      return 1;
-
-    default:
-      return 0;
-    }
-}
-
-/* Expand the word in WORD returning a string.  If WORD expands to
-   multiple words (or no words), then return NULL. */
-char *
-redirection_expand (word)
-     WORD_DESC *word;
-{
-  char *result;
-  WORD_LIST *tlist1, *tlist2;
-
-  tlist1 = make_word_list (copy_word (word), (WORD_LIST *)NULL);
-  tlist2 = expand_words_no_vars (tlist1);
-  dispose_words (tlist1);
-
-  if (!tlist2 || tlist2->next)
-    {
-      /* We expanded to no words, or to more than a single word.
-        Dispose of the word list and return NULL. */
-      if (tlist2)
-       dispose_words (tlist2);
-      return ((char *)NULL);
-    }
-  result = string_list (tlist2);  /* XXX savestring (tlist2->word->word)? */
-  dispose_words (tlist2);
-  return (result);
-}
-
-/* Write the text of the here document pointed to by REDIRECTEE to the file
-   descriptor FD, which is already open to a temp file.  Return 0 if the
-   write is successful, otherwise return errno. */
-static int
-write_here_document (fd, redirectee)
-     int fd;
-     WORD_DESC *redirectee;
-{
-  char *document;
-  int document_len, fd2;
-  FILE *fp;
-  register WORD_LIST *t, *tlist;
-
-  /* Expand the text if the word that was specified had
-     no quoting.  The text that we expand is treated
-     exactly as if it were surrounded by double quotes. */
-
-  if (redirectee->flags & W_QUOTED)
-    {
-      document = redirectee->word;
-      document_len = strlen (document);
-      /* Set errno to something reasonable if the write fails. */
-      if (write (fd, document, document_len) < document_len)
-       {
-         if (errno == 0)
-           errno = ENOSPC;
-         return (errno);
-       }
-      else
-        return 0;
-    }
-
-  tlist = expand_string (redirectee->word, Q_HERE_DOCUMENT);
-  if (tlist)
-    {
-      /* Try using buffered I/O (stdio) and writing a word
-        at a time, letting stdio do the work of buffering
-        for us rather than managing our own strings.  Most
-        stdios are not particularly fast, however -- this
-        may need to be reconsidered later. */
-      if ((fd2 = dup (fd)) < 0 || (fp = fdopen (fd2, "w")) == NULL)
-       {
-         if (fd2 >= 0)
-           close (fd2);
-         return (errno);
-       }
-      errno = 0;
-      for (t = tlist; t; t = t->next)
-       {
-         /* This is essentially the body of
-            string_list_internal expanded inline. */
-         document = t->word->word;
-         document_len = strlen (document);
-         if (t != tlist)
-           putc (' ', fp);     /* separator */
-         fwrite (document, document_len, 1, fp);
-         if (ferror (fp))
-           {
-             if (errno == 0)
-               errno = ENOSPC;
-             fd2 = errno;
-             fclose(fp);
-             dispose_words (tlist);
-             return (fd2);
-           }
-       }
-      fclose (fp);
-      dispose_words (tlist);
-    }
-  return 0;
-}
-
-/* Create a temporary file holding the text of the here document pointed to
-   by REDIRECTEE, and return a file descriptor open for reading to the temp
-   file.  Return -1 on any error, and make sure errno is set appropriately. */
-static int
-here_document_to_fd (redirectee)
-     WORD_DESC *redirectee;
-{
-  char filename[24];
-  int r, fd;
-
-  /* Make the filename for the temp file. */
-  sprintf (filename, "/tmp/t%d-sh", (int)time ((time_t *) 0) + (int)getpid ());
-
-  /* Make sure we open it exclusively. */
-  fd = open (filename, O_TRUNC | O_WRONLY | O_CREAT | O_EXCL, 0600);
-  if (fd < 0)
-    return (fd);
-
-  errno = r = 0;               /* XXX */
-  /* write_here_document returns 0 on success, errno on failure. */
-  if (redirectee->word)
-    r = write_here_document (fd, redirectee);
-
-  close (fd);
-  if (r)
-    {
-      unlink (filename);
-      errno = r;
-      return (-1);
-    }
-
-  /* XXX - this is raceable */
-  /* Make the document really temporary.  Also make it the input. */
-  fd = open (filename, O_RDONLY, 0600);
-
-  if (fd < 0)
-    {
-      r = errno;
-      unlink (filename);
-      errno = r;
-      return -1;
-    }
-
-  if (unlink (filename) < 0)
-    {
-      r = errno;
-      close (fd);
-      errno = r;
-      return (-1);
-    }
-
-  return (fd);
-}
-
-/* Open FILENAME with FLAGS in noclobber mode, hopefully avoiding most
-   race conditions and avoiding the problem where the file is replaced
-   between the stat(2) and open(2). */
-static int
-noclobber_open (filename, flags, ri)
-     char *filename;
-     int flags;
-     enum r_instruction ri;
-{
-  int r, fd;
-  struct stat finfo, finfo2;
-
-  /* If the file exists and is a regular file, return an error
-     immediately. */
-  r = stat (filename, &finfo);
-  if (r == 0 && (S_ISREG (finfo.st_mode)))
-    return (NOCLOBBER_REDIRECT);
-
-  /* If the file was not present (r != 0), make sure we open it
-     exclusively so that if it is created before we open it, our open
-     will fail.  Make sure that we do not truncate an existing file.
-     Note that we don't turn on O_EXCL unless the stat failed -- if
-     the file was not a regular file, we leave O_EXCL off. */
-  flags &= ~O_TRUNC;
-  if (r != 0)
-    {
-      fd = open (filename, flags|O_EXCL, 0666);
-      return ((fd < 0 && errno == EEXIST) ? NOCLOBBER_REDIRECT : fd);
-    }
-  fd = open (filename, flags, 0666);
-
-  /* If the open failed, return the file descriptor right away. */
-  if (fd < 0)
-    return (errno == EEXIST ? NOCLOBBER_REDIRECT : fd);
-
-  /* OK, the open succeeded, but the file may have been changed from a
-     non-regular file to a regular file between the stat and the open.
-     We are assuming that the O_EXCL open handles the case where FILENAME
-     did not exist and is symlinked to an existing file between the stat
-     and open. */
-
-  /* If we can open it and fstat the file descriptor, and neither check
-     revealed that it was a regular file, and the file has not been replaced,
-     return the file descriptor. */
-  if ((fstat (fd, &finfo2) == 0) && (S_ISREG (finfo2.st_mode) == 0) &&
-      r == 0 && (S_ISREG (finfo.st_mode) == 0) &&
-      same_file (filename, filename, &finfo, &finfo2))
-    return fd;
-
-  /* The file has been replaced.  badness. */
-  close (fd);  
-  errno = EEXIST;
-  return (NOCLOBBER_REDIRECT);
-}
-
-/* Do the specific redirection requested.  Returns errno or one of the
-   special redirection errors (*_REDIRECT) in case of error, 0 on success.
-   If FOR_REAL is zero, then just do whatever is neccessary to produce the
-   appropriate side effects.   REMEMBERING, if non-zero, says to remember
-   how to undo each redirection.  If SET_CLEXEC is non-zero, then
-   we set all file descriptors > 2 that we open to be close-on-exec.  */
-static int
-do_redirection_internal (redirect, for_real, remembering, set_clexec)
-     REDIRECT *redirect;
-     int for_real, remembering, set_clexec;
-{
-  WORD_DESC *redirectee;
-  int redir_fd, fd, redirector, r;
-  char *redirectee_word;
-  enum r_instruction ri;
-  REDIRECT *new_redirect;
-
-  redirectee = redirect->redirectee.filename;
-  redir_fd = redirect->redirectee.dest;
-  redirector = redirect->redirector;
-  ri = redirect->instruction;
-
-  if (ri == r_duplicating_input_word || ri == r_duplicating_output_word)
-    {
-      /* We have [N]>&WORD or [N]<&WORD.  Expand WORD, then translate
-        the redirection into a new one and continue. */
-      redirectee_word = redirection_expand (redirectee);
-
-      if (redirectee_word == 0)
-       return (AMBIGUOUS_REDIRECT);
-      else if (redirectee_word[0] == '-' && redirectee_word[1] == '\0')
-       {
-         rd.dest = 0L;
-         new_redirect = make_redirection (redirector, r_close_this, rd);
-       }
-      else if (all_digits (redirectee_word))
-       {
-         if (ri == r_duplicating_input_word)
-           {
-             rd.dest = atol (redirectee_word);
-             new_redirect = make_redirection (redirector, r_duplicating_input, rd);
-           }
-         else
-           {
-             rd.dest = atol (redirectee_word);
-             new_redirect = make_redirection (redirector, r_duplicating_output, rd);
-           }
-       }
-      else if (ri == r_duplicating_output_word && redirector == 1)
-       {
-         if (posixly_correct == 0)
-           {
-             rd.filename = make_bare_word (redirectee_word);
-             new_redirect = make_redirection (1, r_err_and_out, rd);
-           }
-         else
-           new_redirect = copy_redirect (redirect);
-       }
-      else
-       {
-         free (redirectee_word);
-         return (AMBIGUOUS_REDIRECT);
-       }
-
-      free (redirectee_word);
-
-      /* Set up the variables needed by the rest of the function from the
-        new redirection. */
-      if (new_redirect->instruction == r_err_and_out)
-       {
-         char *alloca_hack;
-
-         /* Copy the word without allocating any memory that must be
-            explicitly freed. */
-         redirectee = (WORD_DESC *)alloca (sizeof (WORD_DESC));
-         xbcopy ((char *)new_redirect->redirectee.filename,
-                (char *)redirectee, sizeof (WORD_DESC));
-
-         alloca_hack = (char *)
-           alloca (1 + strlen (new_redirect->redirectee.filename->word));
-         redirectee->word = alloca_hack;
-         strcpy (redirectee->word, new_redirect->redirectee.filename->word);
-       }
-      else
-       /* It's guaranteed to be an integer, and shouldn't be freed. */
-       redirectee = new_redirect->redirectee.filename;
-
-      redir_fd = new_redirect->redirectee.dest;
-      redirector = new_redirect->redirector;
-      ri = new_redirect->instruction;
-
-      /* Overwrite the flags element of the old redirect with the new value. */
-      redirect->flags = new_redirect->flags;
-      dispose_redirects (new_redirect);
-    }
-
-  switch (ri)
-    {
-    case r_output_direction:
-    case r_appending_to:
-    case r_input_direction:
-    case r_inputa_direction:
-    case r_err_and_out:                /* command &>filename */
-    case r_input_output:
-    case r_output_force:
-      if (posixly_correct && !interactive_shell)
-       disallow_filename_globbing++;
-      redirectee_word = redirection_expand (redirectee);
-      if (posixly_correct && !interactive_shell)
-       disallow_filename_globbing--;
-
-      if (redirectee_word == 0)
-       return (AMBIGUOUS_REDIRECT);
-
-#if defined (RESTRICTED_SHELL)
-      if (restricted && (WRITE_REDIRECT (ri)))
-       {
-         free (redirectee_word);
-         return (RESTRICTED_REDIRECT);
-       }
-#endif /* RESTRICTED_SHELL */
-
-      /* If we are in noclobber mode, you are not allowed to overwrite
-        existing files.  Check before opening. */
-      if (noclobber && OUTPUT_REDIRECT (ri))
-       {
-         fd = noclobber_open (redirectee_word, redirect->flags, ri);
-         if (fd == NOCLOBBER_REDIRECT)
-           {
-             free (redirectee_word);
-             return (NOCLOBBER_REDIRECT);
-           }
-       }
-      else
-       {
-         fd = open (redirectee_word, redirect->flags, 0666);
-#if defined (AFS)
-         if ((fd < 0) && (errno == EACCES))
-           fd = open (redirectee_word, redirect->flags & ~O_CREAT, 0666);
-#endif /* AFS */
-       }
-      free (redirectee_word);
-
-      if (fd < 0)
-       return (errno);
-
-      if (for_real)
-       {
-         if (remembering)
-           /* Only setup to undo it if the thing to undo is active. */
-           if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
-             add_undo_redirect (redirector);
-           else
-             add_undo_close_redirect (redirector);
-
-#if defined (BUFFERED_INPUT)
-         check_bash_input (redirector);
-#endif
-
-         if ((fd != redirector) && (dup2 (fd, redirector) < 0))
-           return (errno);
-
-#if defined (BUFFERED_INPUT)
-         /* Do not change the buffered stream for an implicit redirection
-            of /dev/null to fd 0 for asynchronous commands without job
-            control (r_inputa_direction). */
-         if (ri == r_input_direction || ri == r_input_output)
-           duplicate_buffered_stream (fd, redirector);
-#endif /* BUFFERED_INPUT */
-
-         /*
-          * If we're remembering, then this is the result of a while, for
-          * or until loop with a loop redirection, or a function/builtin
-          * executing in the parent shell with a redirection.  In the
-          * function/builtin case, we want to set all file descriptors > 2
-          * to be close-on-exec to duplicate the effect of the old
-          * for i = 3 to NOFILE close(i) loop.  In the case of the loops,
-          * both sh and ksh leave the file descriptors open across execs.
-          * The Posix standard mentions only the exec builtin.
-          */
-         if (set_clexec && (redirector > 2))
-           SET_CLOSE_ON_EXEC (redirector);
-       }
-
-      if (fd != redirector)
-       {
-#if defined (BUFFERED_INPUT)
-         if (INPUT_REDIRECT (ri))
-           close_buffered_fd (fd);
-         else
-#endif /* !BUFFERED_INPUT */
-           close (fd);         /* Don't close what we just opened! */
-       }
-
-      /* If we are hacking both stdout and stderr, do the stderr
-        redirection here. */
-      if (ri == r_err_and_out)
-       {
-         if (for_real)
-           {
-             if (remembering)
-               add_undo_redirect (2);
-             if (dup2 (1, 2) < 0)
-               return (errno);
-           }
-       }
-      break;
-
-    case r_reading_until:
-    case r_deblank_reading_until:
-      /* REDIRECTEE is a pointer to a WORD_DESC containing the text of
-        the new input.  Place it in a temporary file. */
-      if (redirectee)
-       {
-         fd = here_document_to_fd (redirectee);
-
-         if (fd < 0)
-           {
-             heredoc_errno = errno;
-             return (HEREDOC_REDIRECT);
-           }
-
-         if (for_real)
-           {
-             if (remembering)
-               /* Only setup to undo it if the thing to undo is active. */
-               if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
-                 add_undo_redirect (redirector);
-               else
-                 add_undo_close_redirect (redirector);
-
-#if defined (BUFFERED_INPUT)
-             check_bash_input (redirector);
-#endif
-             if (fd != redirector && dup2 (fd, redirector) < 0)
-               {
-                 r = errno;
-                 close (fd);
-                 return (r);
-               }
-
-#if defined (BUFFERED_INPUT)
-             duplicate_buffered_stream (fd, redirector);
-#endif
-
-             if (set_clexec && (redirector > 2))
-               SET_CLOSE_ON_EXEC (redirector);
-           }
-
-#if defined (BUFFERED_INPUT)
-         close_buffered_fd (fd);
-#else
-         close (fd);
-#endif
-       }
-      break;
-
-    case r_duplicating_input:
-    case r_duplicating_output:
-      if (for_real && (redir_fd != redirector))
-       {
-         if (remembering)
-           /* Only setup to undo it if the thing to undo is active. */
-           if (fcntl (redirector, F_GETFD, 0) != -1)
-             add_undo_redirect (redirector);
-           else
-             add_undo_close_redirect (redirector);
-
-#if defined (BUFFERED_INPUT)
-         check_bash_input (redirector);
-#endif
-         /* This is correct.  2>&1 means dup2 (1, 2); */
-         if (dup2 (redir_fd, redirector) < 0)
-           return (errno);
-
-#if defined (BUFFERED_INPUT)
-         if (ri == r_duplicating_input)
-           duplicate_buffered_stream (redir_fd, redirector);
-#endif /* BUFFERED_INPUT */
-
-         /* First duplicate the close-on-exec state of redirectee.  dup2
-            leaves the flag unset on the new descriptor, which means it
-            stays open.  Only set the close-on-exec bit for file descriptors
-            greater than 2 in any case, since 0-2 should always be open
-            unless closed by something like `exec 2<&-'. */
-         /* if ((already_set || set_unconditionally) && (ok_to_set))
-               set_it () */
-         if (((fcntl (redir_fd, F_GETFD, 0) == 1) || set_clexec) &&
-              (redirector > 2))
-           SET_CLOSE_ON_EXEC (redirector);
-       }
-      break;
-
-    case r_close_this:
-      if (for_real)
-       {
-         if (remembering && (fcntl (redirector, F_GETFD, 0) != -1))
-           add_undo_redirect (redirector);
-
-#if defined (BUFFERED_INPUT)
-         check_bash_input (redirector);
-         close_buffered_fd (redirector);
-#else /* !BUFFERED_INPUT */
-         close (redirector);
-#endif /* !BUFFERED_INPUT */
-       }
-      break;
-
-    case r_duplicating_input_word:
-    case r_duplicating_output_word:
-      break;
-    }
-  return (0);
-}
-
-#define SHELL_FD_BASE  10
-
-/* Remember the file descriptor associated with the slot FD,
-   on REDIRECTION_UNDO_LIST.  Note that the list will be reversed
-   before it is executed.  Any redirections that need to be undone
-   even if REDIRECTION_UNDO_LIST is discarded by the exec builtin
-   are also saved on EXEC_REDIRECTION_UNDO_LIST. */
-static int
-add_undo_redirect (fd)
-     int fd;
-{
-  int new_fd, clexec_flag;
-  REDIRECT *new_redirect, *closer, *dummy_redirect;
-
-  new_fd = fcntl (fd, F_DUPFD, SHELL_FD_BASE);
-
-  if (new_fd < 0)
-    {
-      sys_error ("redirection error");
-      return (-1);
-    }
-
-  clexec_flag = fcntl (fd, F_GETFD, 0);
-
-  rd.dest = 0L;
-  closer = make_redirection (new_fd, r_close_this, rd);
-  dummy_redirect = copy_redirects (closer);
-
-  rd.dest = (long)new_fd;
-  new_redirect = make_redirection (fd, r_duplicating_output, rd);
-  new_redirect->next = closer;
-
-  closer->next = redirection_undo_list;
-  redirection_undo_list = new_redirect;
-
-  /* Save redirections that need to be undone even if the undo list
-     is thrown away by the `exec' builtin. */
-  add_exec_redirect (dummy_redirect);
-
-  /* File descriptors used only for saving others should always be
-     marked close-on-exec.  Unfortunately, we have to preserve the
-     close-on-exec state of the file descriptor we are saving, since
-     fcntl (F_DUPFD) sets the new file descriptor to remain open
-     across execs.  If, however, the file descriptor whose state we
-     are saving is <= 2, we can just set the close-on-exec flag,
-     because file descriptors 0-2 should always be open-on-exec,
-     and the restore above in do_redirection() will take care of it. */
-  if (clexec_flag || fd < 3)
-    SET_CLOSE_ON_EXEC (new_fd);
-
-  return (0);
-}
-
-/* Set up to close FD when we are finished with the current command
-   and its redirections. */
-static void
-add_undo_close_redirect (fd)
-     int fd;
-{
-  REDIRECT *closer;
-
-  rd.dest = 0L;
-  closer = make_redirection (fd, r_close_this, rd);
-  closer->next = redirection_undo_list;
-  redirection_undo_list = closer;
-}
-
-static void
-add_exec_redirect (dummy_redirect)
-     REDIRECT *dummy_redirect;
-{
-  dummy_redirect->next = exec_redirection_undo_list;
-  exec_redirection_undo_list = dummy_redirect;
-}
-
-#define u_mode_bits(x) (((x) & 0000700) >> 6)
-#define g_mode_bits(x) (((x) & 0000070) >> 3)
-#define o_mode_bits(x) (((x) & 0000007) >> 0)
-#define X_BIT(x) ((x) & 1)
-
-/* Return some flags based on information about this file.
-   The EXISTS bit is non-zero if the file is found.
-   The EXECABLE bit is non-zero the file is executble.
-   Zero is returned if the file is not found. */
-int
-file_status (name)
-     char *name;
-{
-  struct stat finfo;
-
-  /* Determine whether this file exists or not. */
-  if (stat (name, &finfo) < 0)
-    return (0);
-
-  /* If the file is a directory, then it is not "executable" in the
-     sense of the shell. */
-  if (S_ISDIR (finfo.st_mode))
-    return (FS_EXISTS|FS_DIRECTORY);
-
-#if defined (AFS)
-  /* We have to use access(2) to determine access because AFS does not
-     support Unix file system semantics.  This may produce wrong
-     answers for non-AFS files when ruid != euid.  I hate AFS. */
-  if (access (name, X_OK) == 0)
-    return (FS_EXISTS | FS_EXECABLE);
-  else
-    return (FS_EXISTS);
-#else /* !AFS */
-
-  /* Find out if the file is actually executable.  By definition, the
-     only other criteria is that the file has an execute bit set that
-     we can use. */
-
-  /* Root only requires execute permission for any of owner, group or
-     others to be able to exec a file. */
-  if (current_user.euid == (uid_t)0)
-    {
-      int bits;
-
-      bits = (u_mode_bits (finfo.st_mode) |
-             g_mode_bits (finfo.st_mode) |
-             o_mode_bits (finfo.st_mode));
-
-      if (X_BIT (bits))
-       return (FS_EXISTS | FS_EXECABLE);
-    }
-
-  /* If we are the owner of the file, the owner execute bit applies. */
-  if (current_user.euid == finfo.st_uid && X_BIT (u_mode_bits (finfo.st_mode)))
-    return (FS_EXISTS | FS_EXECABLE);
-
-  /* If we are in the owning group, the group permissions apply. */
-  if (group_member (finfo.st_gid) && X_BIT (g_mode_bits (finfo.st_mode)))
-    return (FS_EXISTS | FS_EXECABLE);
-
-  /* If `others' have execute permission to the file, then so do we,
-     since we are also `others'. */
-  if (X_BIT (o_mode_bits (finfo.st_mode)))
-    return (FS_EXISTS | FS_EXECABLE);
-
-  return (FS_EXISTS);
-#endif /* !AFS */
-}
-
-/* Return non-zero if FILE exists and is executable.
-   Note that this function is the definition of what an
-   executable file is; do not change this unless YOU know
-   what an executable file is. */
-int
-executable_file (file)
-     char *file;
-{
-  int s;
-
-  s = file_status (file);
-  return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0));
-}
-
-int
-is_directory (file)
-     char *file;
-{
-  return (file_status (file) & FS_DIRECTORY);
-}
-
-/* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
-   encounters a `.' as the directory pathname while scanning the
-   list of possible pathnames; i.e., if `.' comes before the directory
-   containing the file of interest. */
-int dot_found_in_search = 0;
-
-/* Locate the executable file referenced by NAME, searching along
-   the contents of the shell PATH variable.  Return a new string
-   which is the full pathname to the file, or NULL if the file
-   couldn't be found.  If a file is found that isn't executable,
-   and that is the only match, then return that. */
-char *
-find_user_command (name)
-     char *name;
-{
-  return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
-}
-
-/* Locate the file referenced by NAME, searching along the contents
-   of the shell PATH variable.  Return a new string which is the full
-   pathname to the file, or NULL if the file couldn't be found.  This
-   returns the first file found. */
-char *
-find_path_file (name)
-     char *name;
-{
-  return (find_user_command_internal (name, FS_EXISTS));
-}
-
-static char *
-_find_user_command_internal (name, flags)
-     char *name;
-     int flags;
-{
-  char *path_list;
-  SHELL_VAR *var;
-
-  /* Search for the value of PATH in both the temporary environment, and
-     in the regular list of variables. */
-  if (var = find_variable_internal ("PATH", 1))        /* XXX could be array? */
-    path_list = value_cell (var);
-  else
-    path_list = (char *)NULL;
-
-  if (path_list == 0 || *path_list == '\0')
-    return (savestring (name));
-
-  return (find_user_command_in_path (name, path_list, flags));
-}
-
-static char *
-find_user_command_internal (name, flags)
-     char *name;
-     int flags;
-{
-#ifdef __WIN32__
-  char *res, *dotexe;
-
-  dotexe = xmalloc (strlen (name) + 5);
-  strcpy (dotexe, name);
-  strcat (dotexe, ".exe");
-  res = _find_user_command_internal (dotexe, flags);
-  free (dotexe);
-  if (res == 0)
-    res = _find_user_command_internal (name, flags);
-  return res;
-#else
-  return (_find_user_command_internal (name, flags));
-#endif
-}
-
-/* Return the next element from PATH_LIST, a colon separated list of
-   paths.  PATH_INDEX_POINTER is the address of an index into PATH_LIST;
-   the index is modified by this function.
-   Return the next element of PATH_LIST or NULL if there are no more. */
-static char *
-get_next_path_element (path_list, path_index_pointer)
-     char *path_list;
-     int *path_index_pointer;
-{
-  char *path;
-
-  path = extract_colon_unit (path_list, path_index_pointer);
-
-  if (!path)
-    return (path);
-
-  if (!*path)
-    {
-      free (path);
-      path = savestring (".");
-    }
-
-  return (path);
-}
-
-/* Look for PATHNAME in $PATH.  Returns either the hashed command
-   corresponding to PATHNAME or the first instance of PATHNAME found
-   in $PATH.  Returns a newly-allocated string. */
-char *
-search_for_command (pathname)
-     char *pathname;
-{
-  char *hashed_file, *command;
-  int temp_path, st;
-  SHELL_VAR *path;
-
-  hashed_file = command = (char *)NULL;
-
-  /* If PATH is in the temporary environment for this command, don't use the
-     hash table to search for the full pathname. */
-  path = find_tempenv_variable ("PATH");
-  temp_path = path != 0;
-
-  /* Don't waste time trying to find hashed data for a pathname
-     that is already completely specified or if we're using a command-
-     specific value for PATH. */
-  if (path == 0 && absolute_program (pathname) == 0)
-    hashed_file = find_hashed_filename (pathname);
-
-  /* If a command found in the hash table no longer exists, we need to
-     look for it in $PATH.  Thank you Posix.2.  This forces us to stat
-     every command found in the hash table. */
-
-  if (hashed_file && (posixly_correct || check_hashed_filenames))
-    {
-      st = file_status (hashed_file);
-      if ((st ^ (FS_EXISTS | FS_EXECABLE)) != 0)
-       {
-         remove_hashed_filename (pathname);
-         free (hashed_file);
-         hashed_file = (char *)NULL;
-       }
-    }
-
-  if (hashed_file)
-    command = hashed_file;
-  else if (absolute_program (pathname))
-    /* A command containing a slash is not looked up in PATH or saved in
-       the hash table. */
-    command = savestring (pathname);
-  else
-    {
-      /* If $PATH is in the temporary environment, we've already retrieved
-        it, so don't bother trying again. */
-      if (temp_path)
-       command = find_user_command_in_path (pathname, value_cell (path),
-                                            FS_EXEC_PREFERRED|FS_NODIRS);
-      else
-       command = find_user_command (pathname);
-      if (command && hashing_enabled && temp_path == 0)
-       remember_filename (pathname, command, dot_found_in_search, 1);
-    }
-  return (command);
-}
-
-char *
-user_command_matches (name, flags, state)
-     char *name;
-     int flags, state;
-{
-  register int i;
-  int  path_index, name_len;
-  char *path_list, *path_element, *match;
-  struct stat dotinfo;
-  static char **match_list = NULL;
-  static int match_list_size = 0;
-  static int match_index = 0;
-
-  if (state == 0)
-    {
-      /* Create the list of matches. */
-      if (match_list == 0)
-       {
-         match_list_size = 5;
-         match_list = (char **)xmalloc (match_list_size * sizeof(char *));
-       }
-
-      /* Clear out the old match list. */
-      for (i = 0; i < match_list_size; i++)
-       match_list[i] = 0;
-
-      /* We haven't found any files yet. */
-      match_index = 0;
-
-      if (absolute_program (name))
-       {
-         match_list[0] = find_absolute_program (name, flags);
-         match_list[1] = (char *)NULL;
-         path_list = (char *)NULL;
-       }
-      else
-       {
-         name_len = strlen (name);
-         file_to_lose_on = (char *)NULL;
-         dot_found_in_search = 0;
-         stat (".", &dotinfo);
-         path_list = get_string_value ("PATH");
-         path_index = 0;
-       }
-
-      while (path_list && path_list[path_index])
-       {
-         path_element = get_next_path_element (path_list, &path_index);
-
-         if (path_element == 0)
-           break;
-
-         match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
-
-         free (path_element);
-
-         if (match == 0)
-           continue;
-
-         if (match_index + 1 == match_list_size)
-           {
-             match_list_size += 10;
-             match_list = (char **)xrealloc (match_list, (match_list_size + 1) * sizeof (char *));
-           }
-
-         match_list[match_index++] = match;
-         match_list[match_index] = (char *)NULL;
-         FREE (file_to_lose_on);
-         file_to_lose_on = (char *)NULL;
-       }
-
-      /* We haven't returned any strings yet. */
-      match_index = 0;
-    }
-
-  match = match_list[match_index];
-
-  if (match)
-    match_index++;
-
-  return (match);
-}
-
-/* Turn PATH, a directory, and NAME, a filename, into a full pathname.
-   This allocates new memory and returns it. */
-static char *
-make_full_pathname (path, name, name_len)
-     char *path, *name;
-     int name_len;
-{
-  char *full_path;
-  int path_len;
-
-  path_len = strlen (path);
-  full_path = xmalloc (2 + path_len + name_len);
-  strcpy (full_path, path);
-  full_path[path_len] = '/';
-  strcpy (full_path + path_len + 1, name);
-  return (full_path);
-}
-
-static char *
-find_absolute_program (name, flags)
-     char *name;
-     int flags;
-{
-  int st;
-
-  st = file_status (name);
-
-  /* If the file doesn't exist, quit now. */
-  if ((st & FS_EXISTS) == 0)
-    return ((char *)NULL);
-
-  /* If we only care about whether the file exists or not, return
-     this filename.  Otherwise, maybe we care about whether this
-     file is executable.  If it is, and that is what we want, return it. */
-  if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE)))
-    return (savestring (name));
-
-  return ((char *)NULL);
-}
-
-static char *
-find_in_path_element (name, path, flags, name_len, dotinfop)
-     char *name, *path;
-     int flags, name_len;
-     struct stat *dotinfop;
-{
-  int status;
-  char *full_path, *xpath;
-
-  xpath = (*path == '~') ? bash_tilde_expand (path) : path;
-
-  /* Remember the location of "." in the path, in all its forms
-     (as long as they begin with a `.', e.g. `./.') */
-  if (dot_found_in_search == 0 && *xpath == '.')
-    dot_found_in_search = same_file (".", xpath, dotinfop, (struct stat *)NULL);
-
-  full_path = make_full_pathname (xpath, name, name_len);
-
-  status = file_status (full_path);
-
-  if (xpath != path)
-    free (xpath);
-
-  if ((status & FS_EXISTS) == 0)
-    {
-      free (full_path);
-      return ((char *)NULL);
-    }
-
-  /* The file exists.  If the caller simply wants the first file, here it is. */
-  if (flags & FS_EXISTS)
-    return (full_path);
-
-  /* If the file is executable, then it satisfies the cases of
-      EXEC_ONLY and EXEC_PREFERRED.  Return this file unconditionally. */
-  if ((status & FS_EXECABLE) &&
-      (((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0)))
-    {
-      FREE (file_to_lose_on);
-      file_to_lose_on = (char *)NULL;
-      return (full_path);
-    }
-
-  /* The file is not executable, but it does exist.  If we prefer
-     an executable, then remember this one if it is the first one
-     we have found. */
-  if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0)
-    file_to_lose_on = savestring (full_path);
-
-  /* If we want only executable files, or we don't want directories and
-     this file is a directory, fail. */
-  if ((flags & FS_EXEC_ONLY) || (flags & FS_EXEC_PREFERRED) ||
-      ((flags & FS_NODIRS) && (status & FS_DIRECTORY)))
-    {
-      free (full_path);
-      return ((char *)NULL);
-    }
-  else
-    return (full_path);
-}
-
-/* This does the dirty work for find_user_command_internal () and
-   user_command_matches ().
-   NAME is the name of the file to search for.
-   PATH_LIST is a colon separated list of directories to search.
-   FLAGS contains bit fields which control the files which are eligible.
-   Some values are:
-      FS_EXEC_ONLY:            The file must be an executable to be found.
-      FS_EXEC_PREFERRED:       If we can't find an executable, then the
-                               the first file matching NAME will do.
-      FS_EXISTS:               The first file found will do.
-      FS_NODIRS:               Don't find any directories.
-*/
-static char *
-find_user_command_in_path (name, path_list, flags)
-     char *name;
-     char *path_list;
-     int flags;
-{
-  char *full_path, *path;
-  int path_index, name_len;
-  struct stat dotinfo;
-
-  /* We haven't started looking, so we certainly haven't seen
-     a `.' as the directory path yet. */
-  dot_found_in_search = 0;
-
-  if (absolute_program (name))
-    {
-      full_path = find_absolute_program (name, flags);
-      return (full_path);
-    }
-
-  if (path_list == 0 || *path_list == '\0')
-    return (savestring (name));                /* XXX */
-
-  file_to_lose_on = (char *)NULL;
-  name_len = strlen (name);
-  stat (".", &dotinfo);
-  path_index = 0;
-
-  while (path_list[path_index])
-    {
-      /* Allow the user to interrupt out of a lengthy path search. */
-      QUIT;
-
-      path = get_next_path_element (path_list, &path_index);
-      if (path == 0)
-       break;
-
-      /* Side effects: sets dot_found_in_search, possibly sets
-        file_to_lose_on. */
-      full_path = find_in_path_element (name, path, flags, name_len, &dotinfo);
-      free (path);
-
-      /* This should really be in find_in_path_element, but there isn't the
-        right combination of flags. */
-      if (full_path && is_directory (full_path))
-       {
-         free (full_path);
-         continue;
-       }
-
-      if (full_path)
-       {
-         FREE (file_to_lose_on);
-         return (full_path);
-       }
-    }
-
-  /* We didn't find exactly what the user was looking for.  Return
-     the contents of FILE_TO_LOSE_ON which is NULL when the search
-     required an executable, or non-NULL if a file was found and the
-     search would accept a non-executable as a last resort. */
-  return (file_to_lose_on);
 }