Imported from ../bash-2.0.tar.gz.
[platform/upstream/bash.git] / execute_cmd.c
index 55274ea..ce154c3 100644 (file)
    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. */
-#if defined (AIX) && defined (RISC6000) && !defined (__GNUC__)
+#include "config.h"
+
+#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
   #pragma alloca
-#endif /* AIX && RISC6000 && !__GNUC__ */
+#endif /* _AIX && RISC6000 && !__GNUC__ */
 
 #include <stdio.h>
 #include <ctype.h>
 #include "filecntl.h"
 #include "posixstat.h"
 #include <signal.h>
+#include <sys/param.h>
 
-#if !defined (SIGABRT)
-#define SIGABRT SIGIOT
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
+
+#if defined (HAVE_LIMITS_H)
+#  include <limits.h>
+#endif
+
+#if defined (HAVE_SYS_TIME_H)
+#  include <sys/time.h>
+#endif
+
+#if defined (HAVE_SYS_RESOURCE_H)
+#  include <sys/resource.h>
+#endif
+
+#if defined (HAVE_SYS_TIMES_H) && defined (HAVE_TIMES)
+#  include <sys/times.h>
 #endif
 
-#include <sys/param.h>
 #include <errno.h>
 
 #if !defined (errno)
 extern int errno;
 #endif
 
-#if defined (HAVE_STRING_H)
-#  include <string.h>
-#else /* !HAVE_STRING_H */
-#  include <strings.h>
-#endif /* !HAVE_STRING_H */
+#include "bashansi.h"
 
+#include "memalloc.h"
 #include "shell.h"
 #include "y.tab.h"
 #include "flags.h"
-#include "hash.h"
+#include "builtins.h"
+#include "hashlib.h"
 #include "jobs.h"
 #include "execute_cmd.h"
+#include "trap.h"
+#include "pathexp.h"
 
-#include "sysdefs.h"
 #include "builtins/common.h"
 #include "builtins/builtext.h" /* list of builtins */
 
@@ -64,55 +81,91 @@ extern int errno;
 #  include "input.h"
 #endif
 
+#if defined (ALIAS)
+#  include "alias.h"
+#endif
+
+#if defined (HISTORY)
+#  include "bashhist.h"
+#endif
+
 extern int posixly_correct;
-extern int breaking, continuing, loop_level;
-extern int interactive, interactive_shell, login_shell;
-extern int parse_and_execute_level;
+extern int executing, breaking, continuing, loop_level;
+extern int interactive, interactive_shell, login_shell, expand_aliases;
+extern int parse_and_execute_level, running_trap;
 extern int command_string_index, variable_context, line_number;
 extern int dot_found_in_search;
+extern int already_making_children;
 extern char **temporary_env, **function_env, **builtin_env;
 extern char *the_printed_command, *shell_name;
 extern pid_t last_command_subst_pid;
 extern Function *last_shell_builtin, *this_shell_builtin;
-extern jmp_buf top_level, subshell_top_level;
-extern int subshell_argc;
 extern char **subshell_argv, **subshell_envp;
-extern int already_making_children;
+extern int subshell_argc;
+extern char *glob_argv_flags;
 
 extern int getdtablesize ();
 extern int close ();
 
 /* Static functions defined and used in this file. */
-static void close_pipes (), do_piping (), execute_disk_command ();
-static void execute_subshell_builtin_or_function ();
-static void cleanup_redirects (), cleanup_func_redirects (), bind_lastarg ();
+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 (), execute_shell_script ();
-static int execute_builtin_or_function (), add_undo_redirect ();
+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
+static int time_command ();
+static int execute_case_command ();
+static int execute_while_command (), execute_until_command ();
+static int execute_while_or_until ();
+static int execute_if_command ();
+static int execute_simple_command ();
+static int execute_builtin (), execute_function ();
+static int execute_builtin_or_function ();
+static int builtin_status ();
+static void execute_subshell_builtin_or_function ();
+static void execute_disk_command ();
+static int execute_connection ();
+static int execute_intern_function ();
 
 /* The line number that the currently executing function starts on. */
-static int function_line_number = 0;
+static int function_line_number;
 
 /* Set to 1 if fd 0 was the subject of redirection to a subshell. */
-static int stdin_redir = 0;
+static int stdin_redir;
 
 /* The name of the command that is currently being executed.
    `test' needs this, for example. */
 char *this_command_name;
 
+static COMMAND *currently_executing_command;
+
 struct stat SB;                /* used for debugging */
 
+static int special_builtin_failed;
 static REDIRECTEE rd;
 
+/* 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 = 0;
+int return_catch_flag;
 int return_catch_value;
-jmp_buf return_catch;
+procenv_t return_catch;
 
 /* The value returned by the last synchronous command. */
-int last_command_exit_value = 0;
+int last_command_exit_value;
 
 /* The list of redirections to perform which will undo the redirections
    that I made in the shell. */
@@ -125,7 +178,11 @@ REDIRECT *exec_redirection_undo_list = (REDIRECT *)NULL;
 
 /* Non-zero if we have just forked and are currently running in a subshell
    environment. */
-int subshell_environment = 0;
+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;
 
 struct fd_bitmap *current_fds_to_close = (struct fd_bitmap *)NULL;
 
@@ -178,6 +235,16 @@ close_fd_bitmap (fdbp)
     }
 }
 
+/* Return the line number of the currently executing command. */
+int
+executing_line_number ()
+{
+  if (executing && variable_context == 0 && currently_executing_command &&
+       currently_executing_command->type == cm_simple)
+    return currently_executing_command->value.Simple->line;
+  return line_number;
+}
+
 /* Execute the command passed in COMMAND.  COMMAND is exactly what
    read_command () places into GLOBAL_COMMAND.  See "command.h" for the
    details of the command structure.
@@ -185,6 +252,7 @@ close_fd_bitmap (fdbp)
    EXECUTION_SUCCESS or EXECUTION_FAILURE are the only possible
    return values.  Executing a command with nothing in it returns
    EXECUTION_SUCCESS. */
+int
 execute_command (command)
      COMMAND *command;
 {
@@ -242,6 +310,7 @@ cleanup_redirects (list)
   dispose_redirects (list);
 }
 
+#if 0
 /* Function to unwind_protect the redirections for functions and builtins. */
 static void
 cleanup_func_redirects (list)
@@ -249,6 +318,7 @@ cleanup_func_redirects (list)
 {
   do_redirections (list, 1, 0, 0);
 }
+#endif
 
 static void
 dispose_exec_redirects ()
@@ -280,7 +350,7 @@ open_files ()
 
   fd_table_size = getdtablesize ();
 
-  fprintf (stderr, "pid %d open files:", getpid ());
+  fprintf (stderr, "pid %d open files:", (int)getpid ());
   for (i = 3; i < fd_table_size; i++)
     {
       if ((f = fcntl (i, F_GETFD, 0)) != -1)
@@ -289,7 +359,7 @@ open_files ()
   fprintf (stderr, "\n");
 }
 
-#define DESCRIBE_PID(pid) if (interactive) describe_pid (pid)
+#define DESCRIBE_PID(pid) do { if (interactive) describe_pid (pid); } while (0)
 
 /* Execute the command passed in COMMAND, perhaps doing it asynchrounously.
    COMMAND is exactly what read_command () places into GLOBAL_COMMAND.
@@ -303,30 +373,44 @@ open_files ()
    EXECUTION_SUCCESS or EXECUTION_FAILURE are the only possible
    return values.  Executing a command with nothing in it returns
    EXECUTION_SUCCESS. */
-execute_command_internal (command, asynchronous, pipe_in, pipe_out, 
+int
+execute_command_internal (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 exec_result = EXECUTION_SUCCESS;
-  int invert, ignore_return;
-  REDIRECT *my_undo_list, *exec_undo_list;
+  int exec_result, invert, ignore_return, was_debug_trap;
+  REDIRECT *my_undo_list, *exec_undo_list, *rp;
+  pid_t last_pid;
 
-  if (!command || breaking || continuing)
+  if (command == 0 || breaking || continuing || read_but_dont_execute)
     return (EXECUTION_SUCCESS);
 
   run_pending_traps ();
 
+  if (running_trap == 0)
+    currently_executing_command = command;
+
+#if defined (COMMAND_TIMING)
+  if (command->flags & CMD_TIME_PIPELINE)
+    {
+      exec_result = time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close);
+      if (running_trap == 0)
+       currently_executing_command = (COMMAND *)NULL;
+      return (exec_result);
+    }
+#endif /* COMMAND_TIMING */
+
   invert = (command->flags & CMD_INVERT_RETURN) != 0;
+  exec_result = EXECUTION_SUCCESS;
 
   /* 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->flags & CMD_WANT_SUBSHELL) ||
-      (command->flags & CMD_FORCE_SUBSHELL)  ||
+  if ((command->flags & (CMD_WANT_SUBSHELL|CMD_FORCE_SUBSHELL)) ||
       (shell_control_structure (command->type) &&
        (pipe_out != NO_PIPE || pipe_in != NO_PIPE || asynchronous)))
     {
@@ -340,26 +424,15 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
        {
          int user_subshell, return_code, function_value;
 
-         /* 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 ();
-
          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), 
+            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, 
+            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
@@ -378,13 +451,26 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
              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 = 1;
+         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
@@ -399,10 +485,8 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
             sh compatibility, but I'm not sure it's the right thing to do. */
          if (user_subshell)
            {
-             REDIRECT *r;
-
-             for (r = command->redirects; r; r = r->next)
-               switch (r->instruction)
+             for (rp = command->redirects; rp; rp = rp->next)
+               switch (rp->instruction)
                  {
                  case r_input_direction:
                  case r_inputa_direction:
@@ -414,10 +498,11 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
                  case r_duplicating_input:
                  case r_duplicating_input_word:
                  case r_close_this:
-                   if (r->redirector == 0)
-                     stdin_redir++;
+                   stdin_redir += (rp->redirector == 0);
                    break;
                  }
+
+             restore_default_signal (0);
            }
 
          if (fds_to_close)
@@ -446,7 +531,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
             need to handle a possible `return'. */
          function_value = 0;
          if (return_catch_flag)
-           function_value =  setjmp (return_catch);
+           function_value = setjmp (return_catch);
 
          if (function_value)
            return_code = return_catch_value;
@@ -474,25 +559,22 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
          /* If we are part of a pipeline, and not the end of the pipeline,
             then we should simply return and let the last command in the
             pipe be waited for.  If we are not in a pipeline, or are the
-            last command in the pipeline, then we wait for the subshell 
+            last command in the pipeline, then we wait for the subshell
             and return its exit status as usual. */
          if (pipe_out != NO_PIPE)
            return (EXECUTION_SUCCESS);
 
          stop_pipeline (asynchronous, (COMMAND *)NULL);
 
-         if (!asynchronous)
+         if (asynchronous == 0)
            {
              last_command_exit_value = wait_for (paren_pid);
 
              /* If we have to, invert the return value. */
              if (invert)
-               {
-                 if (last_command_exit_value == EXECUTION_SUCCESS)
-                   return (EXECUTION_FAILURE);
-                 else
-                   return (EXECUTION_SUCCESS);
-               }
+               return ((last_command_exit_value == EXECUTION_SUCCESS)
+                         ? EXECUTION_FAILURE
+                         : EXECUTION_SUCCESS);
              else
                return (last_command_exit_value);
            }
@@ -550,6 +632,71 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 
   switch (command->type)
     {
+    case cm_simple:
+      {
+       /* We can't rely on this variable retaining its value across a
+          call to execute_simple_command if a longjmp occurs as the
+          result of a `return' builtin.  This is true for sure with gcc. */
+       last_pid = last_made_pid;
+       was_debug_trap = signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0;
+
+       if (ignore_return && command->value.Simple)
+         command->value.Simple->flags |= CMD_IGNORE_RETURN;
+       exec_result =
+         execute_simple_command (command->value.Simple, pipe_in, pipe_out,
+                                 asynchronous, fds_to_close);
+
+       /* The temporary environment should be used for only the simple
+          command immediately following its definition. */
+       dispose_used_env_vars ();
+
+#if (defined (ultrix) && defined (mips)) || defined (C_ALLOCA)
+       /* Reclaim memory allocated with alloca () on machines which
+          may be using the alloca emulation code. */
+       (void) alloca (0);
+#endif /* (ultrix && mips) || C_ALLOCA */
+
+       /* If we forked to do the command, then we must wait_for ()
+          the child. */
+
+       /* XXX - this is something to watch out for if there are problems
+          when the shell is compiled without job control. */
+       if (already_making_children && pipe_out == NO_PIPE &&
+           last_pid != last_made_pid)
+         {
+           stop_pipeline (asynchronous, (COMMAND *)NULL);
+
+           if (asynchronous)
+             {
+               DESCRIBE_PID (last_made_pid);
+             }
+           else
+#if !defined (JOB_CONTROL)
+             /* Do not wait for asynchronous processes started from
+                startup files. */
+           if (last_made_pid != last_asynchronous_pid)
+#endif
+           /* When executing a shell function that executes other
+              commands, this causes the last simple command in
+              the function to be waited for twice. */
+             exec_result = wait_for (last_made_pid);
+         }
+      }
+
+      if (was_debug_trap)
+       run_debug_trap ();
+
+      if (ignore_return == 0 && invert == 0 &&
+          ((posixly_correct && interactive == 0 && special_builtin_failed) ||
+          (exit_immediately_on_error && (exec_result != EXECUTION_SUCCESS))))
+       {
+         last_command_exit_value = exec_result;
+         run_pending_traps ();
+         jump_to_top_level (EXITPROG);
+       }
+
+      break;
+
     case cm_for:
       if (ignore_return)
        command->value.For->flags |= CMD_IGNORE_RETURN;
@@ -632,360 +779,620 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
        }
       break;
 
-    case cm_simple:
-      {
-       /* We can't rely on this variable retaining its value across a
-          call to execute_simple_command if a longjmp occurs as the
-          result of a `return' builtin.  This is true for sure with gcc. */
-       pid_t last_pid = last_made_pid;
+    case cm_connection:
+      exec_result = execute_connection (command, asynchronous,
+                                       pipe_in, pipe_out, fds_to_close);
+      break;
 
-       if (ignore_return && command->value.Simple)
-         command->value.Simple->flags |= CMD_IGNORE_RETURN;
-       exec_result =
-         execute_simple_command (command->value.Simple, pipe_in, pipe_out,
-                                 asynchronous, fds_to_close);
+    case cm_function_def:
+      exec_result = execute_intern_function (command->value.Function_def->name,
+                                            command->value.Function_def->command);
+      break;
 
-       /* The temporary environment should be used for only the simple
-          command immediately following its definition. */
-       dispose_used_env_vars ();
+    default:
+      programming_error
+       ("execute_command: bad command type `%d'", command->type);
+    }
 
-#if (defined (Ultrix) && defined (mips)) || !defined (HAVE_ALLOCA)
-       /* Reclaim memory allocated with alloca () on machines which
-          may be using the alloca emulation code. */
-       (void) alloca (0);
-#endif /* (Ultrix && mips) || !HAVE_ALLOCA */
+  if (my_undo_list)
+    {
+      do_redirections (my_undo_list, 1, 0, 0);
+      dispose_redirects (my_undo_list);
+    }
 
-       /* If we forked to do the command, then we must wait_for ()
-          the child. */
+  if (exec_undo_list)
+    dispose_redirects (exec_undo_list);
 
-       /* XXX - this is something to watch out for if there are problems
-          when the shell is compiled without job control. */
-       if (already_making_children && pipe_out == NO_PIPE &&
-           last_pid != last_made_pid)
-         {
-           stop_pipeline (asynchronous, (COMMAND *)NULL);
+  if (my_undo_list || exec_undo_list)
+    discard_unwind_frame ("loop_redirections");
 
-           if (asynchronous)
-             {
-               DESCRIBE_PID (last_made_pid);
-             }
-           else
-#if !defined (JOB_CONTROL)
-             /* Do not wait for asynchronous processes started from
-                startup files. */
-           if (last_made_pid != last_asynchronous_pid)
-#endif
-           /* When executing a shell function that executes other
-              commands, this causes the last simple command in
-              the function to be waited for twice. */
-             exec_result = wait_for (last_made_pid);
-         }
-      }
+  /* Invert the return value if we have to */
+  if (invert)
+    exec_result = (exec_result == EXECUTION_SUCCESS)
+                   ? EXECUTION_FAILURE
+                   : EXECUTION_SUCCESS;
 
-      if (!ignore_return && exit_immediately_on_error && !invert &&
-         (exec_result != EXECUTION_SUCCESS))
-       {
-         last_command_exit_value = exec_result;
-         run_pending_traps ();
-         longjmp (top_level, EXITPROG);
-       }
+  last_command_exit_value = exec_result;
+  run_pending_traps ();
+  if (running_trap == 0)
+    currently_executing_command = (COMMAND *)NULL;
+  return (last_command_exit_value);
+}
 
-      break;
+#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;
+}
 
-    case cm_connection:
-      switch (command->value.Connection->connector)
-       {
-         /* Do the first command asynchronously. */
-       case '&':
-         {
-           COMMAND *tc = command->value.Connection->first;
-           REDIRECT *rp;
+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;
+}
 
-           if (!tc)
-             break;
+/* 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;
 
-           rp = tc->redirects;
+  addtimeval (&t1, ut, st);
+  t2.tv_sec = rt->tv_sec;
+  t2.tv_usec = rt->tv_usec;
 
-           if (ignore_return && tc)
-             tc->flags |= CMD_IGNORE_RETURN;
+  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;
+    }
 
-           /* If this shell was compiled without job control support, if
-              the shell is not running interactively, if we are currently
-              in a subshell via `( xxx )', or if job control is not active
-              then the standard input for an asynchronous command is
-              forced to /dev/null. */
-#if defined (JOB_CONTROL)
-           if ((!interactive_shell || subshell_environment || !job_control) &&
-               !stdin_redir)
-#else
-           if (!stdin_redir)
-#endif /* JOB_CONTROL */
-           {
-             REDIRECT *tr;
+  return (t1.tv_sec / t2.tv_sec);
+}  
+#endif /* HAVE_GETRUSAGE && HAVE_GETTIMEOFDAY */
 
-             rd.filename = make_word ("/dev/null");
-             tr = make_redirection (0, r_inputa_direction, rd);
-             tr->next = tc->redirects;
-             tc->redirects = tr;
-           }
+#define POSIX_TIMEFORMAT "real %2R\nuser %2U\nsys %2S"
+#define BASH_TIMEFORMAT  "\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS"
 
-           exec_result = execute_command_internal
-             (tc, 1, pipe_in, pipe_out, fds_to_close);
+static int precs[] = { 0, 100, 10, 1 };
 
-#if defined (JOB_CONTROL)
-           if ((!interactive_shell || subshell_environment || !job_control) &&
-               !stdin_redir)
-#else
-           if (!stdin_redir)
-#endif /* JOB_CONTROL */
-           {
-             /* Remove the redirection we added above.  It matters,
-                especially for loops, which call execute_command ()
-                multiple times with the same command. */
-             REDIRECT *tr, *tl;
+/* Expand one `%'-prefixed escape sequence from a time format string. */
+static int
+mkfmt (buf, prec, lng, sec, sec_fraction)
+     char *buf;
+     int prec, lng;
+     long sec;
+     int sec_fraction;
+{
+  long min;
+  char abuf[16];
+  int ind, aind;
 
-             tr = tc->redirects;
-             do
-               {
-                 tl = tc->redirects;
-                 tc->redirects = tc->redirects->next;
-               }
-             while (tc->redirects && tc->redirects != rp);
+  ind = 0;
+  abuf[15] = '\0';
 
-             tl->next = (REDIRECT *)NULL;
-             dispose_redirects (tr);
-           }
+  /* If LNG is non-zero, we want to decompose SEC into minutes and seconds. */
+  if (lng)
+    {
+      min = sec / 60;
+      sec %= 60;
+      aind = 14;
+      do
+       abuf[aind--] = (min % 10) + '0';
+      while (min /= 10);
+      aind++;
+      while (abuf[aind])
+        buf[ind++] = abuf[aind++];
+      buf[ind++] = 'm';
+    }
 
-           {
-             register COMMAND *second;
+  /* Now add the seconds. */
+  aind = 14;
+  do
+    abuf[aind--] = (sec % 10) + '0';
+  while (sec /= 10);
+  aind++;
+  while (abuf[aind])
+    buf[ind++] = abuf[aind++];
+
+  /* We want to add a decimal point and PREC places after it if PREC is
+     nonzero.  PREC is not greater than 3.  SEC_FRACTION is between 0
+     and 999. */
+  if (prec != 0)
+    {
+      buf[ind++] = '.';
+      for (aind = 1; aind <= prec; aind++)
+       {
+         buf[ind++] = (sec_fraction / precs[aind]) + '0';
+         sec_fraction %= precs[aind];
+       }
+    }
 
-             second = command->value.Connection->second;
+  if (lng)
+    buf[ind++] = 's';
+  buf[ind] = '\0';
 
-             if (second)
-               {
-                 if (ignore_return)
-                   second->flags |= CMD_IGNORE_RETURN;
+  return (ind);
+}
 
-                 exec_result = execute_command_internal
-                   (second, asynchronous, pipe_in, pipe_out, fds_to_close);
-               }
-           }
-         }
-         break;
+/* Interpret the format string FORMAT, interpolating the following escape
+   sequences:
+               %[prec][l][RUS]
+
+   where the optional `prec' is a precision, meaning the number of
+   characters after the decimal point, the optional `l' means to format
+   using minutes and seconds (MMmNN[.FF]s), like the `times' builtin',
+   and the last character is one of
+   
+               R       number of seconds of `real' time
+               U       number of seconds of `user' time
+               S       number of seconds of `system' time
+
+   An occurrence of `%%' in the format string is translated to a `%'.  The
+   result is printed to FP, a pointer to a FILE.  The other variables are
+   the seconds and thousandths of a second of real, user, and system time,
+   resectively. */
+static void
+print_formatted_time (fp, format, rs, rsf, us, usf, ss, ssf, cpu)
+     FILE *fp;
+     char *format;
+     long rs, us, ss;
+     int rsf, usf, ssf, cpu;
+{
+  int prec, lng, len;
+  char *str, *s, ts[32];
+  int sum, sum_frac;
+  int sindex, ssize;
 
-       case ';':
-         /* Just call execute command on both of them. */
-         if (ignore_return)
+  len = strlen (format);
+  ssize = (len + 64) - (len % 64);
+  str = xmalloc (ssize);
+  sindex = 0;
+
+  for (s = format; *s; s++)
+    {
+      if (*s != '%' || s[1] == '\0')
+        {
+          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;
+        }
+      else if (s[1] == 'P')
+       {
+         s++;
+         if (cpu > 10000)
+           cpu = 10000;
+         sum = cpu / 100;
+         sum_frac = (cpu % 100) * 10;
+         len = mkfmt (ts, 2, 0, sum, sum_frac);
+         RESIZE_MALLOCED_BUFFER (str, sindex, len, ssize, 64);
+         strcpy (str + sindex, ts);
+         sindex += len;
+       }
+      else
+       {
+         prec = 3;     /* default is three places past the decimal point. */
+         lng = 0;      /* default is to not use minutes or append `s' */
+         s++;
+         if (isdigit (*s))             /* `precision' */
            {
-             if (command->value.Connection->first)
-               command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
-             if (command->value.Connection->second)
-               command->value.Connection->second->flags |= CMD_IGNORE_RETURN;
+             prec = *s++ - '0';
+             if (prec > 3) prec = 3;
            }
-         QUIT;
-         execute_command (command->value.Connection->first);
-         QUIT;
-         exec_result =
-           execute_command_internal (command->value.Connection->second,
-                                     asynchronous, pipe_in, pipe_out,
-                                     fds_to_close);
-         break;
-
-       case '|':
-         {
-           int prev, fildes[2], new_bitmap_size, dummyfd;
-           COMMAND *cmd;
-           struct fd_bitmap *fd_bitmap;
+         if (*s == 'l')                /* `length extender' */
+           {
+             lng = 1;
+             s++;
+           }
+         if (*s == 'R' || *s == 'E')
+           len = mkfmt (ts, prec, lng, rs, rsf);
+         else if (*s == 'U')
+           len = mkfmt (ts, prec, lng, us, usf);
+         else if (*s == 'S')
+           len = mkfmt (ts, prec, lng, ss, ssf);
+         else
+           {
+             internal_error ("bad format character in time format: %c", *s);
+             free (str);
+             return;
+           }
+         RESIZE_MALLOCED_BUFFER (str, sindex, len, ssize, 64);
+         strcpy (str + sindex, ts);
+         sindex += len;
+       }
+    }
+
+  str[sindex] = '\0';
+  fprintf (fp, "%s\n", str);
+  fflush (fp);
+
+  free (str);
+}
+
+static int
+time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
+     COMMAND *command;
+     int asynchronous, pipe_in, pipe_out;
+     struct fd_bitmap *fds_to_close;
+{
+  int rv, posix_time;
+  long rs, us, ss;
+  int rsf, usf, ssf;
+  int cpu;
+  char *time_format;
+
+#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
+  struct timeval real, user, sys;
+  struct timeval before, after;
+  struct timezone dtz;
+  struct rusage selfb, selfa, kidsb, kidsa;    /* a = after, b = before */
+#else
+#  if defined (HAVE_TIMES)
+  clock_t tbefore, tafter, real, user, sys;
+  struct tms before, after;
+#  endif
+#endif
+
+#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
+  gettimeofday (&before, &dtz);
+  getrusage (RUSAGE_SELF, &selfb);
+  getrusage (RUSAGE_CHILDREN, &kidsb);
+#else
+#  if defined (HAVE_TIMES)
+  tbefore = times (&before);
+#  endif
+#endif
+
+  posix_time = (command->flags & CMD_TIME_POSIX);
+
+  command->flags &= ~(CMD_TIME_PIPELINE|CMD_TIME_POSIX);
+  rv = execute_command_internal (command, asynchronous, pipe_in, pipe_out, fds_to_close);
+
+#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
+  gettimeofday (&after, &dtz);
+  getrusage (RUSAGE_SELF, &selfa);
+  getrusage (RUSAGE_CHILDREN, &kidsa);
+
+  difftimeval (&real, &before, &after);
+  timeval_to_secs (&real, &rs, &rsf);
+
+  addtimeval (&user, difftimeval(&after, &selfb.ru_utime, &selfa.ru_utime),
+                    difftimeval(&before, &kidsb.ru_utime, &kidsa.ru_utime));
+  timeval_to_secs (&user, &us, &usf);
+
+  addtimeval (&sys, difftimeval(&after, &selfb.ru_stime, &selfa.ru_stime),
+                   difftimeval(&before, &kidsb.ru_stime, &kidsa.ru_stime));
+  timeval_to_secs (&sys, &ss, &ssf);
+
+  cpu = timeval_to_cpu (&real, &user, &sys);
+#else
+#  if defined (HAVE_TIMES)
+  tafter = times (&after);
+
+  real = tafter - tbefore;
+  clock_t_to_secs (real, &rs, &rsf);
+
+  user = (after.tms_utime - before.tms_utime) + (after.tms_cutime - before.tms_cutime);
+  clock_t_to_secs (user, &us, &usf);
+
+  sys = (after.tms_stime - before.tms_stime) + (after.tms_cstime - before.tms_cstime);
+  clock_t_to_secs (sys, &ss, &ssf);
+
+  cpu = ((user + sys) * 10000) / real;
+
+#  else
+  rs = us = ss = 0L;
+  rsf = usf = ssf = cpu = 0;
+#  endif
+#endif
+
+  if (posix_time)
+    time_format = POSIX_TIMEFORMAT;
+  else if ((time_format = get_string_value ("TIMEFORMAT")) == 0)
+    time_format = BASH_TIMEFORMAT;
+
+  if (time_format && *time_format)
+    print_formatted_time (stderr, time_format, rs, rsf, us, usf, ss, ssf, cpu);
+
+  return rv;
+}
+#endif /* COMMAND_TIMING */
+
+static int
+execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
+     COMMAND *command;
+     int asynchronous, pipe_in, pipe_out;
+     struct fd_bitmap *fds_to_close;
+{
+  int prev, fildes[2], new_bitmap_size, dummyfd, ignore_return, exec_result;
+  COMMAND *cmd;
+  struct fd_bitmap *fd_bitmap;
 
 #if defined (JOB_CONTROL)
-           sigset_t set, oset;
-           BLOCK_CHILD (set, oset);
+  sigset_t set, oset;
+  BLOCK_CHILD (set, oset);
 #endif /* JOB_CONTROL */
 
-           prev = pipe_in;
-           cmd = command;
+  ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
 
-           while (cmd &&
-                  cmd->type == cm_connection &&
-                  cmd->value.Connection &&
-                  cmd->value.Connection->connector == '|')
-             {
-               /* Make a pipeline between the two commands. */
-               if (pipe (fildes) < 0)
-                 {
-                   report_error ("pipe error: %s", strerror (errno));
+  prev = pipe_in;
+  cmd = command;
+
+  while (cmd && cmd->type == cm_connection &&
+        cmd->value.Connection && cmd->value.Connection->connector == '|')
+    {
+      /* Make a pipeline between the two commands. */
+      if (pipe (fildes) < 0)
+       {
+         sys_error ("pipe error");
 #if defined (JOB_CONTROL)
-                   terminate_current_pipeline ();
-                   kill_current_pipeline ();
+         terminate_current_pipeline ();
+         kill_current_pipeline ();
 #endif /* JOB_CONTROL */
-                   last_command_exit_value = EXECUTION_FAILURE;
-                   /* The unwind-protects installed below will take care
-                      of closing all of the open file descriptors. */
-                   throw_to_top_level ();
-                 }
-               else
-                 {
-                   /* Here is a problem: with the new file close-on-exec
-                      code, the read end of the pipe (fildes[0]) stays open
-                      in the first process, so that process will never get a
-                      SIGPIPE.  There is no way to signal the first process
-                      that it should close fildes[0] after forking, so it
-                      remains open.  No SIGPIPE is ever sent because there
-                      is still a file descriptor open for reading connected
-                      to the pipe.  We take care of that here.  This passes
-                      around a bitmap of file descriptors that must be
-                      closed after making a child process in
-                      execute_simple_command. */
-
-                   /* We need fd_bitmap to be at least as big as fildes[0].
-                      If fildes[0] is less than fds_to_close->size, then
-                      use fds_to_close->size. */
-                   if (fildes[0] < fds_to_close->size)
-                     new_bitmap_size = fds_to_close->size;
-                   else
-                     new_bitmap_size = fildes[0] + 8;
-
-                   fd_bitmap = new_fd_bitmap (new_bitmap_size);
-
-                   /* Now copy the old information into the new bitmap. */
-                   xbcopy ((char *)fds_to_close->bitmap,
-                           (char *)fd_bitmap->bitmap, fds_to_close->size);
-
-                   /* And mark the pipe file descriptors to be closed. */
-                   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
-                      unwind-protects are run, and the storage used for the
-                      bitmaps freed up. */
-                   begin_unwind_frame ("pipe-file-descriptors");
-                   add_unwind_protect (dispose_fd_bitmap, fd_bitmap);
-                   add_unwind_protect (close_fd_bitmap, fd_bitmap);
-                   if (prev >= 0)
-                     add_unwind_protect (close, prev);
-                   dummyfd = fildes[1];
-                   add_unwind_protect (close, dummyfd);
+         last_command_exit_value = EXECUTION_FAILURE;
+         /* The unwind-protects installed below will take care
+            of closing all of the open file descriptors. */
+         throw_to_top_level ();
+         return (EXECUTION_FAILURE);   /* XXX */
+       }
+
+      /* Here is a problem: with the new file close-on-exec
+        code, the read end of the pipe (fildes[0]) stays open
+        in the first process, so that process will never get a
+        SIGPIPE.  There is no way to signal the first process
+        that it should close fildes[0] after forking, so it
+        remains open.  No SIGPIPE is ever sent because there
+        is still a file descriptor open for reading connected
+        to the pipe.  We take care of that here.  This passes
+        around a bitmap of file descriptors that must be
+        closed after making a child process in execute_simple_command. */
+
+      /* We need fd_bitmap to be at least as big as fildes[0].
+        If fildes[0] is less than fds_to_close->size, then
+        use fds_to_close->size. */
+      new_bitmap_size = (fildes[0] < fds_to_close->size)
+                               ? fds_to_close->size
+                               : fildes[0] + 8;
+
+      fd_bitmap = new_fd_bitmap (new_bitmap_size);
+
+      /* Now copy the old information into the new bitmap. */
+      xbcopy ((char *)fds_to_close->bitmap, (char *)fd_bitmap->bitmap, fds_to_close->size);
+
+      /* And mark the pipe file descriptors to be closed. */
+      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
+        unwind-protects are run, and the storage used for the
+        bitmaps freed up. */
+      begin_unwind_frame ("pipe-file-descriptors");
+      add_unwind_protect (dispose_fd_bitmap, fd_bitmap);
+      add_unwind_protect (close_fd_bitmap, fd_bitmap);
+      if (prev >= 0)
+       add_unwind_protect (close, prev);
+      dummyfd = fildes[1];
+      add_unwind_protect (close, dummyfd);
 
 #if defined (JOB_CONTROL)
-                   add_unwind_protect (restore_signal_mask, oset);
+      add_unwind_protect (restore_signal_mask, oset);
 #endif /* JOB_CONTROL */
 
-                   if (ignore_return && cmd->value.Connection->first)
-                     cmd->value.Connection->first->flags |=
-                       CMD_IGNORE_RETURN;
-                   execute_command_internal
-                     (cmd->value.Connection->first, asynchronous, prev,
-                      fildes[1], fd_bitmap);
-
-                   if (prev >= 0)
-                     close (prev);
-                   
-                   prev = fildes[0];
-                   close (fildes[1]);
-
-                   dispose_fd_bitmap (fd_bitmap);
-                   discard_unwind_frame ("pipe-file-descriptors");
-                 }
-               cmd = cmd->value.Connection->second;
-             }
+      if (ignore_return && cmd->value.Connection->first)
+       cmd->value.Connection->first->flags |= CMD_IGNORE_RETURN;
+      execute_command_internal (cmd->value.Connection->first, asynchronous,
+                               prev, fildes[1], fd_bitmap);
+
+      if (prev >= 0)
+       close (prev);
+
+      prev = fildes[0];
+      close (fildes[1]);
+
+      dispose_fd_bitmap (fd_bitmap);
+      discard_unwind_frame ("pipe-file-descriptors");
 
-           /* Now execute the rightmost command in the pipeline.  */
-           if (ignore_return && cmd)
-             cmd->flags |= CMD_IGNORE_RETURN;
-           exec_result =
-             execute_command_internal
-               (cmd, asynchronous, prev, pipe_out, fds_to_close);
+      cmd = cmd->value.Connection->second;
+    }
+
+  /* Now execute the rightmost command in the pipeline.  */
+  if (ignore_return && cmd)
+    cmd->flags |= CMD_IGNORE_RETURN;
+  exec_result = execute_command_internal (cmd, asynchronous, prev, pipe_out, fds_to_close);
 
-           if (prev >= 0)
-             close (prev);
+  if (prev >= 0)
+    close (prev);
 
 #if defined (JOB_CONTROL)
-           UNBLOCK_CHILD (oset);
+  UNBLOCK_CHILD (oset);
 #endif
-         }
-         break;
 
-       case AND_AND:
-       case OR_OR:
-         if (asynchronous)
+  return (exec_result);
+}
+
+static int
+execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
+     COMMAND *command;
+     int asynchronous, pipe_in, pipe_out;
+     struct fd_bitmap *fds_to_close;
+{
+  REDIRECT *tr, *tl, *rp;
+  COMMAND *tc, *second;
+  int ignore_return, exec_result;
+
+  ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
+
+  switch (command->value.Connection->connector)
+    {
+    /* Do the first command asynchronously. */
+    case '&':
+      tc = command->value.Connection->first;
+      if (tc == 0)
+       return (EXECUTION_SUCCESS);
+
+      rp = tc->redirects;
+
+      if (ignore_return && tc)
+       tc->flags |= CMD_IGNORE_RETURN;
+
+      /* If this shell was compiled without job control support, if
+        the shell is not running interactively, if we are currently
+        in a subshell via `( xxx )', or if job control is not active
+        then the standard input for an asynchronous command is
+        forced to /dev/null. */
+#if defined (JOB_CONTROL)
+      if ((!interactive_shell || subshell_environment || !job_control) && !stdin_redir)
+#else
+      if (!stdin_redir)
+#endif /* JOB_CONTROL */
+       {
+         rd.filename = make_bare_word ("/dev/null");
+         tr = make_redirection (0, r_inputa_direction, rd);
+         tr->next = tc->redirects;
+         tc->redirects = tr;
+       }
+
+      exec_result = execute_command_internal (tc, 1, pipe_in, pipe_out, fds_to_close);
+
+#if defined (JOB_CONTROL)
+      if ((!interactive_shell || subshell_environment || !job_control) && !stdin_redir)
+#else
+      if (!stdin_redir)
+#endif /* JOB_CONTROL */
+       {
+         /* Remove the redirection we added above.  It matters,
+            especially for loops, which call execute_command ()
+            multiple times with the same command. */
+         tr = tc->redirects;
+         do
            {
-             /* If we have something like `a && b &' or `a || b &', run the
-                && or || stuff in a subshell.  Force a subshell and just call
-                execute_command_internal again.  Leave asynchronous on
-                so that we get a report from the parent shell about the
-                background job. */
-             command->flags |= CMD_FORCE_SUBSHELL;
-             exec_result = execute_command_internal (command, 1, pipe_in,
-                             pipe_out, fds_to_close);
-             break;
+             tl = tc->redirects;
+             tc->redirects = tc->redirects->next;
            }
+         while (tc->redirects && tc->redirects != rp);
 
-         /* Execute the first command.  If the result of that is successful
-            and the connector is AND_AND, or the result is not successful
-            and the connector is OR_OR, then execute the second command,
-            otherwise return. */
+         tl->next = (REDIRECT *)NULL;
+         dispose_redirects (tr);
+       }
 
-         if (command->value.Connection->first)
-           command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
+      second = command->value.Connection->second;
+      if (second)
+       {
+         if (ignore_return)
+           second->flags |= CMD_IGNORE_RETURN;
 
-         exec_result = execute_command (command->value.Connection->first);
-         QUIT;
-         if (((command->value.Connection->connector == AND_AND) && 
-              (exec_result == EXECUTION_SUCCESS)) ||
-             ((command->value.Connection->connector == OR_OR) &&
-              (exec_result != EXECUTION_SUCCESS)))
-           {
-             if (ignore_return && command->value.Connection->second)
-               command->value.Connection->second->flags |=
-                 CMD_IGNORE_RETURN;
+         exec_result = execute_command_internal (second, asynchronous, pipe_in, pipe_out, fds_to_close);
+       }
 
-             exec_result =
-               execute_command (command->value.Connection->second);
-           }
-         break;
+      break;
 
-       default:
-         programming_error ("Bad connector `%d'!",
-                            command->value.Connection->connector);
-         longjmp (top_level, DISCARD);
-         break;
+    /* Just call execute command on both sides. */
+    case ';':
+      if (ignore_return)
+       {
+         if (command->value.Connection->first)
+           command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
+         if (command->value.Connection->second)
+           command->value.Connection->second->flags |= CMD_IGNORE_RETURN;
        }
+      QUIT;
+      execute_command (command->value.Connection->first);
+      QUIT;
+      exec_result = execute_command_internal (command->value.Connection->second,
+                                     asynchronous, pipe_in, pipe_out,
+                                     fds_to_close);
       break;
 
-    case cm_function_def:
-      exec_result = intern_function (command->value.Function_def->name,
-                                    command->value.Function_def->command);
+    case '|':
+      exec_result = execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close);
       break;
 
-    default:
-      programming_error
-       ("execute_command: Bad command type `%d'!", command->type);
-    }
+    case AND_AND:
+    case OR_OR:
+      if (asynchronous)
+       {
+         /* If we have something like `a && b &' or `a || b &', run the
+            && or || stuff in a subshell.  Force a subshell and just call
+            execute_command_internal again.  Leave asynchronous on
+            so that we get a report from the parent shell about the
+            background job. */
+         command->flags |= CMD_FORCE_SUBSHELL;
+         exec_result = execute_command_internal (command, 1, pipe_in, pipe_out, fds_to_close);
+         break;
+       }
 
-  if (my_undo_list)
-    {
-      do_redirections (my_undo_list, 1, 0, 0);
-      dispose_redirects (my_undo_list);
-    }
+      /* Execute the first command.  If the result of that is successful
+        and the connector is AND_AND, or the result is not successful
+        and the connector is OR_OR, then execute the second command,
+        otherwise return. */
 
-  if (exec_undo_list)
-    dispose_redirects (exec_undo_list);
+      if (command->value.Connection->first)
+       command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
 
-  if (my_undo_list || exec_undo_list)
-    discard_unwind_frame ("loop_redirections");
+      exec_result = execute_command (command->value.Connection->first);
+      QUIT;
+      if (((command->value.Connection->connector == AND_AND) &&
+          (exec_result == EXECUTION_SUCCESS)) ||
+         ((command->value.Connection->connector == OR_OR) &&
+          (exec_result != EXECUTION_SUCCESS)))
+       {
+         if (ignore_return && command->value.Connection->second)
+           command->value.Connection->second->flags |= CMD_IGNORE_RETURN;
 
-  /* Invert the return value if we have to */
-  if (invert)
-    {
-      if (exec_result == EXECUTION_SUCCESS)
-       exec_result = EXECUTION_FAILURE;
-      else
-       exec_result = EXECUTION_SUCCESS;
+         exec_result = execute_command (command->value.Connection->second);
+       }
+      break;
+
+    default:
+      programming_error ("execute_connection: bad connector `%d'", command->value.Connection->connector);
+      jump_to_top_level (DISCARD);
+      exec_result = EXECUTION_FAILURE;
     }
 
-  last_command_exit_value = exec_result;
-  run_pending_traps ();
-  return (last_command_exit_value);
+  return exec_result;
 }
 
 #if defined (JOB_CONTROL)
@@ -1009,22 +1416,27 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 
 /* Execute a FOR command.  The syntax is: FOR word_desc IN word_list;
    DO command; DONE */
+static int
 execute_for_command (for_command)
      FOR_COM *for_command;
 {
-  /* I just noticed that the Bourne shell leaves word_desc bound to the
-     last name in word_list after the FOR statement is done.  This seems
-     wrong to me; I thought that the variable binding should be lexically
-     scoped, i.e., only would last the duration of the FOR command.  This
-     behaviour can be gotten by turning on the lexical_scoping switch. */
-
   register WORD_LIST *releaser, *list;
+  SHELL_VAR *v;
   char *identifier;
+  int retval;
+#if 0
   SHELL_VAR *old_value = (SHELL_VAR *)NULL; /* Remember the old value of x. */
-  int retval = EXECUTION_SUCCESS;
+#endif
 
   if (check_identifier (for_command->name, 1) == 0)
-    return (EXECUTION_FAILURE);
+    {
+      if (posixly_correct && interactive_shell == 0)
+        {
+          last_command_exit_value = EX_USAGE;
+          jump_to_top_level (EXITPROG);
+        }
+      return (EXECUTION_FAILURE);
+    }
 
   loop_level++;
   identifier = for_command->name->word;
@@ -1034,28 +1446,43 @@ execute_for_command (for_command)
   begin_unwind_frame ("for");
   add_unwind_protect (dispose_words, releaser);
 
+#if 0
   if (lexical_scoping)
     {
       old_value = copy_variable (find_variable (identifier));
       if (old_value)
        add_unwind_protect (dispose_variable, old_value);
     }
+#endif
 
   if (for_command->flags & CMD_IGNORE_RETURN)
     for_command->action->flags |= CMD_IGNORE_RETURN;
 
-  while (list)
+  for (retval = EXECUTION_SUCCESS; list; list = list->next)
     {
       QUIT;
-      bind_variable (identifier, list->word->word);
-      execute_command (for_command->action);
-      retval = last_command_exit_value;
+      this_command_name = (char *)NULL;
+      v = bind_variable (identifier, list->word->word);
+      if (readonly_p (v))
+       {
+         if (interactive_shell == 0 && posixly_correct)
+           {
+             last_command_exit_value = EXECUTION_FAILURE;
+             jump_to_top_level (FORCE_EOF);
+           }
+         else
+           {
+             run_unwind_frame ("for");
+             return (EXECUTION_FAILURE);
+           }
+       }
+      retval = execute_command (for_command->action);
       REAP ();
       QUIT;
 
       if (breaking)
        {
-         breaking--; 
+         breaking--;
          break;
        }
 
@@ -1065,12 +1492,11 @@ execute_for_command (for_command)
          if (continuing)
            break;
        }
-
-      list = list->next;
     }
 
   loop_level--;
 
+#if 0
   if (lexical_scoping)
     {
       if (!old_value)
@@ -1084,6 +1510,7 @@ execute_for_command (for_command)
          dispose_variable (old_value);
        }
     }
+#endif
 
   dispose_words (releaser);
   discard_unwind_frame ("for");
@@ -1115,10 +1542,8 @@ print_index_and_element (len, ind, list)
 
   if (list == 0)
     return (0);
-  i = ind;
-  l = list;
-  while (l && --i)
-    l = l->next;
+  for (i = ind, l = list; l && --i; l = l->next)
+    ;
   fprintf (stderr, "%*d%s%s", len, ind, RP_SPACE, l->word->word);
   return (STRLEN (l->word->word));
 }
@@ -1156,7 +1581,7 @@ print_select_list (list, list_len, max_elem_len, indices_len)
       return;
     }
 
-  cols = COLS / max_elem_len;
+  cols = max_elem_len ? COLS / max_elem_len : 1;
   if (cols == 0)
     cols = 1;
   rows = list_len ? list_len / cols + (list_len % cols != 0) : 1;
@@ -1232,8 +1657,8 @@ select_query (list, list_len, prompt)
   while (1)
     {
       print_select_list (list, list_len, max_elem_len, indices_len);
-      printf ("%s", prompt);
-      fflush (stdout);
+      fprintf (stderr, "%s", prompt);
+      fflush (stderr);
       QUIT;
 
       if (read_builtin ((WORD_LIST *)NULL) == EXECUTION_FAILURE)
@@ -1248,9 +1673,8 @@ select_query (list, list_len, prompt)
       if (reply < 1 || reply > list_len)
        return "";
 
-      l = list;
-      while (l && --reply)
-        l = l->next;
+      for (l = list; l && --reply; l = l->next)
+       ;
       return (l->word->word);
     }
 }
@@ -1259,18 +1683,14 @@ select_query (list, list_len, prompt)
    SELECT word IN list DO command_list DONE
    Only `break' or `return' in command_list will terminate
    the command. */
+static int
 execute_select_command (select_command)
      SELECT_COM *select_command;
 {
   WORD_LIST *releaser, *list;
+  SHELL_VAR *v;
   char *identifier, *ps3_prompt, *selection;
   int retval, list_len, return_val;
-#if 0
-  SHELL_VAR *old_value = (SHELL_VAR *)0;
-#endif
-
-
-  retval = EXECUTION_SUCCESS;
 
   if (check_identifier (select_command->name, 1) == 0)
     return (EXECUTION_FAILURE);
@@ -1292,18 +1712,11 @@ execute_select_command (select_command)
   begin_unwind_frame ("select");
   add_unwind_protect (dispose_words, releaser);
 
-#if 0
-  if (lexical_scoping)
-    {
-      old_value = copy_variable (find_variable (identifier));
-      if (old_value)
-       add_unwind_protect (dispose_variable, old_value);
-    }
-#endif
-
   if (select_command->flags & CMD_IGNORE_RETURN)
     select_command->action->flags |= CMD_IGNORE_RETURN;
 
+  retval = EXECUTION_SUCCESS;
+
   unwind_protect_int (return_catch_flag);
   unwind_protect_jmp_buf (return_catch);
   return_catch_flag++;
@@ -1311,7 +1724,7 @@ execute_select_command (select_command)
   while (1)
     {
       ps3_prompt = get_string_value ("PS3");
-      if (!ps3_prompt)
+      if (ps3_prompt == 0)
        ps3_prompt = "#? ";
 
       QUIT;
@@ -1319,8 +1732,21 @@ execute_select_command (select_command)
       QUIT;
       if (selection == 0)
        break;
-      else
-       bind_variable (identifier, selection);
+
+      v = bind_variable (identifier, selection);
+      if (readonly_p (v))
+       {
+         if (interactive_shell == 0 && posixly_correct)
+           {
+             last_command_exit_value = EXECUTION_FAILURE;
+             jump_to_top_level (FORCE_EOF);
+           }
+         else
+           {
+             run_unwind_frame ("select");
+             return (EXECUTION_FAILURE);
+           }
+       }
 
       return_val = setjmp (return_catch);
 
@@ -1344,22 +1770,6 @@ execute_select_command (select_command)
 
   loop_level--;
 
-#if 0
-  if (lexical_scoping)
-    {
-      if (!old_value)
-       makunbound (identifier, shell_variables);
-      else
-       {
-         SHELL_VAR *new_value;
-
-         new_value = bind_variable (identifier, value_cell(old_value));
-         new_value->attributes = old_value->attributes;
-         dispose_variable (old_value);
-       }
-    }
-#endif
-
   run_unwind_frame ("select");
   return (retval);
 }
@@ -1369,49 +1779,48 @@ execute_select_command (select_command)
    The pattern_list is a linked list of pattern clauses; each clause contains
    some patterns to compare word_desc against, and an associated command to
    execute. */
+static int
 execute_case_command (case_command)
      CASE_COM *case_command;
 {
   register WORD_LIST *list;
-  WORD_LIST *wlist;
+  WORD_LIST *wlist, *es;
   PATTERN_LIST *clauses;
-  char *word;
-  int retval;
+  char *word, *pattern;
+  int retval, match, ignore_return;
 
   /* Posix.2 specifies that the WORD is tilde expanded. */
   if (member ('~', case_command->word->word))
     {
-      word = tilde_expand (case_command->word->word);
+      word = bash_tilde_expand (case_command->word->word);
       free (case_command->word->word);
       case_command->word->word = word;
     }
 
   wlist = expand_word_no_split (case_command->word, 0);
-  clauses = case_command->clauses;
-  word = (wlist) ? string_list (wlist) : savestring ("");
+  word = wlist ? string_list (wlist) : savestring ("");
+  dispose_words (wlist);
+
   retval = EXECUTION_SUCCESS;
+  ignore_return = case_command->flags & CMD_IGNORE_RETURN;
 
   begin_unwind_frame ("case");
-  add_unwind_protect (dispose_words, wlist);
   add_unwind_protect ((Function *)xfree, word);
 
-  while (clauses)
+#define EXIT_CASE()  goto exit_case_command
+
+  for (clauses = case_command->clauses; clauses; clauses = clauses->next)
     {
       QUIT;
-      list = clauses->patterns;
-      while (list)
+      for (list = clauses->patterns; list; list = list->next)
        {
-         char *pattern;
-         WORD_LIST *es;
-         int match;
-
          /* Posix.2 specifies to tilde expand each member of the pattern
             list. */
          if (member ('~', list->word->word))
            {
-             char *expansion = tilde_expand (list->word->word);
+             pattern = bash_tilde_expand (list->word->word);
              free (list->word->word);
-             list->word->word = expansion;
+             list->word->word = pattern;
            }
 
          es = expand_word_leave_quoted (list->word, 0);
@@ -1419,38 +1828,34 @@ execute_case_command (case_command)
          if (es && es->word && es->word->word && *(es->word->word))
            pattern = quote_string_for_globbing (es->word->word, 1);
          else
-           pattern = savestring ("");
+           {
+             pattern = xmalloc (1);
+             pattern[0] = '\0';
+           }
 
          /* 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, 0) != FNM_NOMATCH;
          free (pattern);
 
          dispose_words (es);
 
          if (match)
            {
-             if (clauses->action && 
-                 (case_command->flags & CMD_IGNORE_RETURN))
+             if (clauses->action && ignore_return)
                clauses->action->flags |= CMD_IGNORE_RETURN;
-             execute_command (clauses->action);
-             retval = last_command_exit_value;
-             goto exit_command;
+             retval = execute_command (clauses->action);
+             EXIT_CASE ();
            }
 
-         list = list->next;
          QUIT;
        }
-
-      clauses = clauses->next;
     }
 
- exit_command:
-  dispose_words (wlist);
-  free (word);  
+exit_case_command:
+  free (word);
   discard_unwind_frame ("case");
-
   return (retval);
 }
 
@@ -1460,6 +1865,7 @@ execute_case_command (case_command)
 /* The WHILE command.  Syntax: WHILE test DO action; DONE.
    Repeatedly execute action while executing test produces
    EXECUTION_SUCCESS. */
+static int
 execute_while_command (while_command)
      WHILE_COM *while_command;
 {
@@ -1467,6 +1873,7 @@ execute_while_command (while_command)
 }
 
 /* UNTIL is just like WHILE except that the test result is negated. */
+static int
 execute_until_command (while_command)
      WHILE_COM *while_command;
 {
@@ -1478,6 +1885,7 @@ execute_until_command (while_command)
    CMD_WHILE or CMD_UNTIL.  The return value for both commands should
    be EXECUTION_SUCCESS if no commands in the body are executed, and
    the status of the last command executed in the body otherwise. */
+static int
 execute_while_or_until (while_command, type)
      WHILE_COM *while_command;
      int type;
@@ -1526,6 +1934,7 @@ execute_while_or_until (while_command, type)
 /* IF test THEN command [ELSE command].
    IF also allows ELIF in the place of ELSE IF, but
    the parser makes *that* stupidity transparent. */
+static int
 execute_if_command (if_command)
      IF_COM *if_command;
 {
@@ -1537,8 +1946,10 @@ execute_if_command (if_command)
   if (return_value == EXECUTION_SUCCESS)
     {
       QUIT;
+
       if (if_command->true_case && (if_command->flags & CMD_IGNORE_RETURN))
-         if_command->true_case->flags |= CMD_IGNORE_RETURN;
+       if_command->true_case->flags |= CMD_IGNORE_RETURN;
+
       return (execute_command (if_command->true_case));
     }
   else
@@ -1558,39 +1969,117 @@ bind_lastarg (arg)
 {
   SHELL_VAR *var;
 
-  if (!arg)
+  if (arg == 0)
     arg = "";
   var = bind_variable ("_", arg);
   var->attributes &= ~att_exported;
 }
 
+/* Execute a null command.  Fork a subshell if the command uses pipes or is
+   to be run asynchronously.  This handles all the side effects that are
+   supposed to take place. */
+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;
+{
+  if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
+    {
+      /* We have a null command, but we really want a subshell to take
+        care of it.  Just fork, do piping and redirections, and exit. */
+      if (make_child ((char *)NULL, async) == 0)
+       {
+         /* Cancel traps, in trap.c. */
+         restore_original_signals ();          /* XXX */
+
+         do_piping (pipe_in, pipe_out);
+
+         subshell_environment = SUBSHELL_ASYNC;
+
+         if (do_redirections (redirects, 1, 0, 0) == 0)
+           exit (EXECUTION_SUCCESS);
+         else
+           exit (EXECUTION_FAILURE);
+       }
+      else
+       {
+         close_pipes (pipe_in, pipe_out);
+#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
+         unlink_fifo_list ();
+#endif
+         return (EXECUTION_SUCCESS);
+       }
+    }
+  else
+    {
+      /* Even if there aren't any command names, pretend to do the
+        redirections that are specified.  The user expects the side
+        effects to take place.  If the redirections fail, then return
+        failure.  Otherwise, if a command substitution took place while
+        expanding the command or a redirection, return the value of that
+        substitution.  Otherwise, return EXECUTION_SUCCESS. */
+
+      if (do_redirections (redirects, 0, 0, 0) != 0)
+       return (EXECUTION_FAILURE);
+      else if (old_last_command_subst_pid != last_command_subst_pid)
+       return (last_command_exit_value);
+      else
+       return (EXECUTION_SUCCESS);
+    }
+}
+
+/* This is a hack to suppress word splitting for assignment statements
+   given as arguments to builtins with the ASSIGNMENT_BUILTIN flag set. */
+static void
+fix_assignment_words (words)
+     WORD_LIST *words;
+{
+  WORD_LIST *w;
+  struct builtin *b;
+
+  if (words == 0)
+    return;
+
+  b = builtin_address_internal (words->word->word);
+  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;
+}
+
 /* The meaty part of all the executions.  We have to start hacking the
    real execution of commands here.  Fork a process, set things up,
    execute the command. */
+static int
 execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
      SIMPLE_COM *simple_command;
      int pipe_in, pipe_out, async;
      struct fd_bitmap *fds_to_close;
 {
   WORD_LIST *words, *lastword;
-  char *command_line, *lastarg;
-  int first_word_quoted, result;
+  char *command_line, *lastarg, *temp;
+  int first_word_quoted, result, builtin_is_special;
   pid_t old_last_command_subst_pid;
+  Function *builtin;
+  SHELL_VAR *func;
 
   result = EXECUTION_SUCCESS;
+  special_builtin_failed = builtin_is_special = 0;
 
-  /* If we're in a function, update the pseudo-line-number information. */
+  /* If we're in a function, update the line number information. */
   if (variable_context)
     line_number = simple_command->line - function_line_number;
 
   /* Remember what this command line looks like at invocation. */
   command_string_index = 0;
   print_simple_command (simple_command);
-  command_line = (char *)alloca (1 + strlen (the_printed_command));
+  command_line = xmalloc (1 + strlen (the_printed_command));
   strcpy (command_line, the_printed_command);
 
   first_word_quoted =
-    simple_command->words ? simple_command->words->word->quoted : 0;
+    simple_command->words ? (simple_command->words->word->flags & W_QUOTED): 0;
 
   old_last_command_subst_pid = last_command_subst_pid;
 
@@ -1599,249 +2088,251 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
   if ((simple_command->flags & CMD_INHIBIT_EXPANSION) == 0)
     {
       current_fds_to_close = fds_to_close;
+      fix_assignment_words (simple_command->words);
       words = expand_words (simple_command->words);
       current_fds_to_close = (struct fd_bitmap *)NULL;
     }
   else
     words = copy_word_list (simple_command->words);
 
-  lastarg = (char *)NULL;
-
   /* It is possible for WORDS not to have anything left in it.
      Perhaps all the words consisted of `$foo', and there was
      no variable `$foo'. */
-  if (words)
+  if (words == 0)
     {
-      Function *builtin;
-      SHELL_VAR *func;
+      result = execute_null_command (simple_command->redirects,
+                                    pipe_in, pipe_out, async,
+                                    old_last_command_subst_pid);
+      FREE (command_line);
+      bind_lastarg ((char *)NULL);
+      return (result);
+    }
 
-      begin_unwind_frame ("simple-command");
+  lastarg = (char *)NULL;
 
-      if (echo_command_at_execute)
-       {
-         char *line = string_list (words);
+  begin_unwind_frame ("simple-command");
 
-         if (line && *line)
-           fprintf (stderr, "%s%s\n", indirection_level_string (), line);
+  if (echo_command_at_execute)
+    xtrace_print_word_list (words);
 
-         FREE (line);
+  builtin = (Function *)NULL;
+  func = (SHELL_VAR *)NULL;
+  if ((simple_command->flags & CMD_NO_FUNCTIONS) == 0)
+    {
+      /* Posix.2 says special builtins are found before functions.  We
+        don't set builtin_is_special anywhere other than here, because
+        this path is followed only when the `command' builtin is *not*
+        being used, and we don't want to exit the shell if a special
+        builtin executed with `command builtin' fails.  `command' is not
+        a special builtin. */
+      if (posixly_correct)
+       {
+         builtin = find_special_builtin (words->word->word);
+         if (builtin)
+           builtin_is_special = 1;
        }
-
-      if (simple_command->flags & CMD_NO_FUNCTIONS)
-       func = (SHELL_VAR *)NULL;
-      else
+      if (builtin == 0)
        func = find_function (words->word->word);
+    }
 
-      add_unwind_protect (dispose_words, words);
-
-      QUIT;
+  add_unwind_protect (dispose_words, words);
+  QUIT;
 
-      /* Bind the last word in this command to "$_" after execution. */
-      for (lastword = words; lastword->next; lastword = lastword->next);
-      lastarg = lastword->word->word;
+  /* Bind the last word in this command to "$_" after execution. */
+  for (lastword = words; lastword->next; lastword = lastword->next)
+    ;
+  lastarg = lastword->word->word;
 
 #if defined (JOB_CONTROL)
-      /* Is this command a job control related thing? */
-      if (words->word->word[0] == '%')
-       {
-         int result;
-
-         if (async)
-           this_command_name = "bg";
-         else
-           this_command_name = "fg";
-
-         last_shell_builtin = this_shell_builtin;
-         this_shell_builtin = builtin_address (this_command_name);
-         result = (*this_shell_builtin) (words);
-         goto return_result;
-       }
-
-      /* 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. */
-      {
-       char *auto_resume_value = get_string_value ("auto_resume");
-
-       if (auto_resume_value &&
-           !first_word_quoted &&
-           !words->next &&
-           words->word->word[0] &&
-           !simple_command->redirects &&
-           pipe_in == NO_PIPE &&
-           pipe_out == NO_PIPE &&
-           !async)
-         {
-           char *word = words->word->word;
-           register int i;
-           int wl, cl, exact, substring, match, started_status;
-           register PROCESS *p;
-
-           exact = STREQ (auto_resume_value, "exact");
-           substring = STREQ (auto_resume_value, "substring");
-           wl = strlen (word);
-           for (i = job_slots - 1; i > -1; i--)
-             {
-               if (!jobs[i] || (JOBSTATE (i) != JSTOPPED))
-                 continue;
-
-               p = jobs[i]->pipe;
-               do
-                 {
-                   if (exact)
-                     {
-                       cl = strlen (p->command);
-                       match = STREQN (p->command, word, cl);
-                     }
-                   else if (substring)
-                     match = strindex (p->command, word) != (char *)0;
-                   else
-                     match = STREQN (p->command, word, wl);
-
-                   if (match == 0)
-                     {
-                       p = p->next;
-                       continue;
-                     }
-
-                   run_unwind_frame ("simple-command");
-                   last_shell_builtin = this_shell_builtin;
-                   this_shell_builtin = builtin_address ("fg");
-
-                   started_status = start_job (i, 1);
-
-                   if (started_status < 0)
-                     return (EXECUTION_FAILURE);
-                   else
-                     return (started_status);
-                 }
-               while (p != jobs[i]->pipe);
-             }
-         }
-      }
-#endif /* JOB_CONTROL */
-
-      /* Remember the name of this command globally. */
-      this_command_name = words->word->word;
-
-      QUIT;
-
-      /* This command could be a shell builtin or a user-defined function.
-        If so, and we have pipes, then fork a subshell in here.  Else, just
-        do the command. */
-
-      if (func)
-       builtin = (Function *)NULL;
-      else
-       builtin = find_shell_builtin (this_command_name);
-
+  /* Is this command a job control related thing? */
+  if (words->word->word[0] == '%')
+    {
+      this_command_name = async ? "bg" : "fg";
       last_shell_builtin = this_shell_builtin;
-      this_shell_builtin = builtin;
+      this_shell_builtin = builtin_address (this_command_name);
+      result = (*this_shell_builtin) (words);
+      goto return_result;
+    }
 
-      if (builtin || func)
+  /* 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 &&
+       !first_word_quoted &&
+       !words->next &&
+       words->word->word[0] &&
+       !simple_command->redirects &&
+       pipe_in == NO_PIPE &&
+       pipe_out == NO_PIPE &&
+       (temp = get_string_value ("auto_resume")))
+    {
+      char *word;
+      register int i;
+      int wl, cl, exact, substring, match, started_status;
+      register PROCESS *p;
+
+      word = words->word->word;
+      exact = STREQ (temp, "exact");
+      substring = STREQ (temp, "substring");
+      wl = strlen (word);
+      for (i = job_slots - 1; i > -1; i--)
        {
-         if ((pipe_in != NO_PIPE) || (pipe_out != NO_PIPE) || async)
+         if (jobs[i] == 0 || (JOBSTATE (i) != JSTOPPED))
+           continue;
+
+         p = jobs[i]->pipe;
+         do
            {
-             if (make_child (savestring (command_line), async) == 0)
+             if (exact)
                {
-                 /* Cancel traps, in trap.c. */
-                 restore_original_signals ();
-
-                 if (async)
-                   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);
+                 cl = strlen (p->command);
+                 match = STREQN (p->command, word, cl);
                }
+             else if (substring)
+               match = strindex (p->command, word) != (char *)0;
              else
+               match = STREQN (p->command, word, wl);
+
+             if (match == 0)
                {
-                 close_pipes (pipe_in, pipe_out);
-#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
-                 unlink_fifo_list ();
-#endif
-                 goto return_result;
+                 p = p->next;
+                 continue;
                }
-           }
-         else
-           {
-             result = execute_builtin_or_function
-               (words, builtin, func, simple_command->redirects, fds_to_close,
-                simple_command->flags);
 
-             goto return_result;
+             run_unwind_frame ("simple-command");
+             this_command_name = "fg";
+             last_shell_builtin = this_shell_builtin;
+             this_shell_builtin = builtin_address ("fg");
+
+             started_status = start_job (i, 1);
+             return ((started_status < 0) ? EXECUTION_FAILURE : started_status);
            }
+         while (p != jobs[i]->pipe);
        }
+    }
+#endif /* JOB_CONTROL */
 
-      execute_disk_command (words, simple_command->redirects, command_line,
-                           pipe_in, pipe_out, async, fds_to_close,
-                           (simple_command->flags & CMD_NO_FORK));
+  /* Remember the name of this command globally. */
+  this_command_name = words->word->word;
 
-      goto return_result;
-    }
-  else if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
+  QUIT;
+
+  /* This command could be a shell builtin or a user-defined function.
+     We have already found special builtins by this time, so we do not
+     set builtin_is_special.  If this is a function or builtin, and we
+     have pipes, then fork a subshell in here.  Otherwise, just execute
+     the command directly. */
+  if (func == 0 && builtin == 0)
+    builtin = find_shell_builtin (this_command_name);
+
+  last_shell_builtin = this_shell_builtin;
+  this_shell_builtin = builtin;
+
+  if (builtin || func)
     {
-      /* We have a null command, but we really want a subshell to take
-        care of it.  Just fork, do piping and redirections, and exit. */
-      if (make_child (savestring (""), async) == 0)
+      if ((pipe_in != NO_PIPE) || (pipe_out != NO_PIPE) || async)
        {
-         /* Cancel traps, in trap.c. */
-         restore_original_signals ();
-
-         do_piping (pipe_in, pipe_out);
+         if (make_child (command_line, async) == 0)
+           {
+             /* reset_terminating_signals (); */       /* XXX */
+             /* Cancel traps, in trap.c. */
+             restore_original_signals ();
 
-         subshell_environment = 1;
+             if (async)
+               setup_async_signals ();
 
-         if (do_redirections (simple_command->redirects, 1, 0, 0) == 0)
-           exit (EXECUTION_SUCCESS);
+             execute_subshell_builtin_or_function
+               (words, simple_command->redirects, builtin, func,
+                pipe_in, pipe_out, async, fds_to_close,
+                simple_command->flags);
+           }
          else
-           exit (EXECUTION_FAILURE);
+           {
+             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;
+           }
        }
       else
        {
-         close_pipes (pipe_in, pipe_out);
-#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
-         unlink_fifo_list ();
-#endif
-         result = EXECUTION_SUCCESS;
+         result = execute_builtin_or_function
+           (words, builtin, func, simple_command->redirects, fds_to_close,
+            simple_command->flags);
+         if (builtin)
+           {
+             if (result > EX_SHERRBASE)
+               {
+                 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
+                completes. */
+             if (posixly_correct && builtin_is_special && temporary_env)
+               merge_temporary_env ();
+           }
+         else          /* function */
+           {
+             if (result == EX_USAGE)
+               result = EX_BADUSAGE;
+             else if (result > EX_SHERRBASE)
+               result = EXECUTION_FAILURE;
+           }
+
          goto return_result;
        }
     }
-  else
-    {
-      /* Even if there aren't any command names, pretend to do the
-        redirections that are specified.  The user expects the side
-        effects to take place.  If the redirections fail, then return
-        failure.  Otherwise, if a command substitution took place while
-        expanding the command or a redirection, return the value of that
-        substitution.  Otherwise, return EXECUTION_SUCCESS. */
 
-      if (do_redirections (simple_command->redirects, 0, 0, 0) != 0)
-       result = EXECUTION_FAILURE;
-      else if (old_last_command_subst_pid != last_command_subst_pid)
-       result = last_command_exit_value;
-      else
-       result = EXECUTION_SUCCESS;
-    }
+  execute_disk_command (words, simple_command->redirects, command_line,
+                       pipe_in, pipe_out, async, fds_to_close,
+                       (simple_command->flags & CMD_NO_FORK));
 
  return_result:
   bind_lastarg (lastarg);
-  /* The unwind-protect frame is set up only if WORDS is not empty. */
-  if (words)
-    run_unwind_frame ("simple-command");
+  FREE (command_line);
+  run_unwind_frame ("simple-command");
   return (result);
 }
 
+/* Translate the special builtin exit statuses.  We don't really need a
+   function for this; it's a placeholder for future work. */
+static int
+builtin_status (result)
+     int result;
+{
+  int r;
+
+  switch (result)
+    {
+    case EX_USAGE:
+      r = EX_BADUSAGE;
+      break;
+    case EX_REDIRFAIL:
+    case EX_BADSYNTAX:
+    case EX_BADASSIGN:
+    case EX_EXPFAIL:
+      r = EXECUTION_FAILURE;
+      break;
+    default:
+      r = EXECUTION_SUCCESS;
+      break;
+    }
+  return (r);
+}
+
 static int
 execute_builtin (builtin, words, flags, subshell)
      Function *builtin;
      WORD_LIST *words;
      int flags, subshell;
 {
-  int old_e_flag = exit_immediately_on_error;
-  int result;
+  int old_e_flag, result;
 
+  old_e_flag = exit_immediately_on_error;
   /* The eval builtin calls parse_and_execute, which does not know about
      the setting of flags, and always calls the execution functions with
      flags that will exit the shell on an error if -e is set.  If the
@@ -1880,6 +2371,11 @@ execute_builtin (builtin, words, flags, subshell)
 
   if (subshell == 0 && builtin == source_builtin)
     {
+      /* In POSIX mode, if any variable assignments precede the `.' builtin,
+        they persist after the builtin completes, since `.' is a special
+        builtin. */
+      if (posixly_correct && builtin_env)
+       merge_builtin_env ();
       dispose_builtin_env ();
       discard_unwind_frame ("builtin_env");
     }
@@ -1893,8 +2389,6 @@ execute_builtin (builtin, words, flags, subshell)
   return (result);
 }
 
-/* XXX -- why do we need to set up unwind-protects for the case where
-   subshell == 1 at all? */
 static int
 execute_function (var, words, flags, fds_to_close, async, subshell)
      SHELL_VAR *var;
@@ -1904,36 +2398,44 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
 {
   int return_val, result;
   COMMAND *tc, *fc;
+  char *debug_trap;
 
   tc = (COMMAND *)copy_command (function_cell (var));
   if (tc && (flags & CMD_IGNORE_RETURN))
     tc->flags |= CMD_IGNORE_RETURN;
 
-  if (subshell)
-    begin_unwind_frame ("subshell_function_calling");
-  else
-    begin_unwind_frame ("function_calling");
-
   if (subshell == 0)
     {
+      begin_unwind_frame ("function_calling");
       push_context ();
       add_unwind_protect (pop_context, (char *)NULL);
       unwind_protect_int (line_number);
+      unwind_protect_int (return_catch_flag);
+      unwind_protect_jmp_buf (return_catch);
+      add_unwind_protect (dispose_command, (char *)tc);
+      unwind_protect_int (loop_level);
     }
-  else
-    unwind_protect_int (variable_context);
 
-  unwind_protect_int (loop_level);
-  unwind_protect_int (return_catch_flag);
-  unwind_protect_jmp_buf (return_catch);
-  add_unwind_protect (dispose_command, (char *)tc);
+  debug_trap = (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
+                       ? trap_list[DEBUG_TRAP]
+                       : (char *)NULL;
+  if (debug_trap)
+    {
+      if (subshell == 0)
+       {
+         debug_trap = savestring (debug_trap);
+         add_unwind_protect (set_debug_trap, debug_trap);
+       }
+      restore_default_signal (DEBUG_TRAP);
+    }
 
   /* The temporary environment for a function is supposed to apply to
      all commands executed within the function body. */
   if (temporary_env)
     {
       function_env = copy_array (temporary_env);
-      add_unwind_protect (dispose_function_env, (char *)NULL);
+      if (subshell == 0)
+       add_unwind_protect (dispose_function_env, (char *)NULL);
       dispose_used_env_vars ();
     }
 #if 0
@@ -1941,10 +2443,9 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
     function_env = (char **)NULL;
 #endif
 
-  /* Note the second argument of "1", meaning that we discard
-     the current value of "$*"!  This is apparently the right thing. */
   remember_args (words->next, 1);
 
+  /* Number of the line on which the function body starts. */
   line_number = function_line_number = tc->line;
 
   if (subshell)
@@ -1952,10 +2453,7 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
 #if defined (JOB_CONTROL)
       stop_pipeline (async, (COMMAND *)NULL);
 #endif
-      if (tc->type == cm_group)
-       fc = tc->value.Group->command;
-      else
-       fc = tc;
+      fc = (tc->type == cm_group) ? tc->value.Group->command : tc;
 
       if (fc && (flags & CMD_IGNORE_RETURN))
        fc->flags |= CMD_IGNORE_RETURN;
@@ -1973,9 +2471,7 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
   else
     result = execute_command_internal (fc, 0, NO_PIPE, NO_PIPE, fds_to_close);
 
-  if (subshell)
-    run_unwind_frame ("subshell_function_calling");
-  else
+  if (subshell == 0)
     run_unwind_frame ("function_calling");
 
   return (result);
@@ -1999,12 +2495,14 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
      struct fd_bitmap *fds_to_close;
      int flags;
 {
+  int result, r;
+
   /* A subshell is neither a login shell nor interactive. */
   login_shell = interactive = 0;
 
-  subshell_environment = 1;
+  subshell_environment = SUBSHELL_ASYNC;
 
-  maybe_make_export_env ();
+  maybe_make_export_env ();    /* XXX - is this needed? */
 
 #if defined (JOB_CONTROL)
   /* Eradicate all traces of job control after we fork the subshell, so
@@ -2033,8 +2531,6 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
 
   if (builtin)
     {
-      int result;
-
       /* Give builtins a place to jump back to on failure,
         so we don't go back up to main(). */
       result = setjmp (top_level);
@@ -2044,12 +2540,15 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
       else if (result)
        exit (EXECUTION_FAILURE);
       else
-       exit (execute_builtin (builtin, words, flags, 1));
+        {
+          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));
-    }
+    exit (execute_function (var, words, flags, fds_to_close, async, 1));
 }
 
 /* Execute a builtin or function in the current shell context.  If BUILTIN
@@ -2070,7 +2569,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
      struct fd_bitmap *fds_to_close;
      int flags;
 {
-  int result = EXECUTION_FAILURE;
+  int result;
   REDIRECT *saved_undo_list;
 
   if (do_redirections (redirects, 1, 1, 0) != 0)
@@ -2078,7 +2577,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
       cleanup_redirects (redirection_undo_list);
       redirection_undo_list = (REDIRECT *)NULL;
       dispose_exec_redirects ();
-      return (EXECUTION_FAILURE);
+      return (EX_REDIRFAIL);   /* was EXECUTION_FAILURE */
     }
 
   saved_undo_list = redirection_undo_list;
@@ -2096,8 +2595,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
   if (saved_undo_list)
     {
       begin_unwind_frame ("saved redirects");
-      add_unwind_protect (cleanup_func_redirects, (char *)saved_undo_list);
-      add_unwind_protect (dispose_redirects, (char *)saved_undo_list);
+      add_unwind_protect (cleanup_redirects, (char *)saved_undo_list);
     }
 
   redirection_undo_list = (REDIRECT *)NULL;
@@ -2115,8 +2613,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
 
   if (redirection_undo_list)
     {
-      do_redirections (redirection_undo_list, 1, 0, 0);
-      dispose_redirects (redirection_undo_list);
+      cleanup_redirects (redirection_undo_list);
       redirection_undo_list = (REDIRECT *)NULL;
     }
 
@@ -2163,72 +2660,29 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
      struct fd_bitmap *fds_to_close;
      int nofork;       /* Don't fork, just exec, if no pipes */
 {
-  register char *pathname;
-  char *hashed_file, *command, **args;
-  int pid, temp_path;
-  SHELL_VAR *path;
+  char *pathname, *command, **args;
+  int pid;
 
   pathname = words->word->word;
+
 #if defined (RESTRICTED_SHELL)
   if (restricted && strchr (pathname, '/'))
     {
-      report_error ("%s: restricted: cannot specify `/' in command names",
+      internal_error ("%s: restricted: cannot specify `/' in command names",
                    pathname);
       last_command_exit_value = EXECUTION_FAILURE;
       return;
     }
 #endif /* RESTRICTED_SHELL */
 
-  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. */
-  temp_path = 0;
-  path = find_tempenv_variable ("PATH");
-  if (path)
-    temp_path = 1;
-
-  /* Don't waste time trying to find hashed data for a pathname
-     that is already completely specified. */
-
-  if (!path && !absolute_program (pathname))
-    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.  It seems pretty stupid to me,
-     so I am basing it on the presence of POSIXLY_CORRECT. */
-
-  if (hashed_file && posixly_correct)
-    {
-      int st;
-
-      st = file_status (hashed_file);
-      if ((st ^ (FS_EXISTS | FS_EXECABLE)) != 0)
-       {
-         remove_hashed_filename (pathname);
-         hashed_file = (char *)NULL;
-       }
-    }
+  command = search_for_command (pathname);
 
-  if (hashed_file)
-    command = savestring (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 (command)
     {
-      command = find_user_command (pathname);
-      if (command && !hashing_disabled && !temp_path)
-       remember_filename (pathname, command, dot_found_in_search, 1);
+      maybe_make_export_env ();
+      put_command_name_into_env (command);
     }
 
-  maybe_make_export_env ();
-
-  if (command)
-    put_command_name_into_env (command);
-
   /* We have to make the child before we check for the non-existance
      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,
@@ -2242,6 +2696,11 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
     {
       int old_interactive;
 
+#if !defined (ARG_MAX) || ARG_MAX >= 10240
+      if (posixly_correct == 0)
+       put_gnu_argv_flags_into_env ((int)getpid (), glob_argv_flags);
+#endif
+
       /* Cancel traps, in trap.c. */
       restore_original_signals ();
 
@@ -2253,23 +2712,18 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
 
       do_piping (pipe_in, pipe_out);
 
-      /* Execve expects the command name to be in args[0].  So we
-        leave it there, in the same format that the user used to
-        type it in. */
-      args = make_word_array (words);
-
       if (async)
        {
          old_interactive = interactive;
          interactive = 0;
        }
 
-      subshell_environment = 1;
+      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 
+        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);
@@ -2287,12 +2741,16 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
       if (async)
        interactive = old_interactive;
 
-      if (!command)
+      if (command == 0)
        {
-         report_error ("%s: command not found", args[0]);
+         internal_error ("%s: command not found", pathname);
          exit (EX_NOTFOUND);   /* Posix.2 says the exit status is 127 */
        }
 
+      /* Execve expects the command name to be in args[0].  So we
+        leave it there, in the same format that the user used to
+        type it in. */
+      args = word_list_to_argv (words, 0, 0, (int *)NULL);
       exit (shell_execve (command, args, export_env));
     }
   else
@@ -2306,6 +2764,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
     }
 }
 
+#if !defined (HAVE_HASH_BANG_EXEC)
 /* If the operating system on which we're running does not handle
    the #! executable format, then help out.  SAMPLE is the text read
    from the file, SAMPLE_LEN characters.  COMMAND is the name of
@@ -2335,9 +2794,10 @@ execute_shell_script (sample, sample_len, command, args, env)
        i++)
     ;
 
-  execname = xmalloc (1 + (i - start));
-  strncpy (execname, (char *) (sample + start), i - start);
-  execname[i - start] = '\0';
+  larry = i - start;
+  execname = xmalloc (1 + larry);
+  strncpy (execname, (char *)(sample + start), larry);
+  execname[larry] = '\0';
   size_increment = 1;
 
   /* Now the argument, if any. */
@@ -2355,9 +2815,10 @@ execute_shell_script (sample, sample_len, command, args, env)
           !whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
           i++)
        ;
-      firstarg = xmalloc (1 + (i - start));
-      strncpy (firstarg, (char *)(sample + start), i - start);
-      firstarg[i - start] = '\0';
+      larry = i - start;
+      firstarg = xmalloc (1 + larry);
+      strncpy (firstarg, (char *)(sample + start), larry);
+      firstarg[larry] = '\0';
 
       size_increment = 2;
     }
@@ -2382,6 +2843,13 @@ execute_shell_script (sample, sample_len, command, args, env)
 
   return (shell_execve (execname, args, env));
 }
+#endif /* !HAVE_HASH_BANG_EXEC */
+
+#if defined (HAVE_SETOSTYPE) && defined (_POSIX_SOURCE)
+#  define SETOSTYPE(x) __setostype(x)
+#else
+#  define SETOSTYPE(x)
+#endif
 
 /* Call execve (), handling interpreting shell scripts, and handling
    exec failures. */
@@ -2390,130 +2858,170 @@ shell_execve (command, args, env)
      char *command;
      char **args, **env;
 {
-#if defined (isc386) && defined (_POSIX_SOURCE)
-  __setostype (0);             /* Turn on USGr3 semantics. */
-  execve (command, args, env);
-  __setostype (1);             /* Turn the POSIX semantics back on. */
-#else
+  struct stat finfo;
+  int larray, i, fd;
+
+  SETOSTYPE (0);               /* Some systems use for USG/POSIX semantics */
   execve (command, args, env);
-#endif /* !(isc386 && _POSIX_SOURCE) */
+  SETOSTYPE (1);
 
   /* If we get to this point, then start checking out the file.
      Maybe it is something we can hack ourselves. */
-  {
-    struct stat finfo;
-
-    if (errno != ENOEXEC)
-      {
-       if ((stat (command, &finfo) == 0) &&
-           (S_ISDIR (finfo.st_mode)))
-         report_error ("%s: is a directory", args[0]);
-       else
+  if (errno != ENOEXEC)
+    {
+      i = errno;
+      if ((stat (command, &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
+       internal_error ("%s: is a directory", command);
+      else
+       {
+         errno = i;
          file_error (command);
+       }
+      return (EX_NOEXEC);      /* XXX Posix.2 says that exit status is 126 */
+    }
 
-       return (EX_NOEXEC);     /* XXX Posix.2 says that exit status is 126 */
-      }
-    else
-      {
-       /* 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 30 characters) are in the ASCII set.
-          Execute the contents as shell commands. */
-       int larray = array_len (args) + 1;
-       int i, should_exec = 0;
-
+  /* 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 (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)
        {
-         int fd = open (command, O_RDONLY);
-         if (fd != -1)
+#if !defined (HAVE_HASH_BANG_EXEC)
+         if (sample[0] == '#' && sample[1] == '!')
+           return (execute_shell_script (sample, sample_len, command, args, env));
+         else
+#endif
+         if (check_binary_file (sample, sample_len))
            {
-             unsigned char sample[80];
-             int sample_len = read (fd, &sample[0], 80);
-
-             close (fd);
-
-             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 && sample[0] == '#' && sample[1] == '!')
-               return (execute_shell_script
-                       (sample, sample_len, command, args, env));
-             else if ((sample_len != -1) &&
-                      check_binary_file (sample, sample_len))
-               {
-                 report_error ("%s: cannot execute binary file", command);
-                 return (EX_BINARY_FILE);
-               }
+             internal_error ("%s: cannot execute binary file", command);
+             return (EX_BINARY_FILE);
            }
        }
-#if defined (JOB_CONTROL)
-       /* Forget about the way that job control was working. We are
-          in a subshell. */
-       without_job_control ();
-#endif /* JOB_CONTROL */
+    }
+
+  larray = array_len (args) + 1;
+
 #if defined (ALIAS)
-       /* Forget about any aliases that we knew of.  We are in a subshell. */
-       delete_all_aliases ();
+  /* Forget about any aliases that we knew of.  We are in a subshell. */
+  delete_all_aliases ();
 #endif /* ALIAS */
 
+#if defined (HISTORY)
+  /* Forget about the history lines we have read.  This is a non-interactive
+     subshell. */
+  history_lines_this_session = 0;
+#endif
+
 #if defined (JOB_CONTROL)
-       set_sigchld_handler ();
+  /* Forget about the way job control was working. We are in a subshell. */
+  without_job_control ();
+  set_sigchld_handler ();
 #endif /* JOB_CONTROL */
-       set_sigint_handler ();
 
-       /* Insert the name of this shell into the argument list. */
-       args = (char **)xrealloc ((char *)args, (1 + larray) * sizeof (char *));
+  /* 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
 
-       for (i = larray - 1; i; i--)
-         args[i] = args[i - 1];
+  set_sigint_handler ();
 
-       args[0] = shell_name;
-       args[1] = command;
-       args[larray] = (char *)NULL;
+  /* Insert the name of this shell into the argument list. */
+  args = (char **)xrealloc ((char *)args, (1 + larray) * sizeof (char *));
 
-       if (args[0][0] == '-')
-         args[0]++;
+  for (i = larray - 1; i; i--)
+    args[i] = args[i - 1];
 
-       if (should_exec)
-         {
-           struct stat finfo;
+  args[0] = shell_name;
+  args[1] = command;
+  args[larray] = (char *)NULL;
 
-#if defined (isc386) && defined (_POSIX_SOURCE)
-           __setostype (0);    /* Turn on USGr3 semantics. */
-           execve (shell_name, args, env);
-           __setostype (1);    /* Turn the POSIX semantics back on. */
-#else
-           execve (shell_name, args, env);
-#endif /* isc386 && _POSIX_SOURCE */
+  if (args[0][0] == '-')
+    args[0]++;
 
-           /* Oh, no!  We couldn't even exec this! */
-           if ((stat (args[0], &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
-             report_error ("%s: is a directory", args[0]);
-           else
-             file_error (args[0]);
+#if defined (RESTRICTED_SHELL)
+  if (restricted)
+    change_flag ('r', FLAG_OFF);
+#endif
 
-           return (EXECUTION_FAILURE);
-         }
-       else
-         {
-           subshell_argc = larray;
-           subshell_argv = args;
-           subshell_envp = env;
-           longjmp (subshell_top_level, 1);
-         }
-      }
-  }
+  if (subshell_argv)
+    {
+      /* Can't free subshell_argv[0]; that is shell_name. */
+      for (i = 1; i < subshell_argc; i++)
+       free (subshell_argv[i]);
+      free (subshell_argv);
+    }
+
+  dispose_command (currently_executing_command);       /* XXX */
+  currently_executing_command = (COMMAND *)NULL;
+
+  subshell_argc = larray;
+  subshell_argv = args;
+  subshell_envp = env;
+
+  unbind_args ();      /* remove the positional parameters */
+
+  longjmp (subshell_top_level, 1);
+}
+
+static int
+execute_intern_function (name, function)
+     WORD_DESC *name;
+     COMMAND *function;
+{
+  SHELL_VAR *var;
+
+  if (check_identifier (name, posixly_correct) == 0)
+    {
+      if (posixly_correct && interactive_shell == 0)
+       {
+         last_command_exit_value = EX_USAGE;
+         jump_to_top_level (EXITPROG);
+       }
+      return (EXECUTION_FAILURE);
+    }
+
+  var = find_function (name->word);
+  if (var && readonly_p (var))
+    {
+      internal_error ("%s: readonly function", var->name);
+      return (EXECUTION_FAILURE);
+    }
+
+  bind_function (name->word, function);
+  return (EXECUTION_SUCCESS);
 }
 
 #if defined (PROCESS_SUBSTITUTION)
-/* Currently unused */
 void
 close_all_files ()
 {
@@ -2547,8 +3055,7 @@ do_piping (pipe_in, pipe_out)
   if (pipe_in != NO_PIPE)
     {
       if (dup2 (pipe_in, 0) < 0)
-       internal_error ("cannot duplicate fd %d to fd 0: %s",
-                       pipe_in, strerror (errno));
+       sys_error ("cannot duplicate fd %d to fd 0", pipe_in);
       if (pipe_in > 0)
         close (pipe_in);
     }
@@ -2557,19 +3064,64 @@ do_piping (pipe_in, pipe_out)
       if (pipe_out != REDIRECT_BOTH)
        {
          if (dup2 (pipe_out, 1) < 0)
-           internal_error ("cannot duplicate fd %d to fd 1: %s",
-                           pipe_out, strerror (errno));
+           sys_error ("cannot duplicate fd %d to fd 1", pipe_out);
          if (pipe_out == 0 || pipe_out > 1)
            close (pipe_out);
        }
       else
-       dup2 (1, 2);
+       if (dup2 (1, 2) < 0)
+         sys_error ("cannot duplicate fd 1 to fd 2");
     }
 }
 
-#define AMBIGUOUS_REDIRECT  -1
-#define NOCLOBBER_REDIRECT  -2
-#define RESTRICTED_REDIRECT -3 /* Only can happen in restricted shells. */
+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 */
+
+    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
@@ -2582,8 +3134,8 @@ do_redirections (list, for_real, internal, set_clexec)
      REDIRECT *list;
      int for_real, internal, set_clexec;
 {
-  register int error;
-  register REDIRECT *temp = list;
+  int error;
+  REDIRECT *temp;
 
   if (internal)
     {
@@ -2596,54 +3148,14 @@ do_redirections (list, for_real, internal, set_clexec)
        dispose_exec_redirects ();
     }
 
-  while (temp)
+  for (temp = list; temp; temp = temp->next)
     {
       error = do_redirection_internal (temp, for_real, internal, set_clexec);
-
       if (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)
-               filename = savestring ("");
-           }
-         else
-           filename = itos (temp->redirectee.dest);
-
-         switch (error)
-           {
-           case AMBIGUOUS_REDIRECT:
-             report_error ("%s: Ambiguous redirect", filename);
-             break;
-
-           case NOCLOBBER_REDIRECT:
-             report_error ("%s: Cannot clobber existing file", filename);
-             break;
-
-#if defined (RESTRICTED_SHELL)
-           case RESTRICTED_REDIRECT:
-             report_error ("%s: output redirection restricted", filename);
-             break;
-#endif /* RESTRICTED_SHELL */
-
-           default:
-             report_error ("%s: %s", filename, strerror (error));
-             break;
-           }
-
-         free (filename);
+         redirection_error (temp, error);
          return (error);
        }
-
-      temp = temp->next;
     }
   return (0);
 }
@@ -2654,8 +3166,6 @@ static int
 expandable_redirection_filename (redirect)
      REDIRECT *redirect;
 {
-  int result;
-
   switch (redirect->instruction)
     {
     case r_output_direction:
@@ -2667,15 +3177,13 @@ expandable_redirection_filename (redirect)
     case r_output_force:
     case r_duplicating_input_word:
     case r_duplicating_output_word:
-      result = 1;
-      break;
+      return 1;
 
     default:
-      result = 0;
+      return 0;
     }
-  return (result);
 }
-\f
+
 /* Expand the word in WORD returning a string.  If WORD expands to
    multiple words (or no words), then return NULL. */
 char *
@@ -2689,17 +3197,86 @@ redirection_expand (word)
   tlist2 = expand_words_no_vars (tlist1);
   dispose_words (tlist1);
 
-  if (!tlist2 || tlist2->next)
+  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);
+}
+
+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)
     {
-      /* 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);
+      /* 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);
     }
-  result = string_list (tlist2);
-  dispose_words (tlist2);
-  return (result);
+  return 0;
 }
 
 /* Do the specific redirection requested.  Returns errno in case of error.
@@ -2712,12 +3289,17 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
      REDIRECT *redirect;
      int for_real, remembering, set_clexec;
 {
-  WORD_DESC *redirectee = redirect->redirectee.filename;
-  int redir_fd = redirect->redirectee.dest;
-  int fd, redirector = redirect->redirector;
+  WORD_DESC *redirectee;
+  int redir_fd, fd, redirector, r;
   char *redirectee_word;
-  enum r_instruction ri = redirect->instruction;
+  enum r_instruction ri;
   REDIRECT *new_redirect;
+  struct stat finfo;
+
+  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)
     {
@@ -2725,7 +3307,9 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
         the redirection into a new one and continue. */
       redirectee_word = redirection_expand (redirectee);
 
-      if (redirectee_word[0] == '-' && redirectee_word[1] == '\0')
+      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);
@@ -2745,9 +3329,9 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
        }
       else if (ri == r_duplicating_output_word && redirector == 1)
        {
-         if (!posixly_correct)
+         if (posixly_correct == 0)
            {
-             rd.filename = make_word (redirectee_word);
+             rd.filename = make_bare_word (redirectee_word);
              new_redirect = make_redirection (1, r_err_and_out, rd);
            }
          else
@@ -2800,22 +3384,17 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
     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)
+
+      if (redirectee_word == 0)
        return (AMBIGUOUS_REDIRECT);
 
 #if defined (RESTRICTED_SHELL)
-      if (restricted && (ri == r_output_direction ||
-                        ri == r_input_output ||
-                        ri == r_err_and_out ||
-                        ri == r_appending_to ||
-                        ri == r_output_force))
+      if (restricted && (WRITE_REDIRECT (ri)))
        {
          free (redirectee_word);
          return (RESTRICTED_REDIRECT);
@@ -2824,16 +3403,11 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
 
       /* If we are in noclobber mode, you are not allowed to overwrite
         existing files.  Check first. */
-      if (noclobber && (ri == r_output_direction ||
-                     ri == r_input_output ||
-                     ri == r_err_and_out))
+      if (noclobber && OUTPUT_REDIRECT (ri))
        {
-         struct stat finfo;
-         int stat_result;
+         r = stat (redirectee_word, &finfo);
 
-         stat_result = stat (redirectee_word, &finfo);
-
-         if ((stat_result == 0) && (S_ISREG (finfo.st_mode)))
+         if (r == 0 && (S_ISREG (finfo.st_mode)))
            {
              free (redirectee_word);
              return (NOCLOBBER_REDIRECT);
@@ -2841,12 +3415,12 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
 
          /* If the file was not present, make sure we open it exclusively
             so that if it is created before we open it, our open will fail. */
-         if (stat_result != 0)
+         if (r != 0)
            redirect->flags |= O_EXCL;
 
          fd = open (redirectee_word, redirect->flags, 0666);
 
-         if ((fd < 0) && (errno == EEXIST))
+         if (fd < 0 && errno == EEXIST)
            {
              free (redirectee_word);
              return (NOCLOBBER_REDIRECT);
@@ -2855,10 +3429,10 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
       else
        {
          fd = open (redirectee_word, redirect->flags, 0666);
-#if defined (AFS_CREATE_BUG)
+#if defined (AFS)
          if ((fd < 0) && (errno == EACCES))
-           fd = open (redirectee_word, (redirect->flags & ~O_CREAT), 0666);
-#endif /* AFS_CREATE_BUG */
+           fd = open (redirectee_word, redirect->flags & ~O_CREAT, 0666);
+#endif /* AFS */
        }
       free (redirectee_word);
 
@@ -2906,8 +3480,7 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
       if (fd != redirector)
        {
 #if defined (BUFFERED_INPUT)
-         if (ri == r_input_direction || ri == r_inputa_direction ||
-             ri == r_input_output)
+         if (INPUT_REDIRECT (ri))
            close_buffered_fd (fd);
          else
 #endif /* !BUFFERED_INPUT */
@@ -2934,97 +3507,34 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
         the new input.  Place it in a temporary file. */
       if (redirectee)
        {
-         char filename[40];
-         pid_t pid = getpid ();
+         char filename[24];
 
          /* Make the filename for the temp file. */
-         sprintf (filename, "/tmp/t%d-sh", pid);
+         sprintf (filename, "/tmp/t%d-sh", (int)getpid ());
 
          fd = open (filename, O_TRUNC | O_WRONLY | O_CREAT, 0666);
          if (fd < 0)
            return (errno);
 
-         errno = 0;            /* XXX */
+         errno = r = 0;                /* XXX */
          if (redirectee->word)
-           {
-             char *document;
-             int document_len;
-
-             /* 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->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;
-                     close (fd);
-                     return (errno);
-                   }
-               }
-             else
-               {
-                 WORD_LIST *tlist;
-                 tlist = expand_string (redirectee->word, Q_HERE_DOCUMENT);
-                 if (tlist)
-                   {
-                     int fd2;
-                     FILE *fp;
-                     register WORD_LIST *t;
-
-                     /* 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);
-                         close (fd);
-                         return (errno);
-                       }
-                     errno = 0;        /* XXX */
-                     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;
-                             break;
-                           }
-                       }
-                     fclose (fp);
-                     dispose_words (tlist);
-                   }
-               }
-           }     
+           r = write_here_document (fd, redirectee);
 
          close (fd);
-         if (errno)
-           return (errno);
+         if (r)
+           return (r);
 
          /* Make the document really temporary.  Also make it the input. */
          fd = open (filename, O_RDONLY, 0666);
 
-         if (unlink (filename) < 0 || fd < 0)
+         if (fd < 0)
+           return (errno);
+
+         if (unlink (filename) < 0)
            {
-             if (fd >= 0)
-               close (fd);
-             return (errno);
+             r = errno;
+             close (fd);
+             return (r);
            }
 
          if (for_real)
@@ -3039,10 +3549,11 @@ do_redirection_internal (redirect, for_real, remembering, set_clexec)
 #if defined (BUFFERED_INPUT)
              check_bash_input (redirector);
 #endif
-             if (dup2 (fd, redirector) < 0)
+             if (fd != redirector && dup2 (fd, redirector) < 0)
                {
+                 r = errno;
                  close (fd);
-                 return (errno);
+                 return (r);
                }
 
 #if defined (BUFFERED_INPUT)
@@ -3127,47 +3638,44 @@ add_undo_redirect (fd)
      int fd;
 {
   int new_fd, clexec_flag;
-  REDIRECT *new_redirect, *closer;
+  REDIRECT *new_redirect, *closer, *dummy_redirect;
 
   new_fd = fcntl (fd, F_DUPFD, SHELL_FD_BASE);
 
   if (new_fd < 0)
     {
-      file_error ("redirection error");
+      sys_error ("redirection error");
       return (-1);
     }
-  else
-    {
-      REDIRECT *dummy_redirect;
-
-      clexec_flag = fcntl (fd, F_GETFD, 0);
 
-      rd.dest = 0L;
-      closer = make_redirection (new_fd, r_close_this, rd);
-      dummy_redirect = copy_redirects (closer);
+  clexec_flag = fcntl (fd, F_GETFD, 0);
 
-      rd.dest = (long)new_fd;
-      new_redirect = make_redirection (fd, r_duplicating_output, rd);
-      new_redirect->next = closer;
+  rd.dest = 0L;
+  closer = make_redirection (new_fd, r_close_this, rd);
+  dummy_redirect = copy_redirects (closer);
 
-      closer->next = redirection_undo_list;
-      redirection_undo_list = new_redirect;
+  rd.dest = (long)new_fd;
+  new_redirect = make_redirection (fd, r_duplicating_output, rd);
+  new_redirect->next = closer;
 
-      /* Save redirections that need to be undone even if the undo list
-        is thrown away by the `exec' builtin. */
-      add_exec_redirect (dummy_redirect);
+  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);
 
-      /* 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);
 }
 
@@ -3193,26 +3701,6 @@ add_exec_redirect (dummy_redirect)
   exec_redirection_undo_list = dummy_redirect;
 }
 
-intern_function (name, function)
-     WORD_DESC *name;
-     COMMAND *function;
-{
-  SHELL_VAR *var;
-
-  if (!check_identifier (name, posixly_correct))
-    return (EXECUTION_FAILURE);
-
-  var = find_function (name->word);
-  if (var && readonly_p (var))
-    {
-      report_error ("%s: readonly function", var->name);
-      return (EXECUTION_FAILURE);
-    }
-
-  bind_function (name->word, function);
-  return (EXECUTION_SUCCESS);
-}
-
 #define u_mode_bits(x) (((x) & 0000700) >> 6)
 #define g_mode_bits(x) (((x) & 0000070) >> 3)
 #define o_mode_bits(x) (((x) & 0000007) >> 0)
@@ -3236,7 +3724,7 @@ file_status (name)
   /* 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);
+    return (FS_EXISTS|FS_DIRECTORY);
 
 #if defined (AFS)
   /* We have to use access(2) to determine access because AFS does not
@@ -3280,8 +3768,8 @@ file_status (name)
      since we are also `others'. */
   if (X_BIT (o_mode_bits (finfo.st_mode)))
     return (FS_EXISTS | FS_EXECABLE);
-  else
-    return (FS_EXISTS);
+
+  return (FS_EXISTS);
 #endif /* !AFS */
 }
 
@@ -3293,7 +3781,17 @@ int
 executable_file (file)
      char *file;
 {
-  return (file_status (file) & FS_EXECABLE);
+  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 ()
@@ -3311,7 +3809,7 @@ char *
 find_user_command (name)
      char *name;
 {
-  return (find_user_command_internal (name, FS_EXEC_PREFERRED));
+  return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
 }
 
 /* Locate the file referenced by NAME, searching along the contents
@@ -3326,7 +3824,7 @@ find_path_file (name)
 }
 
 static char *
-find_user_command_internal (name, flags)
+_find_user_command_internal (name, flags)
      char *name;
      int flags;
 {
@@ -3335,7 +3833,7 @@ find_user_command_internal (name, flags)
 
   /* 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))
+  if (var = find_variable_internal ("PATH", 1))        /* XXX could be array? */
     path_list = value_cell (var);
   else
     path_list = (char *)NULL;
@@ -3346,6 +3844,27 @@ find_user_command_internal (name, flags)
   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.
@@ -3372,60 +3891,130 @@ get_next_path_element (path_list, path_index_pointer)
 }
 
 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);
+         hashed_file = (char *)NULL;
+       }
+    }
+
+  if (hashed_file)
+    command = savestring (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;
-  char *path_list;
-  int  path_index;
-  char *path_element;
-  char *match;
+  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)
+  if (state == 0)
     {
       /* Create the list of matches. */
-      if (!match_list)
+      if (match_list == 0)
        {
-         match_list =
-           (char **) xmalloc ((match_list_size = 5) * sizeof(char *));
-
-         for (i = 0; i < match_list_size; i++)
-           match_list[i] = 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] = NULL;
+       match_list[i] = 0;
 
       /* We haven't found any files yet. */
       match_index = 0;
 
-      path_list = get_string_value ("PATH");
-      path_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)
+         if (path_element == 0)
            break;
 
-         match = find_user_command_in_path (name, path_element, flags);
+         match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
 
          free (path_element);
 
-         if (!match)
+         if (match == 0)
            continue;
 
          if (match_index + 1 == match_list_size)
-           match_list = (char **)xrealloc
-             (match_list, ((match_list_size += 10) + 1) * sizeof (char *));
+           {
+             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. */
@@ -3440,33 +4029,6 @@ user_command_matches (name, flags, state)
   return (match);
 }
 
-/* Return 1 if PATH1 and PATH2 are the same file.  This is kind of
-   expensive.  If non-NULL STP1 and STP2 point to stat structures
-   corresponding to PATH1 and PATH2, respectively. */
-int
-same_file (path1, path2, stp1, stp2)
-     char *path1, *path2;
-     struct stat *stp1, *stp2;
-{
-  struct stat st1, st2;
-
-  if (stp1 == NULL)
-    {
-      if (stat (path1, &st1) != 0)
-       return (0);
-      stp1 = &st1;
-    }
-
-  if (stp2 == NULL)
-    {
-      if (stat (path2, &st2) != 0)
-       return (0);
-      stp2 = &st2;
-    }
-
-  return ((stp1->st_dev == stp2->st_dev) && (stp1->st_ino == stp2->st_ino));
-}
-
 /* Turn PATH, a directory, and NAME, a filename, into a full pathname.
    This allocates new memory and returns it. */
 static char *
@@ -3484,8 +4046,92 @@ make_full_pathname (path, name, name_len)
   strcpy (full_path + path_len + 1, name);
   return (full_path);
 }
-  
-/* This does the dirty work for find_path_file () and find_user_command ().
+
+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.
@@ -3494,6 +4140,7 @@ make_full_pathname (path, name, name_len)
       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)
@@ -3501,18 +4148,9 @@ find_user_command_in_path (name, path_list, flags)
      char *path_list;
      int flags;
 {
-  char *full_path, *path, *file_to_lose_on;
-  int status, path_index, name_len;
-  struct stat finfo;
-
-  name_len = strlen (name);
-
-  /* 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. */
-  file_to_lose_on = (char *)NULL;
+  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. */
@@ -3520,94 +4158,45 @@ find_user_command_in_path (name, path_list, flags)
 
   if (absolute_program (name))
     {
-      full_path = xmalloc (1 + name_len);
-      strcpy (full_path, name);
-
-      status = file_status (full_path);
-
-      /* If the file doesn't exist, quit now. */
-      if (!(status & FS_EXISTS))
-       {
-         free (full_path);
-         return ((char *)NULL);
-       }
-
-      /* If we only care about whether the file exists or not, return
-        this filename. */
-      if (flags & FS_EXISTS)
-       return (full_path);
-
-      /* Otherwise, maybe we care about whether this file is executable.
-        If it is, and that is what we want, return it. */
-      if ((flags & FS_EXEC_ONLY) && (status & FS_EXECABLE))
-       return (full_path);
-      else
-       {
-         free (full_path);
-         return ((char *)NULL);
-       }
+      full_path = find_absolute_program (name, flags);
+      return (full_path);
     }
 
-  /* Find out the location of the current working directory. */
-  stat (".", &finfo);
+  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_list[path_index])
+
+  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)
+      if (path == 0)
        break;
 
-      if (*path == '~')
-       {
-         char *t = tilde_expand (path);
-         free (path);
-         path = t;
-       }
-
-      /* 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 && (*path == '.') &&
-         same_file (".", path, &finfo, (struct stat *)NULL))
-       dot_found_in_search = 1;
-
-      full_path = make_full_pathname (path, name, name_len);
+      /* 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);
 
-      status = file_status (full_path);
-
-      if (!(status & FS_EXISTS))
-       goto next_file;
-
-      /* 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)
+      /* 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 (file_to_lose_on);
-
-         return (full_path);
+         free (full_path);
+         continue;
        }
 
-      /* 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)
+      if (full_path)
        {
-         if (!file_to_lose_on)
-           file_to_lose_on = savestring (full_path);
+         FREE (file_to_lose_on);
+         return (full_path);
        }
-
-    next_file:
-      free (full_path);
     }
 
   /* We didn't find exactly what the user was looking for.  Return
@@ -3616,83 +4205,3 @@ find_user_command_in_path (name, path_list, flags)
      search would accept a non-executable as a last resort. */
   return (file_to_lose_on);
 }
-
-/* Given a string containing units of information separated by colons,
-   return the next one pointed to by (P_INDEX), or NULL if there are no more.
-   Advance (P_INDEX) to the character after the colon. */
-char *
-extract_colon_unit (string, p_index)
-     char *string;
-     int *p_index;
-{
-  int i, start;
-
-  i = *p_index;
-
-  if (!string || (i >= (int)strlen (string)))
-    return ((char *)NULL);
-
-  /* Each call to this routine leaves the index pointing at a colon if
-     there is more to the path.  If I is > 0, then increment past the
-     `:'.  If I is 0, then the path has a leading colon.  Trailing colons
-     are handled OK by the `else' part of the if statement; an empty
-     string is returned in that case. */
-  if (i && string[i] == ':')
-    i++;
-
-  start = i;
-
-  while (string[i] && string[i] != ':') i++;
-
-  *p_index = i;
-
-  if (i == start)
-    {
-      if (string[i])
-       (*p_index)++;
-
-      /* Return "" in the case of a trailing `:'. */
-      return (savestring (""));
-    }
-  else
-    {
-      char *value;
-
-      value = xmalloc (1 + i - start);
-      strncpy (value, string + start, i - start);
-      value [i - start] = '\0';
-
-      return (value);
-    }
-}
-
-/* Return non-zero if the characters from SAMPLE are not all valid
-   characters to be found in the first line of a shell script.  We
-   check up to the first newline, or SAMPLE_LEN, whichever comes first.
-   All of the characters must be printable or whitespace. */
-
-#if !defined (isspace)
-#define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\f')
-#endif
-
-#if !defined (isprint)
-#define isprint(c) (isletter(c) || digit(c) || ispunct(c))
-#endif
-
-int
-check_binary_file (sample, sample_len)
-     unsigned char *sample;
-     int sample_len;
-{
-  register int i;
-
-  for (i = 0; i < sample_len; i++)
-    {
-      if (sample[i] == '\n')
-       break;
-
-      if (!isspace (sample[i]) && !isprint (sample[i]))
-       return (1);
-    }
-  return (0);
-}