]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - execute_cmd.c
Bash-4.3 patch 15
[thirdparty/bash.git] / execute_cmd.c
index 79abc28ce128fc63e41f71872e7d5dc2a2443649..f9a755de82a29ebb199b1295ae4ea80ae2ff5ac0 100644 (file)
@@ -1,22 +1,23 @@
-/* execute_command.c -- Execute a COMMAND structure. */
+/* execute_cmd.c -- Execute a COMMAND structure. */
 
-/* Copyright (C) 1987,1991 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2013 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
-   Bash is free software; you can redistribute it and/or modify it
-   under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
+   Bash is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
 
-   Bash is distributed in the hope that it will be useful, but WITHOUT
-   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
-   License for more details.
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with Bash; see the file COPYING.  If not, write to the Free
-   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
 #include "config.h"
 
 #if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
 #endif /* _AIX && RISC6000 && !__GNUC__ */
 
 #include <stdio.h>
-#include <ctype.h>
+#include "chartypes.h"
 #include "bashtypes.h"
-#ifndef _MINIX
+#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
 #  include <sys/file.h>
 #endif
 #include "filecntl.h"
 #include "posixstat.h"
 #include <signal.h>
-#ifndef _MINIX
+#if defined (HAVE_SYS_PARAM_H)
 #  include <sys/param.h>
 #endif
 
 #  include <unistd.h>
 #endif
 
-#if defined (HAVE_LIMITS_H)
-#  include <limits.h>
-#endif
-
 #include "posixtime.h"
 
 #if defined (HAVE_SYS_RESOURCE_H) && !defined (RLIMTYPE)
 extern int errno;
 #endif
 
+#define NEED_FPURGE_DECL
+
 #include "bashansi.h"
+#include "bashintl.h"
 
 #include "memalloc.h"
 #include "shell.h"
@@ -83,7 +83,7 @@ extern int errno;
 #include "builtins/common.h"
 #include "builtins/builtext.h" /* list of builtins */
 
-#include <glob/fnmatch.h>
+#include <glob/strmatch.h>
 #include <tilde/tilde.h>
 
 #if defined (BUFFERED_INPUT)
@@ -98,61 +98,106 @@ extern int errno;
 #  include "bashhist.h"
 #endif
 
+extern int dollar_dollar_pid;
 extern int posixly_correct;
-extern int executing, breaking, continuing, loop_level;
-extern int interactive, interactive_shell, login_shell, expand_aliases;
-extern int parse_and_execute_level, running_trap, trap_line_number;
-extern int command_string_index, variable_context, line_number;
+extern int expand_aliases;
+extern int autocd;
+extern int breaking, continuing, loop_level;
+extern int parse_and_execute_level, running_trap, sourcelevel;
+extern int command_string_index, line_number;
 extern int dot_found_in_search;
 extern int already_making_children;
-extern char **temporary_env, **function_env, **builtin_env;
+extern int tempenv_assign_error;
 extern char *the_printed_command, *shell_name;
 extern pid_t last_command_subst_pid;
-extern Function *last_shell_builtin, *this_shell_builtin;
+extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
 extern char **subshell_argv, **subshell_envp;
 extern int subshell_argc;
+extern time_t shell_start_time;
+#if 0
 extern char *glob_argv_flags;
+#endif
+
+extern int job_control;        /* XXX */
 
-extern int getdtablesize ();
-extern int close ();
+extern int close __P((int));
 
 /* Static functions defined and used in this file. */
-static void close_pipes (), do_piping (), bind_lastarg ();
-static void cleanup_redirects ();
+static void close_pipes __P((int, int));
+static void do_piping __P((int, int));
+static void bind_lastarg __P((char *));
+static int shell_control_structure __P((enum command_type));
+static void cleanup_redirects __P((REDIRECT *));
 
-static int execute_for_command ();
+#if defined (JOB_CONTROL)
+static int restore_signal_mask __P((sigset_t *));
+#endif
+
+static void async_redirect_stdin __P((void));
+
+static int builtin_status __P((int));
+
+static int execute_for_command __P((FOR_COM *));
 #if defined (SELECT_COMMAND)
-static int execute_select_command ();
+static int displen __P((const char *));
+static int print_index_and_element __P((int, int, WORD_LIST *));
+static void indent __P((int, int));
+static void print_select_list __P((WORD_LIST *, int, int, int));
+static char *select_query __P((WORD_LIST *, int, char *, int));
+static int execute_select_command __P((SELECT_COM *));
 #endif
 #if defined (DPAREN_ARITHMETIC)
-static int execute_arith_command ();
+static int execute_arith_command __P((ARITH_COM *));
 #endif
 #if defined (COND_COMMAND)
-static int execute_cond_command ();
+static int execute_cond_node __P((COND_COM *));
+static int execute_cond_command __P((COND_COM *));
 #endif
 #if defined (COMMAND_TIMING)
-static int time_command ();
+static int mkfmt __P((char *, int, int, time_t, int));
+static void print_formatted_time __P((FILE *, char *,
+                                     time_t, int, time_t, int,
+                                     time_t, int, int));
+static int time_command __P((COMMAND *, int, int, int, struct fd_bitmap *));
 #endif
 #if defined (ARITH_FOR_COMMAND)
-static int execute_arith_for_command ();
-#endif
-static int execute_case_command ();
-static int execute_while_command (), execute_until_command ();
-static int execute_while_or_until ();
-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 ();
-
-static int execute_in_subshell ();
+static intmax_t eval_arith_for_expr __P((WORD_LIST *, int *));
+static int execute_arith_for_command __P((ARITH_FOR_COM *));
+#endif
+static int execute_case_command __P((CASE_COM *));
+static int execute_while_command __P((WHILE_COM *));
+static int execute_until_command __P((WHILE_COM *));
+static int execute_while_or_until __P((WHILE_COM *, int));
+static int execute_if_command __P((IF_COM *));
+static int execute_null_command __P((REDIRECT *, int, int, int));
+static void fix_assignment_words __P((WORD_LIST *));
+static int execute_simple_command __P((SIMPLE_COM *, int, int, int, struct fd_bitmap *));
+static int execute_builtin __P((sh_builtin_func_t *, WORD_LIST *, int, int));
+static int execute_function __P((SHELL_VAR *, WORD_LIST *, int, struct fd_bitmap *, int, int));
+static int execute_builtin_or_function __P((WORD_LIST *, sh_builtin_func_t *,
+                                           SHELL_VAR *,
+                                           REDIRECT *, struct fd_bitmap *, int));
+static void execute_subshell_builtin_or_function __P((WORD_LIST *, REDIRECT *,
+                                                     sh_builtin_func_t *,
+                                                     SHELL_VAR *,
+                                                     int, int, int,
+                                                     struct fd_bitmap *,
+                                                     int));
+static int execute_disk_command __P((WORD_LIST *, REDIRECT *, char *,
+                                     int, int, int, struct fd_bitmap *, int));
+
+static char *getinterp __P((char *, int, int *));
+static void initialize_subshell __P((void));
+static int execute_in_subshell __P((COMMAND *, int, int, int, struct fd_bitmap *));
+#if defined (COPROCESS_SUPPORT)
+static int execute_coproc __P((COMMAND *, int, int, struct fd_bitmap *));
+#endif
 
-/* The line number that the currently executing function starts on. */
-static int function_line_number;
+static int execute_pipeline __P((COMMAND *, int, int, int, struct fd_bitmap *));
+
+static int execute_connection __P((COMMAND *, int, int, int, struct fd_bitmap *));
+
+static int execute_intern_function __P((WORD_DESC *, FUNCTION_DEF *));
 
 /* Set to 1 if fd 0 was the subject of redirection to a subshell.  Global
    so that reader_loop can set it to zero before executing a command. */
@@ -162,11 +207,10 @@ int stdin_redir;
    `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;
+/* The printed representation of the currently-executing command (same as
+   the_printed_command), except when a trap is being executed.  Useful for
+   a debugger to know where exactly the program is currently executing. */
+char *the_printed_command_except_trap;
 
 /* For catching RETURN in a function. */
 int return_catch_flag;
@@ -176,6 +220,14 @@ procenv_t return_catch;
 /* The value returned by the last synchronous command. */
 int last_command_exit_value;
 
+/* Whether or not the last command (corresponding to last_command_exit_value)
+   was terminated by a signal, and, if so, which one. */
+int last_command_exit_signal;
+
+/* Are we currently ignoring the -e option for the duration of a builtin's
+   execution? */
+int builtin_ignoring_errexit = 0;
+
 /* The list of redirections to perform which will undo the redirections
    that I made in the shell. */
 REDIRECT *redirection_undo_list = (REDIRECT *)NULL;
@@ -185,23 +237,67 @@ REDIRECT *redirection_undo_list = (REDIRECT *)NULL;
    that must be undone even when exec discards redirection_undo_list. */
 REDIRECT *exec_redirection_undo_list = (REDIRECT *)NULL;
 
+/* When greater than zero, value is the `level' of builtins we are
+   currently executing (e.g. `eval echo a' would have it set to 2). */
+int executing_builtin = 0;
+
+/* Non-zero if we are executing a command list (a;b;c, etc.) */
+int executing_list = 0;
+
+/* Non-zero if failing commands in a command substitution should not exit the
+   shell even if -e is set.  Used to pass the CMD_IGNORE_RETURN flag down to
+   commands run in command substitutions by parse_and_execute. */
+int comsub_ignore_return = 0;
+
 /* Non-zero if we have just forked and are currently running in a subshell
    environment. */
 int subshell_environment;
 
+/* Count of nested subshells, like SHLVL.  Available via $BASH_SUBSHELL */
+int subshell_level = 0;
+
 /* Currently-executing shell function. */
 SHELL_VAR *this_shell_function;
 
+/* If non-zero, matches in case and [[ ... ]] are case-insensitive */
+int match_ignore_case = 0;
+
+int executing_command_builtin = 0;
+
+struct stat SB;                /* used for debugging */
+
+static int special_builtin_failed;
+
+static COMMAND *currently_executing_command;
+
+/* The line number that the currently executing function starts on. */
+static int function_line_number;
+
+/* XXX - set to 1 if we're running the DEBUG trap and we want to show the line
+   number containing the function name.  Used by executing_line_number to
+   report the correct line number.  Kind of a hack. */
+static int showing_function_line;
+
+/* $LINENO ($BASH_LINENO) for use by an ERR trap.  Global so parse_and_execute
+   can save and restore it. */
+int line_number_for_err_trap;
+
+/* A sort of function nesting level counter */
+int funcnest = 0;
+int funcnest_max = 0;          /* bash-4.2 */
+
+int lastpipe_opt = 0;
+
 struct fd_bitmap *current_fds_to_close = (struct fd_bitmap *)NULL;
 
-#define FD_BITMAP_DEFAULT_SIZE 32L
+#define FD_BITMAP_DEFAULT_SIZE 32
 
 /* Functions to allocate and deallocate the structures used to pass
    information from the shell to its children about file descriptors
    to close. */
 struct fd_bitmap *
 new_fd_bitmap (size)
-     long size;
+     int size;
 {
   struct fd_bitmap *ret;
 
@@ -211,8 +307,8 @@ new_fd_bitmap (size)
 
   if (size)
     {
-      ret->bitmap = xmalloc (size);
-      bzero (ret->bitmap, size);
+      ret->bitmap = (char *)xmalloc (size);
+      memset (ret->bitmap, '\0', size);
     }
   else
     ret->bitmap = (char *)NULL;
@@ -248,11 +344,25 @@ close_fd_bitmap (fdbp)
 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;
-  else if (running_trap)
-    return trap_line_number;
+  if (executing && showing_function_line == 0 &&
+      (variable_context == 0 || interactive_shell == 0) &&
+      currently_executing_command)
+    {
+#if defined (COND_COMMAND)
+      if (currently_executing_command->type == cm_cond)
+       return currently_executing_command->value.Cond->line;
+#endif
+#if defined (DPAREN_ARITHMETIC)
+      if (currently_executing_command->type == cm_arith)
+       return currently_executing_command->value.Arith->line;
+#endif
+#if defined (ARITH_FOR_COMMAND)
+      if (currently_executing_command->type == cm_arith_for)
+       return currently_executing_command->value.ArithFor->line;
+#endif
+
+       return line_number;
+    }
   else
     return line_number;
 }
@@ -289,6 +399,7 @@ execute_command (command)
     unlink_fifo_list ();
 #endif /* PROCESS_SUBSTITUTION */
 
+  QUIT;
   return (result);
 }
 
@@ -299,7 +410,6 @@ shell_control_structure (type)
 {
   switch (type)
     {
-    case cm_for:
 #if defined (ARITH_FOR_COMMAND)
     case cm_arith_for:
 #endif
@@ -316,7 +426,9 @@ shell_control_structure (type)
     case cm_while:
     case cm_until:
     case cm_if:
+    case cm_for:
     case cm_group:
+    case cm_function_def:
       return (1);
 
     default:
@@ -330,7 +442,7 @@ static void
 cleanup_redirects (list)
      REDIRECT *list;
 {
-  do_redirections (list, 1, 0, 0);
+  do_redirections (list, RX_ACTIVE);
   dispose_redirects (list);
 }
 
@@ -340,7 +452,7 @@ static void
 cleanup_func_redirects (list)
      REDIRECT *list;
 {
-  do_redirections (list, 1, 0, 0);
+  do_redirections (list, RX_ACTIVE);
 }
 #endif
 
@@ -359,12 +471,13 @@ dispose_exec_redirects ()
    is interrupted or errors occur while creating a pipeline. */
 static int
 restore_signal_mask (set)
-     sigset_t set;
+     sigset_t *set;
 {
-  return (sigprocmask (SIG_SETMASK, &set, (sigset_t *)NULL));
+  return (sigprocmask (SIG_SETMASK, set, (sigset_t *)NULL));
 }
 #endif /* JOB_CONTROL */
 
+#ifdef DEBUG
 /* A debugging function that can be called from gdb, for instance. */
 void
 open_files ()
@@ -374,7 +487,7 @@ open_files ()
 
   fd_table_size = getdtablesize ();
 
-  fprintf (stderr, "pid %d open files:", (int)getpid ());
+  fprintf (stderr, "pid %ld open files:", (long)getpid ());
   for (i = 3; i < fd_table_size; i++)
     {
       if ((f = fcntl (i, F_GETFD, 0)) != -1)
@@ -382,6 +495,7 @@ open_files ()
     }
   fprintf (stderr, "\n");
 }
+#endif
 
 static void
 async_redirect_stdin ()
@@ -395,12 +509,12 @@ async_redirect_stdin ()
       close (fd);
     }
   else if (fd < 0)
-    internal_error ("cannot redirect standard input from /dev/null: %s", strerror (errno));
+    internal_error (_("cannot redirect standard input from /dev/null: %s"), strerror (errno));
 }
 
 #define DESCRIBE_PID(pid) do { if (interactive) describe_pid (pid); } while (0)
 
-/* Execute the command passed in COMMAND, perhaps doing it asynchrounously.
+/* Execute the command passed in COMMAND, perhaps doing it asynchronously.
    COMMAND is exactly what read_command () places into GLOBAL_COMMAND.
    ASYNCHROUNOUS, if non-zero, says to do this command in the background.
    PIPE_IN and PIPE_OUT are file descriptors saying where input comes
@@ -420,16 +534,27 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
      int pipe_in, pipe_out;
      struct fd_bitmap *fds_to_close;
 {
-  int exec_result, invert, ignore_return, was_debug_trap;
+  int exec_result, user_subshell, invert, ignore_return, was_error_trap;
   REDIRECT *my_undo_list, *exec_undo_list;
-  volatile pid_t last_pid;
+  char *tcmd;
+  volatile int last_pid;
+  volatile int save_line_number;
+#if defined (PROCESS_SUBSTITUTION)
+  volatile int ofifo, nfifo, osize, saved_fifo;
+  volatile char *ofifo_list;
+#endif
 
-  if (command == 0 || breaking || continuing || read_but_dont_execute)
+  if (breaking || continuing)
+    return (last_command_exit_value);
+  if (command == 0 || read_but_dont_execute)
     return (EXECUTION_SUCCESS);
 
+  QUIT;
   run_pending_traps ();
 
+#if 0
   if (running_trap == 0)
+#endif
     currently_executing_command = command;
 
   invert = (command->flags & CMD_INVERT_RETURN) != 0;
@@ -448,26 +573,60 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
   if (command->type == cm_subshell && (command->flags & CMD_NO_FORK))
     return (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
 
+#if defined (COPROCESS_SUPPORT)
+  if (command->type == cm_coproc)
+    return (execute_coproc (command, pipe_in, pipe_out, fds_to_close));
+#endif
+
+  user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0);
+
   if (command->type == cm_subshell ||
       (command->flags & (CMD_WANT_SUBSHELL|CMD_FORCE_SUBSHELL)) ||
       (shell_control_structure (command->type) &&
        (pipe_out != NO_PIPE || pipe_in != NO_PIPE || asynchronous)))
     {
       pid_t paren_pid;
+      int s;
 
       /* Fork a subshell, turn off the subshell bit, turn off job
         control and call execute_command () on the command again. */
-      paren_pid = make_child (savestring (make_command_string (command)),
-                             asynchronous);
+      line_number_for_err_trap = line_number;
+      tcmd = make_command_string (command);
+      paren_pid = make_child (savestring (tcmd), asynchronous);
+
+      if (user_subshell && signal_is_trapped (ERROR_TRAP) && 
+         signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
+       {
+         FREE (the_printed_command_except_trap);
+         the_printed_command_except_trap = savestring (the_printed_command);
+       }
+
       if (paren_pid == 0)
-       exit (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
-       /* NOTREACHED */
+        {
+         /* We want to run the exit trap for forced {} subshells, and we
+            want to note this before execute_in_subshell modifies the
+            COMMAND struct.  Need to keep in mind that execute_in_subshell
+            runs the exit trap for () subshells itself. */
+         /* This handles { command; } & */
+         s = user_subshell == 0 && command->type == cm_group && pipe_in == NO_PIPE && pipe_out == NO_PIPE && asynchronous;
+         /* run exit trap for : | { ...; } and { ...; } | : */
+         /* run exit trap for : | ( ...; ) and ( ...; ) | : */
+         s += user_subshell == 0 && command->type == cm_group && (pipe_in != NO_PIPE || pipe_out != NO_PIPE) && asynchronous == 0;
+
+         last_command_exit_value = execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close);
+         if (s)
+           subshell_exit (last_command_exit_value);
+         else
+           exit (last_command_exit_value);
+         /* NOTREACHED */
+        }
       else
        {
          close_pipes (pipe_in, pipe_out);
 
 #if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
-         unlink_fifo_list ();
+         if (variable_context == 0)    /* wait until shell function completes */
+           unlink_fifo_list ();
 #endif
          /* 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
@@ -481,17 +640,34 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 
          if (asynchronous == 0)
            {
-             last_command_exit_value = wait_for (paren_pid);
+             was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
+             invert = (command->flags & CMD_INVERT_RETURN) != 0;
+             ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
+
+             exec_result = wait_for (paren_pid);
 
              /* If we have to, invert the return value. */
              if (invert)
-               exec_result = ((last_command_exit_value == EXECUTION_SUCCESS)
+               exec_result = ((exec_result == EXECUTION_SUCCESS)
                                ? EXECUTION_FAILURE
                                : EXECUTION_SUCCESS);
-             else
-               exec_result = last_command_exit_value;
 
-             return (last_command_exit_value = exec_result);
+             last_command_exit_value = exec_result;
+             if (user_subshell && was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
+               {
+                 save_line_number = line_number;
+                 line_number = line_number_for_err_trap;
+                 run_error_trap ();
+                 line_number = save_line_number;
+               }
+
+             if (user_subshell && ignore_return == 0 && invert == 0 && exit_immediately_on_error && exec_result != EXECUTION_SUCCESS)
+               {
+                 run_pending_traps ();
+                 jump_to_top_level (ERREXIT);
+               }
+
+             return (last_command_exit_value);
            }
          else
            {
@@ -499,6 +675,9 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 
              run_pending_traps ();
 
+             /* Posix 2013 2.9.3.1: "the exit status of an asynchronous list
+                shall be zero." */
+             last_command_exit_value = 0;
              return (EXECUTION_SUCCESS);
            }
        }
@@ -515,7 +694,9 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
       else
        {
          exec_result = time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close);
+#if 0
          if (running_trap == 0)
+#endif
            currently_executing_command = (COMMAND *)NULL;
        }
       return (exec_result);
@@ -525,18 +706,34 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
   if (shell_control_structure (command->type) && command->redirects)
     stdin_redir = stdin_redirects (command->redirects);
 
+#if defined (PROCESS_SUBSTITUTION)
+  if (variable_context != 0)
+    {
+      ofifo = num_fifos ();
+      ofifo_list = copy_fifo_list ((int *)&osize);
+      saved_fifo = 1;
+    }
+  else
+    saved_fifo = 0;
+#endif
+
   /* Handle WHILE FOR CASE etc. with redirections.  (Also '&' input
      redirection.)  */
-  if (do_redirections (command->redirects, 1, 1, 0) != 0)
+  if (do_redirections (command->redirects, RX_ACTIVE|RX_UNDOABLE) != 0)
     {
       cleanup_redirects (redirection_undo_list);
       redirection_undo_list = (REDIRECT *)NULL;
       dispose_exec_redirects ();
-      return (EXECUTION_FAILURE);
+#if defined (PROCESS_SUBSTITUTION)
+      if (saved_fifo)
+       free ((void *)ofifo_list);
+#endif
+      return (last_command_exit_value = EXECUTION_FAILURE);
     }
 
   if (redirection_undo_list)
     {
+      /* XXX - why copy here? */
       my_undo_list = (REDIRECT *)copy_redirects (redirection_undo_list);
       dispose_redirects (redirection_undo_list);
       redirection_undo_list = (REDIRECT *)NULL;
@@ -546,6 +743,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 
   if (exec_redirection_undo_list)
     {
+      /* XXX - why copy here? */
       exec_undo_list = (REDIRECT *)copy_redirects (exec_redirection_undo_list);
       dispose_redirects (exec_redirection_undo_list);
       exec_redirection_undo_list = (REDIRECT *)NULL;
@@ -570,19 +768,26 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
     {
     case cm_simple:
       {
-       /* We can't rely on this variable retaining its value across a
+       save_line_number = line_number;
+       /* We can't rely on variables retaining their values 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. */
+#if defined (RECYCLES_PIDS)
+       last_made_pid = NO_PID;
+#endif
        last_pid = last_made_pid;
-       was_debug_trap = signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0;
+       was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
 
        if (ignore_return && command->value.Simple)
          command->value.Simple->flags |= CMD_IGNORE_RETURN;
        if (command->flags & CMD_STDIN_REDIR)
          command->value.Simple->flags |= CMD_STDIN_REDIR;
+
+       line_number_for_err_trap = line_number = command->value.Simple->line;
        exec_result =
          execute_simple_command (command->value.Simple, pipe_in, pipe_out,
                                  asynchronous, fds_to_close);
+       line_number = save_line_number;
 
        /* The temporary environment should be used for only the simple
           command immediately following its definition. */
@@ -598,9 +803,11 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
           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)
+          when the shell is compiled without job control.  Don't worry about
+          whether or not last_made_pid == last_pid; already_making_children
+          tells us whether or not there are unwaited-for children to wait
+          for and reap. */
+       if (already_making_children && pipe_out == NO_PIPE)
          {
            stop_pipeline (asynchronous, (COMMAND *)NULL);
 
@@ -616,31 +823,30 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 #endif
            /* When executing a shell function that executes other
               commands, this causes the last simple command in
-              the function to be waited for twice. */
+              the function to be waited for twice.  This also causes
+              subshells forked to execute builtin commands (e.g., in
+              pipelines) to be waited for twice. */
              exec_result = wait_for (last_made_pid);
-#if defined (RECYCLES_PIDS)
-             /* LynxOS, for one, recycles pids very quickly -- so quickly
-                that a new process may have the same pid as the last one
-                created.  This has been reported to fix the problem. */
-             if (exec_result == 0)
-               last_made_pid = NO_PID;
-#endif
          }
       }
 
-      if (was_debug_trap)
+      /* 2009/02/13 -- pipeline failure is processed elsewhere.  This handles
+        only the failure of a simple command. */
+      if (was_error_trap && ignore_return == 0 && invert == 0 && pipe_in == NO_PIPE && pipe_out == NO_PIPE && exec_result != EXECUTION_SUCCESS)
        {
          last_command_exit_value = exec_result;
-         run_debug_trap ();
+         line_number = line_number_for_err_trap;
+         run_error_trap ();
+         line_number = save_line_number;
        }
 
       if (ignore_return == 0 && invert == 0 &&
          ((posixly_correct && interactive == 0 && special_builtin_failed) ||
-          (exit_immediately_on_error && (exec_result != EXECUTION_SUCCESS))))
+          (exit_immediately_on_error && pipe_in == NO_PIPE && pipe_out == NO_PIPE && exec_result != EXECUTION_SUCCESS)))
        {
          last_command_exit_value = exec_result;
          run_pending_traps ();
-         jump_to_top_level (EXITPROG);
+         jump_to_top_level (ERREXIT);
        }
 
       break;
@@ -742,23 +948,64 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 
 #if defined (DPAREN_ARITHMETIC)
     case cm_arith:
+      was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
       if (ignore_return)
        command->value.Arith->flags |= CMD_IGNORE_RETURN;
+      line_number_for_err_trap = save_line_number = line_number;
       exec_result = execute_arith_command (command->value.Arith);
+      line_number = save_line_number;
+
+      if (was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
+       {
+         last_command_exit_value = exec_result;
+         save_line_number = line_number;
+         line_number = line_number_for_err_trap;
+         run_error_trap ();
+         line_number = save_line_number;
+       }
+
+      if (ignore_return == 0 && invert == 0 && exit_immediately_on_error && exec_result != EXECUTION_SUCCESS)
+       {
+         last_command_exit_value = exec_result;
+         run_pending_traps ();
+         jump_to_top_level (ERREXIT);
+       }
+
       break;
 #endif
 
 #if defined (COND_COMMAND)
     case cm_cond:
+      was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
       if (ignore_return)
        command->value.Cond->flags |= CMD_IGNORE_RETURN;
+
+      line_number_for_err_trap = save_line_number = line_number;
       exec_result = execute_cond_command (command->value.Cond);
+      line_number = save_line_number;
+
+      if (was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
+       {
+         last_command_exit_value = exec_result;
+         save_line_number = line_number;
+         line_number = line_number_for_err_trap;
+         run_error_trap ();
+         line_number = save_line_number;
+       }
+
+      if (ignore_return == 0 && invert == 0 && exit_immediately_on_error && exec_result != EXECUTION_SUCCESS)
+       {
+         last_command_exit_value = exec_result;
+         run_pending_traps ();
+         jump_to_top_level (ERREXIT);
+       }
+
       break;
 #endif
     
     case cm_function_def:
       exec_result = execute_intern_function (command->value.Function_def->name,
-                                            command->value.Function_def->command);
+                                            command->value.Function_def);
       break;
 
     default:
@@ -767,7 +1014,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 
   if (my_undo_list)
     {
-      do_redirections (my_undo_list, 1, 0, 0);
+      do_redirections (my_undo_list, RX_ACTIVE);
       dispose_redirects (my_undo_list);
     }
 
@@ -777,53 +1024,84 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
   if (my_undo_list || exec_undo_list)
     discard_unwind_frame ("loop_redirections");
 
+#if defined (PROCESS_SUBSTITUTION)
+  if (saved_fifo)
+    {
+      nfifo = num_fifos ();
+      if (nfifo > ofifo)
+       close_new_fifos ((char *)ofifo_list, osize);
+      free ((void *)ofifo_list);
+    }
+#endif
+
   /* Invert the return value if we have to */
   if (invert)
     exec_result = (exec_result == EXECUTION_SUCCESS)
                    ? EXECUTION_FAILURE
                    : EXECUTION_SUCCESS;
 
+#if defined (DPAREN_ARITHMETIC) || defined (COND_COMMAND)
+  /* This is where we set PIPESTATUS from the exit status of the appropriate
+     compound commands (the ones that look enough like simple commands to
+     cause confusion).  We might be able to optimize by not doing this if
+     subshell_environment != 0. */
+  switch (command->type)
+    {
+#  if defined (DPAREN_ARITHMETIC)
+    case cm_arith:
+#  endif
+#  if defined (COND_COMMAND)
+    case cm_cond:
+#  endif
+      set_pipestatus_from_exit (exec_result);
+      break;
+    }
+#endif
+
   last_command_exit_value = exec_result;
   run_pending_traps ();
+#if 0
   if (running_trap == 0)
+#endif
     currently_executing_command = (COMMAND *)NULL;
+
   return (last_command_exit_value);
 }
 
 #if defined (COMMAND_TIMING)
 
 #if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
-extern struct timeval *difftimeval();
-extern struct timeval *addtimeval();
-extern int timeval_to_cpu();
+extern struct timeval *difftimeval __P((struct timeval *, struct timeval *, struct timeval *));
+extern struct timeval *addtimeval __P((struct timeval *, struct timeval *, struct timeval *));
+extern int timeval_to_cpu __P((struct timeval *, struct timeval *, struct timeval *));
 #endif
 
 #define POSIX_TIMEFORMAT "real %2R\nuser %2U\nsys %2S"
 #define BASH_TIMEFORMAT  "\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS"
 
-static int precs[] = { 0, 100, 10, 1 };
+static const int precs[] = { 0, 100, 10, 1 };
 
 /* 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;
+     time_t sec;
      int sec_fraction;
 {
-  long min;
-  char abuf[16];
+  time_t min;
+  char abuf[INT_STRLEN_BOUND(time_t) + 1];
   int ind, aind;
 
   ind = 0;
-  abuf[15] = '\0';
+  abuf[sizeof(abuf) - 1] = '\0';
 
   /* If LNG is non-zero, we want to decompose SEC into minutes and seconds. */
   if (lng)
     {
       min = sec / 60;
       sec %= 60;
-      aind = 14;
+      aind = sizeof(abuf) - 2;
       do
        abuf[aind--] = (min % 10) + '0';
       while (min /= 10);
@@ -834,7 +1112,7 @@ mkfmt (buf, prec, lng, sec, sec_fraction)
     }
 
   /* Now add the seconds. */
-  aind = 14;
+  aind = sizeof (abuf) - 2;
   do
     abuf[aind--] = (sec % 10) + '0';
   while (sec /= 10);
@@ -864,16 +1142,16 @@ mkfmt (buf, prec, lng, sec, sec_fraction)
 
 /* Interpret the format string FORMAT, interpolating the following escape
    sequences:
-               %[prec][l][RUS]
+               %[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
+               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
@@ -883,18 +1161,22 @@ 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;
+     time_t rs;
+     int rsf;
+     time_t us;
+     int usf;
+     time_t ss;
+     int ssf, cpu;
 {
   int prec, lng, len;
-  char *str, *s, ts[32];
-  long sum;
+  char *str, *s, ts[INT_STRLEN_BOUND (time_t) + sizeof ("mSS.FFFF")];
+  time_t sum;
   int sum_frac;
   int sindex, ssize;
 
   len = strlen (format);
   ssize = (len + 64) - (len % 64);
-  str = xmalloc (ssize);
+  str = (char *)xmalloc (ssize);
   sindex = 0;
 
   for (s = format; *s; s++)
@@ -913,8 +1195,11 @@ print_formatted_time (fp, format, rs, rsf, us, usf, ss, ssf, cpu)
       else if (s[1] == 'P')
        {
          s++;
+#if 0
+         /* clamp CPU usage at 100% */
          if (cpu > 10000)
            cpu = 10000;
+#endif
          sum = cpu / 100;
          sum_frac = (cpu % 100) * 10;
          len = mkfmt (ts, 2, 0, sum, sum_frac);
@@ -927,7 +1212,7 @@ print_formatted_time (fp, format, rs, rsf, us, usf, ss, ssf, cpu)
          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 (DIGIT (*s))               /* `precision' */
            {
              prec = *s++ - '0';
              if (prec > 3) prec = 3;
@@ -945,7 +1230,7 @@ print_formatted_time (fp, format, rs, rsf, us, usf, ss, ssf, cpu)
            len = mkfmt (ts, prec, lng, ss, ssf);
          else
            {
-             internal_error ("bad format character in time format: %c", *s);
+             internal_error (_("TIMEFORMAT: `%c': invalid format character"), *s);
              free (str);
              return;
            }
@@ -968,8 +1253,8 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
      int asynchronous, pipe_in, pipe_out;
      struct fd_bitmap *fds_to_close;
 {
-  int rv, posix_time, old_flags;
-  long rs, us, ss;
+  int rv, posix_time, old_flags, nullcmd;
+  time_t rs, us, ss;
   int rsf, usf, ssf;
   int cpu;
   char *time_format;
@@ -977,7 +1262,9 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
 #if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
   struct timeval real, user, sys;
   struct timeval before, after;
-  struct timezone dtz;
+#  if defined (HAVE_STRUCT_TIMEZONE)
+  struct timezone dtz;                         /* posix doesn't define this */
+#  endif
   struct rusage selfb, selfa, kidsb, kidsa;    /* a = after, b = before */
 #else
 #  if defined (HAVE_TIMES)
@@ -987,7 +1274,11 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
 #endif
 
 #if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
+#  if defined (HAVE_STRUCT_TIMEZONE)
   gettimeofday (&before, &dtz);
+#  else
+  gettimeofday (&before, (void *)NULL);
+#  endif /* !HAVE_STRUCT_TIMEZONE */
   getrusage (RUSAGE_SELF, &selfb);
   getrusage (RUSAGE_CHILDREN, &kidsb);
 #else
@@ -996,18 +1287,36 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
 #  endif
 #endif
 
-  posix_time = (command->flags & CMD_TIME_POSIX);
+  posix_time = command && (command->flags & CMD_TIME_POSIX);
+
+  nullcmd = (command == 0) || (command->type == cm_simple && command->value.Simple->words == 0 && command->value.Simple->redirects == 0);
+  if (posixly_correct && nullcmd)
+    {
+#if defined (HAVE_GETRUSAGE)
+      selfb.ru_utime.tv_sec = kidsb.ru_utime.tv_sec = selfb.ru_stime.tv_sec = kidsb.ru_stime.tv_sec = 0;
+      selfb.ru_utime.tv_usec = kidsb.ru_utime.tv_usec = selfb.ru_stime.tv_usec = kidsb.ru_stime.tv_usec = 0;
+      before.tv_sec = shell_start_time;
+      before.tv_usec = 0;
+#else
+      before.tms_utime = before.tms_stime = before.tms_cutime = before.tms_cstime = 0;
+      tbefore = shell_start_time;
+#endif
+    }
 
   old_flags = command->flags;
   command->flags &= ~(CMD_TIME_PIPELINE|CMD_TIME_POSIX);
   rv = execute_command_internal (command, asynchronous, pipe_in, pipe_out, fds_to_close);
   command->flags = old_flags;
 
-  rs = us = ss = 0L;
+  rs = us = ss = 0;
   rsf = usf = ssf = cpu = 0;
 
 #if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
+#  if defined (HAVE_STRUCT_TIMEZONE)
   gettimeofday (&after, &dtz);
+#  else
+  gettimeofday (&after, (void *)NULL);
+#  endif /* !HAVE_STRUCT_TIMEZONE */
   getrusage (RUSAGE_SELF, &selfa);
   getrusage (RUSAGE_CHILDREN, &kidsa);
 
@@ -1039,7 +1348,7 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
   cpu = (real == 0) ? 0 : ((user + sys) * 10000) / real;
 
 #  else
-  rs = us = ss = 0L;
+  rs = us = ss = 0;
   rsf = usf = ssf = cpu = 0;
 #  endif
 #endif
@@ -1047,8 +1356,12 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
   if (posix_time)
     time_format = POSIX_TIMEFORMAT;
   else if ((time_format = get_string_value ("TIMEFORMAT")) == 0)
-    time_format = BASH_TIMEFORMAT;
-
+    {
+      if (posixly_correct && nullcmd)
+       time_format = "user\t%2lU\nsys\t%2lS";
+      else
+       time_format = BASH_TIMEFORMAT;
+    }
   if (time_format && *time_format)
     print_formatted_time (stderr, time_format, rs, rsf, us, usf, ss, ssf, cpu);
 
@@ -1067,15 +1380,24 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
      struct fd_bitmap *fds_to_close;
 {
   int user_subshell, return_code, function_value, should_redir_stdin, invert;
-  int ois;
-  COMMAND *tcom;
+  int ois, user_coproc;
+  int result;
+  volatile COMMAND *tcom;
 
+  USE_VAR(user_subshell);
+  USE_VAR(user_coproc);
+  USE_VAR(invert);
+  USE_VAR(tcom);
+  USE_VAR(asynchronous);
+
+  subshell_level++;
   should_redir_stdin = (asynchronous && (command->flags & CMD_STDIN_REDIR) &&
                          pipe_in == NO_PIPE &&
                          stdin_redirects (command->redirects) == 0);
 
   invert = (command->flags & CMD_INVERT_RETURN) != 0;
   user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0);
+  user_coproc = command->type == cm_coproc;
 
   command->flags &= ~(CMD_FORCE_SUBSHELL | CMD_WANT_SUBSHELL | CMD_INVERT_RETURN);
 
@@ -1083,6 +1405,10 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
      the special case of an asynchronous GROUP command where the
      the subshell bit is turned on down in case cm_group: below),
      turn off `asynchronous', so that two subshells aren't spawned.
+     XXX - asynchronous used to be set to 0 in this block, but that
+     means that setup_async_signals was never run.  Now it's set to
+     0 after subshell_environment is set appropriately and setup_async_signals
+     is run.
 
      This seems semantically correct to me.  For example,
      ( foo ) & seems to say ``do the command `foo' in a subshell
@@ -1110,19 +1436,41 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
         aliases. */
       if (ois != interactive_shell)
        expand_aliases = 0;
-      asynchronous = 0;
     }
 
   /* Subshells are neither login nor interactive. */
   login_shell = interactive = 0;
 
-  subshell_environment = user_subshell ? SUBSHELL_PAREN : SUBSHELL_ASYNC;
+  if (user_subshell)
+    subshell_environment = SUBSHELL_PAREN;
+  else
+    {
+      subshell_environment = 0;                        /* XXX */
+      if (asynchronous)
+       subshell_environment |= SUBSHELL_ASYNC;
+      if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
+       subshell_environment |= SUBSHELL_PIPE;
+      if (user_coproc)
+       subshell_environment |= SUBSHELL_COPROC;
+    }
 
   reset_terminating_signals ();                /* in sig.c */
   /* Cancel traps, in trap.c. */
-  restore_original_signals ();
+  /* Reset the signal handlers in the child, but don't free the
+     trap strings.  Set a flag noting that we have to free the
+     trap strings if we run trap to change a signal disposition. */
+  reset_signal_handlers ();
+  subshell_environment |= SUBSHELL_RESETTRAP;
+
+  /* Make sure restore_original_signals doesn't undo the work done by
+     make_child to ensure that asynchronous children are immune to SIGINT
+     and SIGQUIT.  Turn off asynchronous to make sure more subshells are
+     not spawned. */
   if (asynchronous)
-    setup_async_signals ();
+    {
+      setup_async_signals ();
+      asynchronous = 0;
+    }
 
 #if defined (JOB_CONTROL)
   set_sigchld_handler ();
@@ -1141,6 +1489,10 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
 
   do_piping (pipe_in, pipe_out);
 
+#if defined (COPROCESS_SUPPORT)
+  coproc_closeall ();
+#endif
+
   /* If this is a user subshell, set a flag if stdin was redirected.
      This is used later to decide whether to redirect fd 0 to
      /dev/null for async commands in the subshell.  This adds more
@@ -1148,7 +1500,7 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
   if (user_subshell)
     {
       stdin_redir = stdin_redirects (command->redirects);
-      restore_default_signal (0);
+      restore_default_signal (EXIT_TRAP);
     }
 
   /* If this is an asynchronous command (command &), we want to
@@ -1160,21 +1512,35 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
   /* Do redirections, then dispose of them before recursive call. */
   if (command->redirects)
     {
-      if (do_redirections (command->redirects, 1, 0, 0) != 0)
+      if (do_redirections (command->redirects, RX_ACTIVE) != 0)
        exit (invert ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
 
       dispose_redirects (command->redirects);
       command->redirects = (REDIRECT *)NULL;
     }
 
-  tcom = (command->type == cm_subshell) ? command->value.Subshell->command : command;
+  if (command->type == cm_subshell)
+    tcom = command->value.Subshell->command;
+  else if (user_coproc)
+    tcom = command->value.Coproc->command;
+  else
+    tcom = command;
+
+  if (command->flags & CMD_TIME_PIPELINE)
+    tcom->flags |= CMD_TIME_PIPELINE;
+  if (command->flags & CMD_TIME_POSIX)
+    tcom->flags |= CMD_TIME_POSIX;
+  
+  /* Make sure the subshell inherits any CMD_IGNORE_RETURN flag. */
+  if ((command->flags & CMD_IGNORE_RETURN) && tcom != command)
+    tcom->flags |= CMD_IGNORE_RETURN;
 
   /* If this is a simple command, tell execute_disk_command that it
      might be able to get away without forking and simply exec.
      This means things like ( sleep 10 ) will only cause one fork.
      If we're timing the command or inverting its return value, however,
      we cannot do this optimization. */
-  if (user_subshell && (tcom->type == cm_simple || tcom->type == cm_subshell) &&
+  if ((user_subshell || user_coproc) && (tcom->type == cm_simple || tcom->type == cm_subshell) &&
       ((tcom->flags & CMD_TIME_PIPELINE) == 0) &&
       ((tcom->flags & CMD_INVERT_RETURN) == 0))
     {
@@ -1186,17 +1552,24 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
   invert = (tcom->flags & CMD_INVERT_RETURN) != 0;
   tcom->flags &= ~CMD_INVERT_RETURN;
 
+  result = setjmp_nosigs (top_level);
+
   /* If we're inside a function while executing this subshell, we
      need to handle a possible `return'. */
   function_value = 0;
   if (return_catch_flag)
-    function_value = setjmp (return_catch);
-
-  if (function_value)
+    function_value = setjmp_nosigs (return_catch);
+
+  /* If we're going to exit the shell, we don't want to invert the return
+     status. */
+  if (result == EXITPROG)
+    invert = 0, return_code = last_command_exit_value;
+  else if (result)
+    return_code = EXECUTION_FAILURE;
+  else if (function_value)
     return_code = return_catch_value;
   else
-    return_code = execute_command_internal
-      (tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
+    return_code = execute_command_internal ((COMMAND *)tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
 
   /* If we are asked to, invert the return value. */
   if (invert)
@@ -1211,96 +1584,767 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
       return_code = run_exit_trap ();
     }
 
+  subshell_level--;
   return (return_code);
   /* NOTREACHED */
 }
 
-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;
+#if defined (COPROCESS_SUPPORT)
+#define COPROC_MAX     16
+
+typedef struct cpelement
+  {
+    struct cpelement *next;
+    struct coproc *coproc;
+  }
+cpelement_t;
+    
+typedef struct cplist
+  {
+    struct cpelement *head;
+    struct cpelement *tail;
+    int ncoproc;
+    int lock;
+  }
+cplist_t;
+
+static struct cpelement *cpe_alloc __P((struct coproc *));
+static void cpe_dispose __P((struct cpelement *));
+static struct cpelement *cpl_add __P((struct coproc *));
+static struct cpelement *cpl_delete __P((pid_t));
+static void cpl_reap __P((void));
+static void cpl_flush __P((void));
+static void cpl_closeall __P((void));
+static struct cpelement *cpl_search __P((pid_t));
+static struct cpelement *cpl_searchbyname __P((const char *));
+static void cpl_prune __P((void));
+
+static void coproc_free __P((struct coproc *));
+
+/* Will go away when there is fully-implemented support for multiple coprocs. */
+Coproc sh_coproc = { 0, NO_PID, -1, -1, 0, 0, 0, 0, 0 };
+
+cplist_t coproc_list = {0, 0, 0};
+
+/* Functions to manage the list of coprocs */
+
+static struct cpelement *
+cpe_alloc (cp)
+     Coproc *cp;
 {
-  int prev, fildes[2], new_bitmap_size, dummyfd, ignore_return, exec_result;
-  COMMAND *cmd;
-  struct fd_bitmap *fd_bitmap;
+  struct cpelement *cpe;
 
-#if defined (JOB_CONTROL)
-  sigset_t set, oset;
-  BLOCK_CHILD (set, oset);
-#endif /* JOB_CONTROL */
+  cpe = (struct cpelement *)xmalloc (sizeof (struct cpelement));
+  cpe->coproc = cp;
+  cpe->next = (struct cpelement *)0;
+  return cpe;
+}
 
-  ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
+static void
+cpe_dispose (cpe)
+      struct cpelement *cpe;
+{
+  free (cpe);
+}
 
-  prev = pipe_in;
-  cmd = command;
+static struct cpelement *
+cpl_add (cp)
+     Coproc *cp;
+{
+  struct cpelement *cpe;
 
-  while (cmd && cmd->type == cm_connection &&
-        cmd->value.Connection && cmd->value.Connection->connector == '|')
+  cpe = cpe_alloc (cp);
+
+  if (coproc_list.head == 0)
     {
-      /* 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 ();
-#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 ();
-         return (EXECUTION_FAILURE);   /* XXX */
-       }
+      coproc_list.head = coproc_list.tail = cpe;
+      coproc_list.ncoproc = 0;                 /* just to make sure */
+    }
+  else
+    {
+      coproc_list.tail->next = cpe;
+      coproc_list.tail = cpe;
+    }
+  coproc_list.ncoproc++;
 
-      /* 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. */
+  return cpe;
+}
 
-      /* 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;
+static struct cpelement *
+cpl_delete (pid)
+     pid_t pid;
+{
+  struct cpelement *prev, *p;
 
-      fd_bitmap = new_fd_bitmap (new_bitmap_size);
+  for (prev = p = coproc_list.head; p; prev = p, p = p->next)
+    if (p->coproc->c_pid == pid)
+      {
+        prev->next = p->next;  /* remove from list */
+        break;
+      }
 
-      /* Now copy the old information into the new bitmap. */
-      xbcopy ((char *)fds_to_close->bitmap, (char *)fd_bitmap->bitmap, fds_to_close->size);
+  if (p == 0)
+    return 0;          /* not found */
 
-      /* And mark the pipe file descriptors to be closed. */
-      fd_bitmap->bitmap[fildes[0]] = 1;
+#if defined (DEBUG)
+  itrace("cpl_delete: deleting %d", pid);
+#endif
 
-      /* 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);
+  /* Housekeeping in the border cases. */
+  if (p == coproc_list.head)
+    coproc_list.head = coproc_list.head->next;
+  else if (p == coproc_list.tail)
+    coproc_list.tail = prev;
 
-#if defined (JOB_CONTROL)
-      add_unwind_protect (restore_signal_mask, oset);
-#endif /* JOB_CONTROL */
+  coproc_list.ncoproc--;
+  if (coproc_list.ncoproc == 0)
+    coproc_list.head = coproc_list.tail = 0;
+  else if (coproc_list.ncoproc == 1)
+    coproc_list.tail = coproc_list.head;               /* just to make sure */
 
-      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);
+  return (p);
+}
 
-      if (prev >= 0)
+static void
+cpl_reap ()
+{
+  struct cpelement *p, *next, *nh, *nt;
+
+  /* Build a new list by removing dead coprocs and fix up the coproc_list
+     pointers when done. */
+  nh = nt = next = (struct cpelement *)0;
+  for (p = coproc_list.head; p; p = next)
+    {
+      next = p->next;
+      if (p->coproc->c_flags & COPROC_DEAD)
+       {
+         coproc_list.ncoproc--;        /* keep running count, fix up pointers later */
+
+#if defined (DEBUG)
+         itrace("cpl_reap: deleting %d", p->coproc->c_pid);
+#endif
+
+         coproc_dispose (p->coproc);
+         cpe_dispose (p);
+       }
+      else if (nh == 0)
+       nh = nt = p;
+      else
+       {
+         nt->next = p;
+         nt = nt->next;
+       }
+    }
+
+  if (coproc_list.ncoproc == 0)
+    coproc_list.head = coproc_list.tail = 0;
+  else
+    {
+      if (nt)
+        nt->next = 0;
+      coproc_list.head = nh;
+      coproc_list.tail = nt;
+      if (coproc_list.ncoproc == 1)
+       coproc_list.tail = coproc_list.head;            /* just to make sure */  
+    }
+}
+
+/* Clear out the list of saved statuses */
+static void
+cpl_flush ()
+{
+  struct cpelement *cpe, *p;
+
+  for (cpe = coproc_list.head; cpe; )
+    {
+      p = cpe;
+      cpe = cpe->next;
+
+      coproc_dispose (p->coproc);
+      cpe_dispose (p);
+    }
+
+  coproc_list.head = coproc_list.tail = 0;
+  coproc_list.ncoproc = 0;
+}
+
+static void
+cpl_closeall ()
+{
+  struct cpelement *cpe;
+
+  for (cpe = coproc_list.head; cpe; cpe = cpe->next)
+    coproc_close (cpe->coproc);
+}
+
+static void
+cpl_fdchk (fd)
+     int fd;
+{
+  struct cpelement *cpe;
+
+  for (cpe = coproc_list.head; cpe; cpe = cpe->next)
+    coproc_checkfd (cpe->coproc, fd);
+}
+
+/* Search for PID in the list of coprocs; return the cpelement struct if
+   found.  If not found, return NULL. */
+static struct cpelement *
+cpl_search (pid)
+     pid_t pid;
+{
+  struct cpelement *cpe;
+
+  for (cpe = coproc_list.head ; cpe; cpe = cpe->next)
+    if (cpe->coproc->c_pid == pid)
+      return cpe;
+  return (struct cpelement *)NULL;
+}
+
+/* Search for the coproc named NAME in the list of coprocs; return the
+   cpelement struct if found.  If not found, return NULL. */
+static struct cpelement *
+cpl_searchbyname (name)
+     const char *name;
+{
+  struct cpelement *cp;
+
+  for (cp = coproc_list.head ; cp; cp = cp->next)
+    if (STREQ (cp->coproc->c_name, name))
+      return cp;
+  return (struct cpelement *)NULL;
+}
+
+#if 0
+static void
+cpl_prune ()
+{
+  struct cpelement *cp;
+
+  while (coproc_list.head && coproc_list.ncoproc > COPROC_MAX)
+    {
+      cp = coproc_list.head;
+      coproc_list.head = coproc_list.head->next;
+      coproc_dispose (cp->coproc);
+      cpe_dispose (cp);
+      coproc_list.ncoproc--;
+    }
+}
+#endif
+
+/* These currently use a single global "shell coproc" but are written in a
+   way to not preclude additional coprocs later (using the list management
+   package above). */
+
+struct coproc *
+getcoprocbypid (pid)
+     pid_t pid;
+{
+#if MULTIPLE_COPROCS
+  struct cpelement *p;
+
+  p = cpl_search (pid);
+  return (p ? p->coproc : 0);
+#else
+  return (pid == sh_coproc.c_pid ? &sh_coproc : 0);
+#endif
+}
+
+struct coproc *
+getcoprocbyname (name)
+     const char *name;
+{
+#if MULTIPLE_COPROCS
+  struct cpelement *p;
+
+  p = cpl_searchbyname (name);
+  return (p ? p->coproc : 0);
+#else
+  return ((sh_coproc.c_name && STREQ (sh_coproc.c_name, name)) ? &sh_coproc : 0);
+#endif
+}
+
+void
+coproc_init (cp)
+     struct coproc *cp;
+{
+  cp->c_name = 0;
+  cp->c_pid = NO_PID;
+  cp->c_rfd = cp->c_wfd = -1;
+  cp->c_rsave = cp->c_wsave = -1;
+  cp->c_flags = cp->c_status = cp->c_lock = 0;
+}
+
+struct coproc *
+coproc_alloc (name, pid)
+     char *name;
+     pid_t pid;
+{
+  struct coproc *cp;
+
+#if MULTIPLE_COPROCS
+  cp = (struct coproc *)xmalloc (sizeof (struct coproc));
+#else
+  cp = &sh_coproc;
+#endif
+  coproc_init (cp);
+  cp->c_lock = 2;
+
+  cp->c_pid = pid;
+  cp->c_name = savestring (name);
+#if MULTIPLE_COPROCS
+  cpl_add (cp);
+#endif
+  cp->c_lock = 0;
+  return (cp);
+}
+
+static void
+coproc_free (cp)
+     struct coproc *cp;
+{
+  free (cp);
+}
+
+void
+coproc_dispose (cp)
+     struct coproc *cp;
+{
+  sigset_t set, oset;
+
+  if (cp == 0)
+    return;
+
+  BLOCK_SIGNAL (SIGCHLD, set, oset);
+  cp->c_lock = 3;
+  coproc_unsetvars (cp);
+  FREE (cp->c_name);
+  coproc_close (cp);
+#if MULTIPLE_COPROCS
+  coproc_free (cp);
+#else
+  coproc_init (cp);
+  cp->c_lock = 0;
+#endif
+  UNBLOCK_SIGNAL (oset);
+}
+
+/* Placeholder for now.  Will require changes for multiple coprocs */
+void
+coproc_flush ()
+{
+#if MULTIPLE_COPROCS
+  cpl_flush ();
+#else
+  coproc_dispose (&sh_coproc);
+#endif
+}
+
+void
+coproc_close (cp)
+     struct coproc *cp;
+{
+  if (cp->c_rfd >= 0)
+    {
+      close (cp->c_rfd);
+      cp->c_rfd = -1;
+    }
+  if (cp->c_wfd >= 0)
+    {
+      close (cp->c_wfd);
+      cp->c_wfd = -1;
+    }
+  cp->c_rsave = cp->c_wsave = -1;
+}
+
+void
+coproc_closeall ()
+{
+#if MULTIPLE_COPROCS
+  cpl_closeall ();
+#else
+  coproc_close (&sh_coproc);   /* XXX - will require changes for multiple coprocs */
+#endif
+}
+
+void
+coproc_reap ()
+{
+#if MULTIPLE_COPROCS
+  cpl_reap ();
+#else
+  struct coproc *cp;
+
+  cp = &sh_coproc;             /* XXX - will require changes for multiple coprocs */
+  if (cp && (cp->c_flags & COPROC_DEAD))
+    coproc_dispose (cp);
+#endif
+}
+
+void
+coproc_rclose (cp, fd)
+     struct coproc *cp;
+     int fd;
+{
+  if (cp->c_rfd >= 0 && cp->c_rfd == fd)
+    {
+      close (cp->c_rfd);
+      cp->c_rfd = -1;
+    }
+}
+
+void
+coproc_wclose (cp, fd)
+     struct coproc *cp;
+     int fd;
+{
+  if (cp->c_wfd >= 0 && cp->c_wfd == fd)
+    {
+      close (cp->c_wfd);
+      cp->c_wfd = -1;
+    }
+}
+
+void
+coproc_checkfd (cp, fd)
+     struct coproc *cp;
+     int fd;
+{
+  int update;
+
+  update = 0;
+  if (cp->c_rfd >= 0 && cp->c_rfd == fd)
+    update = cp->c_rfd = -1;
+  if (cp->c_wfd >= 0 && cp->c_wfd == fd)
+    update = cp->c_wfd = -1;
+  if (update)
+    coproc_setvars (cp);
+}
+
+void
+coproc_fdchk (fd)
+     int fd;
+{
+#if MULTIPLE_COPROCS
+  cpl_fdchk (fd);
+#else
+  coproc_checkfd (&sh_coproc, fd);
+#endif
+}
+
+void
+coproc_fdclose (cp, fd)
+     struct coproc *cp;
+     int fd;
+{
+  coproc_rclose (cp, fd);
+  coproc_wclose (cp, fd);
+  coproc_setvars (cp);
+}
+
+void
+coproc_fdsave (cp)
+     struct coproc *cp;
+{
+  cp->c_rsave = cp->c_rfd;
+  cp->c_wsave = cp->c_wfd;
+}
+
+void
+coproc_fdrestore (cp)
+     struct coproc *cp;
+{
+  cp->c_rfd = cp->c_rsave;
+  cp->c_wfd = cp->c_wsave;
+}
+
+void
+coproc_pidchk (pid, status)
+     pid_t pid;
+{
+  struct coproc *cp;
+
+#if MULTIPLE_COPROCS
+  struct cpelement *cpe;
+
+  cpe = cpl_delete (pid);
+  cp = cpe ? cpe->coproc : 0;
+#else
+  cp = getcoprocbypid (pid);
+#endif
+  if (cp)
+    {
+      cp->c_lock = 4;
+      cp->c_status = status;
+      cp->c_flags |= COPROC_DEAD;
+      cp->c_flags &= ~COPROC_RUNNING;
+      /* Don't dispose the coproc or unset the COPROC_XXX variables because
+        this is executed in a signal handler context.  Wait until coproc_reap
+        takes care of it. */
+      cp->c_lock = 0;
+    }
+}
+
+void
+coproc_setvars (cp)
+     struct coproc *cp;
+{
+  SHELL_VAR *v;
+  char *namevar, *t;
+  int l;
+#if defined (ARRAY_VARS)
+  arrayind_t ind;
+#endif
+
+  if (cp->c_name == 0)
+    return;
+
+  l = strlen (cp->c_name);
+  namevar = xmalloc (l + 16);
+
+#if defined (ARRAY_VARS)
+  v = find_variable (cp->c_name);
+  if (v == 0)
+    v = make_new_array_variable (cp->c_name);
+  if (array_p (v) == 0)
+    v = convert_var_to_array (v);
+
+  t = itos (cp->c_rfd);
+  ind = 0;
+  v = bind_array_variable (cp->c_name, ind, t, 0);
+  free (t);
+
+  t = itos (cp->c_wfd);
+  ind = 1;
+  bind_array_variable (cp->c_name, ind, t, 0);
+  free (t);
+#else
+  sprintf (namevar, "%s_READ", cp->c_name);
+  t = itos (cp->c_rfd);
+  bind_variable (namevar, t, 0);
+  free (t);
+  sprintf (namevar, "%s_WRITE", cp->c_name);
+  t = itos (cp->c_wfd);
+  bind_variable (namevar, t, 0);
+  free (t);
+#endif
+
+  sprintf (namevar, "%s_PID", cp->c_name);
+  t = itos (cp->c_pid);
+  bind_variable (namevar, t, 0);
+  free (t);
+
+  free (namevar);
+}
+
+void
+coproc_unsetvars (cp)
+     struct coproc *cp;
+{
+  int l;
+  char *namevar;
+
+  if (cp->c_name == 0)
+    return;
+
+  l = strlen (cp->c_name);
+  namevar = xmalloc (l + 16);
+
+  sprintf (namevar, "%s_PID", cp->c_name);
+  unbind_variable (namevar);  
+
+#if defined (ARRAY_VARS)
+  unbind_variable (cp->c_name);
+#else
+  sprintf (namevar, "%s_READ", cp->c_name);
+  unbind_variable (namevar);
+  sprintf (namevar, "%s_WRITE", cp->c_name);
+  unbind_variable (namevar);
+#endif  
+
+  free (namevar);
+}
+
+static int
+execute_coproc (command, pipe_in, pipe_out, fds_to_close)
+     COMMAND *command;
+     int pipe_in, pipe_out;
+     struct fd_bitmap *fds_to_close;
+{
+  int rpipe[2], wpipe[2], estat, invert;
+  pid_t coproc_pid;
+  Coproc *cp;
+  char *tcmd;
+  sigset_t set, oset;
+
+  /* XXX -- can be removed after changes to handle multiple coprocs */
+#if !MULTIPLE_COPROCS
+  if (sh_coproc.c_pid != NO_PID)
+    internal_warning ("execute_coproc: coproc [%d:%s] still exists", sh_coproc.c_pid, sh_coproc.c_name);
+  coproc_init (&sh_coproc);
+#endif
+
+  invert = (command->flags & CMD_INVERT_RETURN) != 0;
+  command_string_index = 0;
+  tcmd = make_command_string (command);
+
+  sh_openpipe ((int *)&rpipe); /* 0 = parent read, 1 = child write */
+  sh_openpipe ((int *)&wpipe); /* 0 = child read, 1 = parent write */
+
+  BLOCK_SIGNAL (SIGCHLD, set, oset);
+
+  coproc_pid = make_child (savestring (tcmd), 1);
+
+  if (coproc_pid == 0)
+    {
+      close (rpipe[0]);
+      close (wpipe[1]);
+
+      UNBLOCK_SIGNAL (oset);
+      estat = execute_in_subshell (command, 1, wpipe[0], rpipe[1], fds_to_close);
+
+      fflush (stdout);
+      fflush (stderr);
+
+      exit (estat);
+    }
+
+  close (rpipe[1]);
+  close (wpipe[0]);
+
+  /* XXX - possibly run Coproc->name through word expansion? */
+  cp = coproc_alloc (command->value.Coproc->name, coproc_pid);
+  cp->c_rfd = rpipe[0];
+  cp->c_wfd = wpipe[1];
+
+  SET_CLOSE_ON_EXEC (cp->c_rfd);
+  SET_CLOSE_ON_EXEC (cp->c_wfd);
+
+  coproc_setvars (cp);
+
+  UNBLOCK_SIGNAL (oset);
+
+#if 0
+  itrace ("execute_coproc: [%d] %s", coproc_pid, the_printed_command);
+#endif
+
+  close_pipes (pipe_in, pipe_out);
+#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
+  unlink_fifo_list ();
+#endif
+  stop_pipeline (1, (COMMAND *)NULL);
+  DESCRIBE_PID (coproc_pid);
+  run_pending_traps ();
+
+  return (invert ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
+}
+#endif
+
+static void
+restore_stdin (s)
+     int s;
+{
+  dup2 (s, 0);
+  close (s);
+}
+
+/* Catch-all cleanup function for lastpipe code for unwind-protects */
+static void
+lastpipe_cleanup (s)
+     int s;
+{
+  unfreeze_jobs_list ();
+}
+
+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;
+  int lstdin, lastpipe_flag, lastpipe_jid;
+  COMMAND *cmd;
+  struct fd_bitmap *fd_bitmap;
+  pid_t lastpid;
+
+#if defined (JOB_CONTROL)
+  sigset_t set, oset;
+  BLOCK_CHILD (set, oset);
+#endif /* JOB_CONTROL */
+
+  ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
+
+  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 ();
+         UNBLOCK_CHILD (oset);
+#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 ();
+         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);
+#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];
@@ -1312,11 +2356,43 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
       cmd = cmd->value.Connection->second;
     }
 
+  lastpid = last_made_pid;
+
   /* Now execute the rightmost command in the pipeline.  */
   if (ignore_return && cmd)
     cmd->flags |= CMD_IGNORE_RETURN;
+
+  lastpipe_flag = 0;
+
+  begin_unwind_frame ("lastpipe-exec");
+  lstdin = -1;
+  /* If the `lastpipe' option is set with shopt, and job control is not
+     enabled, execute the last element of non-async pipelines in the
+     current shell environment. */
+  if (lastpipe_opt && job_control == 0 && asynchronous == 0 && pipe_out == NO_PIPE && prev > 0)
+    {
+      lstdin = move_to_high_fd (0, 1, -1);
+      if (lstdin > 0)
+       {
+         do_piping (prev, pipe_out);
+         prev = NO_PIPE;
+         add_unwind_protect (restore_stdin, lstdin);
+         lastpipe_flag = 1;
+         freeze_jobs_list ();
+         lastpipe_jid = stop_pipeline (0, (COMMAND *)NULL);    /* XXX */
+         add_unwind_protect (lastpipe_cleanup, lastpipe_jid);
+       }
+      if (cmd)
+       cmd->flags |= CMD_LASTPIPE;
+    }    
+  if (prev >= 0)
+    add_unwind_protect (close, prev);
+
   exec_result = execute_command_internal (cmd, asynchronous, prev, pipe_out, fds_to_close);
 
+  if (lstdin > 0)
+    restore_stdin (lstdin);
+
   if (prev >= 0)
     close (prev);
 
@@ -1324,6 +2400,22 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
   UNBLOCK_CHILD (oset);
 #endif
 
+  QUIT;
+
+  if (lastpipe_flag)
+    {
+#if defined (JOB_CONTROL)
+      append_process (savestring (the_printed_command), dollar_dollar_pid, exec_result, lastpipe_jid);
+#endif
+      lstdin = wait_for (lastpid);
+#if defined (JOB_CONTROL)
+      exec_result = job_exit_status (lastpipe_jid);
+#endif
+      unfreeze_jobs_list ();
+    }
+
+  discard_unwind_frame ("lastpipe-exec");
+
   return (exec_result);
 }
 
@@ -1333,12 +2425,9 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
      int asynchronous, pipe_in, pipe_out;
      struct fd_bitmap *fds_to_close;
 {
-#if 0
-  REDIRECT *tr, *tl;
-#endif
-  REDIRECT *rp;
   COMMAND *tc, *second;
-  int ignore_return, exec_result;
+  int ignore_return, exec_result, was_error_trap, invert;
+  volatile int save_line_number;
 
   ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
 
@@ -1350,53 +2439,26 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
       if (tc == 0)
        return (EXECUTION_SUCCESS);
 
-      rp = tc->redirects;
-
       if (ignore_return)
        tc->flags |= CMD_IGNORE_RETURN;
       tc->flags |= CMD_AMPERSAND;
 
-      /* 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 this shell was compiled without job control support,
+        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)
+      if ((subshell_environment || !job_control) && !stdin_redir)
 #else
       if (!stdin_redir)
 #endif /* JOB_CONTROL */
-       {
-#if 0
-         rd.filename = make_bare_word ("/dev/null");
-         tr = make_redirection (0, r_inputa_direction, rd);
-         tr->next = tc->redirects;
-         tc->redirects = tr;
-#endif
-         tc->flags |= CMD_STDIN_REDIR;
-       }
+       tc->flags |= CMD_STDIN_REDIR;
 
       exec_result = execute_command_internal (tc, 1, pipe_in, pipe_out, fds_to_close);
+      QUIT;
 
       if (tc->flags & CMD_STDIN_REDIR)
-       {
-#if 0
-         /* 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
-           {
-             tl = tc->redirects;
-             tc->redirects = tc->redirects->next;
-           }
-         while (tc->redirects && tc->redirects != rp);
-
-         tl->next = (REDIRECT *)NULL;
-         dispose_redirects (tr);
-#endif
-         tc->flags &= ~CMD_STDIN_REDIR;
-       }
+       tc->flags &= ~CMD_STDIN_REDIR;
 
       second = command->value.Connection->second;
       if (second)
@@ -1418,16 +2480,40 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
          if (command->value.Connection->second)
            command->value.Connection->second->flags |= CMD_IGNORE_RETURN;
        }
+      executing_list++;
       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);
+      executing_list--;
       break;
 
     case '|':
+      was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
+      invert = (command->flags & CMD_INVERT_RETURN) != 0;
+      ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
+
+      line_number_for_err_trap = line_number;
       exec_result = execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close);
+
+      if (was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
+       {
+         last_command_exit_value = exec_result;
+         save_line_number = line_number;
+         line_number = line_number_for_err_trap;
+         run_error_trap ();
+         line_number = save_line_number;
+       }
+
+      if (ignore_return == 0 && invert == 0 && exit_immediately_on_error && exec_result != EXECUTION_SUCCESS)
+       {
+         last_command_exit_value = exec_result;
+         run_pending_traps ();
+         jump_to_top_level (ERREXIT);
+       }
+
       break;
 
     case AND_AND:
@@ -1449,6 +2535,7 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
         and the connector is OR_OR, then execute the second command,
         otherwise return. */
 
+      executing_list++;
       if (command->value.Connection->first)
        command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
 
@@ -1464,6 +2551,7 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
 
          exec_result = execute_command (command->value.Connection->second);
        }
+      executing_list--;
       break;
 
     default:
@@ -1492,17 +2580,18 @@ execute_for_command (for_command)
   register WORD_LIST *releaser, *list;
   SHELL_VAR *v;
   char *identifier;
-  int retval;
+  int retval, save_line_number;
 #if 0
   SHELL_VAR *old_value = (SHELL_VAR *)NULL; /* Remember the old value of x. */
 #endif
 
+  save_line_number = line_number;
   if (check_identifier (for_command->name, 1) == 0)
     {
       if (posixly_correct && interactive_shell == 0)
        {
-         last_command_exit_value = EX_USAGE;
-         jump_to_top_level (EXITPROG);
+         last_command_exit_value = EX_BADUSAGE;
+         jump_to_top_level (ERREXIT);
        }
       return (EXECUTION_FAILURE);
     }
@@ -1510,6 +2599,7 @@ execute_for_command (for_command)
   loop_level++;
   identifier = for_command->name->word;
 
+  line_number = for_command->line;     /* for expansion error messages */
   list = releaser = expand_words_no_vars (for_command->map_list);
 
   begin_unwind_frame ("for");
@@ -1530,10 +2620,44 @@ execute_for_command (for_command)
   for (retval = EXECUTION_SUCCESS; list; list = list->next)
     {
       QUIT;
+
+      line_number = for_command->line;
+
+      /* Remember what this command looks like, for debugger. */
+      command_string_index = 0;
+      print_for_command_head (for_command);
+
+      if (echo_command_at_execute)
+       xtrace_print_for_command_head (for_command);
+
+      /* Save this command unless it's a trap command and we're not running
+        a debug trap. */
+      if (signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
+       {
+         FREE (the_printed_command_except_trap);
+         the_printed_command_except_trap = savestring (the_printed_command);
+       }
+
+      retval = run_debug_trap ();
+#if defined (DEBUGGER)
+      /* In debugging mode, if the DEBUG trap returns a non-zero status, we
+        skip the command. */
+      if (debugging_mode && retval != EXECUTION_SUCCESS)
+        continue;
+#endif
+
       this_command_name = (char *)NULL;
-      v = bind_variable (identifier, list->word->word);
+      /* XXX - special ksh93 for command index variable handling */
+      v = find_variable_last_nameref (identifier);
+      if (v && nameref_p (v))
+        {
+          v = bind_variable_value (v, list->word->word, 0);
+        }
+      else
+        v = bind_variable (identifier, list->word->word, 0);
       if (readonly_p (v) || noassign_p (v))
        {
+         line_number = save_line_number;
          if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
            {
              last_command_exit_value = EXECUTION_FAILURE;
@@ -1541,7 +2665,8 @@ execute_for_command (for_command)
            }
          else
            {
-             run_unwind_frame ("for");
+             dispose_words (releaser);
+             discard_unwind_frame ("for");
              loop_level--;
              return (EXECUTION_FAILURE);
            }
@@ -1565,17 +2690,18 @@ execute_for_command (for_command)
     }
 
   loop_level--;
+  line_number = save_line_number;
 
 #if 0
   if (lexical_scoping)
     {
       if (!old_value)
-       makunbound (identifier, shell_variables);
+        unbind_variable (identifier);
       else
        {
          SHELL_VAR *new_value;
 
-         new_value = bind_variable (identifier, value_cell(old_value));
+         new_value = bind_variable (identifier, value_cell(old_value), 0);
          new_value->attributes = old_value->attributes;
          dispose_variable (old_value);
        }
@@ -1603,19 +2729,53 @@ execute_for_command (for_command)
                eval \(\( step \)\)
        done
 */
-static long
+static intmax_t
 eval_arith_for_expr (l, okp)
      WORD_LIST *l;
      int *okp;
 {
   WORD_LIST *new;
-  long expresult;
+  intmax_t expresult;
+  int r;
 
   new = expand_words_no_vars (l);
-  if (echo_command_at_execute)
-    xtrace_print_arith_cmd (new);
-  expresult = evalexp (new->word->word, okp);
-  dispose_words (new);
+  if (new)
+    {
+      if (echo_command_at_execute)
+       xtrace_print_arith_cmd (new);
+      this_command_name = "((";                /* )) for expression error messages */
+
+      command_string_index = 0;
+      print_arith_command (new);
+      if (signal_in_progress (DEBUG_TRAP) == 0)
+       {
+         FREE (the_printed_command_except_trap);
+         the_printed_command_except_trap = savestring (the_printed_command);
+       }
+
+      r = run_debug_trap ();
+      /* In debugging mode, if the DEBUG trap returns a non-zero status, we
+        skip the command. */
+#if defined (DEBUGGER)
+      if (debugging_mode == 0 || r == EXECUTION_SUCCESS)
+       expresult = evalexp (new->word->word, okp);
+      else
+       {
+         expresult = 0;
+         if (okp)
+           *okp = 1;
+       }
+#else
+      expresult = evalexp (new->word->word, okp);
+#endif
+      dispose_words (new);
+    }
+  else
+    {
+      expresult = 0;
+      if (okp)
+       *okp = 1;
+    }
   return (expresult);
 }
 
@@ -1623,29 +2783,40 @@ static int
 execute_arith_for_command (arith_for_command)
      ARITH_FOR_COM *arith_for_command;
 {
-  long expresult;
-  int expok, result, body_status;
+  intmax_t expresult;
+  int expok, body_status, arith_lineno, save_lineno;
 
   body_status = EXECUTION_SUCCESS;
   loop_level++;
+  save_lineno = line_number;
 
   if (arith_for_command->flags & CMD_IGNORE_RETURN)
     arith_for_command->action->flags |= CMD_IGNORE_RETURN;
 
   this_command_name = "((";    /* )) for expression error messages */
 
-  if (variable_context)
-    line_number = arith_for_command->line - function_line_number;
+  /* save the starting line number of the command so we can reset
+     line_number before executing each expression -- for $LINENO
+     and the DEBUG trap. */
+  line_number = arith_lineno = arith_for_command->line;
+  if (variable_context && interactive_shell)
+    line_number -= function_line_number;
 
   /* Evaluate the initialization expression. */
   expresult = eval_arith_for_expr (arith_for_command->init, &expok);
   if (expok == 0)
-    return (EXECUTION_FAILURE);
+    {
+      line_number = save_lineno;
+      return (EXECUTION_FAILURE);
+    }
 
   while (1)
     {
       /* Evaluate the test expression. */
+      line_number = arith_lineno;
       expresult = eval_arith_for_expr (arith_for_command->test, &expok);
+      line_number = save_lineno;
+
       if (expok == 0)
        {
          body_status = EXECUTION_FAILURE;
@@ -1675,7 +2846,10 @@ execute_arith_for_command (arith_for_command)
        }
 
       /* Evaluate the step expression. */
+      line_number = arith_lineno;
       expresult = eval_arith_for_expr (arith_for_command->step, &expok);
+      line_number = save_lineno;
+
       if (expok == 0)
        {
          body_status = EXECUTION_FAILURE;
@@ -1684,6 +2858,8 @@ execute_arith_for_command (arith_for_command)
     }
 
   loop_level--;
+  line_number = save_lineno;
+
   return (body_status);
 }
 #endif
@@ -1703,6 +2879,29 @@ static int LINES, COLS, tabsize;
                                                 : ((s < 100000) ? 5 \
                                                                : 6)))))
 
+static int
+displen (s)
+     const char *s;
+{
+#if defined (HANDLE_MULTIBYTE)
+  wchar_t *wcstr;
+  size_t slen;
+  int wclen;
+
+  wcstr = 0;
+  slen = mbstowcs (wcstr, s, 0);
+  if (slen == -1)
+    slen = 0;
+  wcstr = (wchar_t *)xmalloc (sizeof (wchar_t) * (slen + 1));
+  mbstowcs (wcstr, s, slen + 1);
+  wclen = wcswidth (wcstr, slen);
+  free (wcstr);
+  return (wclen < 0 ? STRLEN(s) : wclen);
+#else
+  return (STRLEN (s));
+#endif
+}
+
 static int
 print_index_and_element (len, ind, list)
       int len, ind;
@@ -1715,8 +2914,10 @@ print_index_and_element (len, ind, list)
     return (0);
   for (i = ind, l = list; l && --i; l = l->next)
     ;
+  if (l == 0)          /* don't think this can happen */
+    return (0);
   fprintf (stderr, "%*d%s%s", len, ind, RP_SPACE, l->word->word);
-  return (STRLEN (l->word->word));
+  return (displen (l->word->word));
 }
 
 static void
@@ -1792,18 +2993,21 @@ print_select_list (list, list_len, max_elem_len, indices_len)
    is read, return a null string.  If a blank line is entered, or an invalid
    number is entered, the loop is executed again. */
 static char *
-select_query (list, list_len, prompt)
+select_query (list, list_len, prompt, print_menu)
      WORD_LIST *list;
      int list_len;
      char *prompt;
+     int print_menu;
 {
   int max_elem_len, indices_len, len;
-  long reply;
+  intmax_t reply;
   WORD_LIST *l;
   char *repl_string, *t;
 
+#if 0
   t = get_string_value ("LINES");
   LINES = (t && *t) ? atoi (t) : 24;
+#endif
   t = get_string_value ("COLUMNS");
   COLS =  (t && *t) ? atoi (t) : 80;
 
@@ -1819,7 +3023,7 @@ select_query (list, list_len, prompt)
   max_elem_len = 0;
   for (l = list; l; l = l->next)
     {
-      len = STRLEN (l->word->word);
+      len = displen (l->word->word);
       if (len > max_elem_len)
        max_elem_len = len;
     }
@@ -1828,19 +3032,23 @@ select_query (list, list_len, prompt)
 
   while (1)
     {
-      print_select_list (list, list_len, max_elem_len, indices_len);
+      if (print_menu)
+       print_select_list (list, list_len, max_elem_len, indices_len);
       fprintf (stderr, "%s", prompt);
       fflush (stderr);
       QUIT;
 
-      if (read_builtin ((WORD_LIST *)NULL) == EXECUTION_FAILURE)
+      if (read_builtin ((WORD_LIST *)NULL) != EXECUTION_SUCCESS)
        {
          putchar ('\n');
          return ((char *)NULL);
        }
       repl_string = get_string_value ("REPLY");
       if (*repl_string == 0)
-       continue;
+       {
+         print_menu = 1;
+         continue;
+       }
       if (legal_number (repl_string, &reply) == 0)
        return "";
       if (reply < 1 || reply > list_len)
@@ -1848,7 +3056,7 @@ select_query (list, list_len, prompt)
 
       for (l = list; l && --reply; l = l->next)
        ;
-      return (l->word->word);
+      return (l->word->word);          /* XXX - can't be null? */
     }
 }
 
@@ -1863,11 +3071,38 @@ execute_select_command (select_command)
   WORD_LIST *releaser, *list;
   SHELL_VAR *v;
   char *identifier, *ps3_prompt, *selection;
-  int retval, list_len, return_val;
+  int retval, list_len, show_menu, save_line_number;
 
   if (check_identifier (select_command->name, 1) == 0)
     return (EXECUTION_FAILURE);
 
+  save_line_number = line_number;
+  line_number = select_command->line;
+
+  command_string_index = 0;
+  print_select_command_head (select_command);
+
+  if (echo_command_at_execute)
+    xtrace_print_select_command_head (select_command);
+
+#if 0
+  if (signal_in_progress (DEBUG_TRAP) == 0 && (this_command_name == 0 || (STREQ (this_command_name, "trap") == 0)))
+#else
+  if (signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
+#endif
+    {
+      FREE (the_printed_command_except_trap);
+      the_printed_command_except_trap = savestring (the_printed_command);
+    }
+
+  retval = run_debug_trap ();
+#if defined (DEBUGGER)
+  /* In debugging mode, if the DEBUG trap returns a non-zero status, we
+     skip the command. */
+  if (debugging_mode && retval != EXECUTION_SUCCESS)
+    return (EXECUTION_SUCCESS);
+#endif
+
   loop_level++;
   identifier = select_command->name->word;
 
@@ -1879,6 +3114,7 @@ execute_select_command (select_command)
     {
       if (list)
        dispose_words (list);
+      line_number = save_line_number;
       return (EXECUTION_SUCCESS);
     }
 
@@ -1889,24 +3125,27 @@ execute_select_command (select_command)
     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++;
+  show_menu = 1;
 
   while (1)
     {
+      line_number = select_command->line;
       ps3_prompt = get_string_value ("PS3");
       if (ps3_prompt == 0)
        ps3_prompt = "#? ";
 
       QUIT;
-      selection = select_query (list, list_len, ps3_prompt);
+      selection = select_query (list, list_len, ps3_prompt, show_menu);
       QUIT;
       if (selection == 0)
-       break;
+       {
+         /* select_query returns EXECUTION_FAILURE if the read builtin
+            fails, so we want to return failure in this case. */
+         retval = EXECUTION_FAILURE;
+         break;
+       }
 
-      v = bind_variable (identifier, selection);
+      v = bind_variable (identifier, selection, 0);
       if (readonly_p (v) || noassign_p (v))
        {
          if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
@@ -1916,20 +3155,15 @@ execute_select_command (select_command)
            }
          else
            {
-             run_unwind_frame ("select");
+             dispose_words (releaser);
+             discard_unwind_frame ("select");
+             loop_level--;
+             line_number = save_line_number;
              return (EXECUTION_FAILURE);
            }
        }
 
-      return_val = setjmp (return_catch);
-
-      if (return_val)
-       {
-         retval = return_catch_value;
-         break;
-       }
-      else
-       retval = execute_command (select_command->action);
+      retval = execute_command (select_command->action);
 
       REAP ();
       QUIT;
@@ -1946,11 +3180,20 @@ execute_select_command (select_command)
          if (continuing)
            break;
        }
+
+#if defined (KSH_COMPATIBLE_SELECT)
+      show_menu = 0;
+      selection = get_string_value ("REPLY");
+      if (selection && *selection == '\0')
+        show_menu = 1;
+#endif
     }
 
   loop_level--;
+  line_number = save_line_number;
 
-  run_unwind_frame ("select");
+  dispose_words (releaser);
+  discard_unwind_frame ("select");
   return (retval);
 }
 #endif /* SELECT_COMMAND */
@@ -1967,15 +3210,37 @@ execute_case_command (case_command)
   WORD_LIST *wlist, *es;
   PATTERN_LIST *clauses;
   char *word, *pattern;
-  int retval, match, ignore_return;
+  int retval, match, ignore_return, save_line_number;
+
+  save_line_number = line_number;
+  line_number = case_command->line;
+
+  command_string_index = 0;
+  print_case_command_head (case_command);
+
+  if (echo_command_at_execute)
+    xtrace_print_case_command_head (case_command);
+
+#if 0
+  if (signal_in_progress (DEBUG_TRAP) == 0 && (this_command_name == 0 || (STREQ (this_command_name, "trap") == 0)))
+#else
+  if (signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
+#endif
+    {
+      FREE (the_printed_command_except_trap);
+      the_printed_command_except_trap = savestring (the_printed_command);
+    }
 
-  /* Posix.2 specifies that the WORD is tilde expanded. */
-  if (member ('~', case_command->word->word))
+  retval = run_debug_trap();
+#if defined (DEBUGGER)
+  /* In debugging mode, if the DEBUG trap returns a non-zero status, we
+     skip the command. */
+  if (debugging_mode && retval != EXECUTION_SUCCESS)
     {
-      word = bash_tilde_expand (case_command->word->word);
-      free (case_command->word->word);
-      case_command->word->word = word;
+      line_number = save_line_number;
+      return (EXECUTION_SUCCESS);
     }
+#endif
 
   wlist = expand_word_unsplit (case_command->word, 0);
   word = wlist ? string_list (wlist) : savestring ("");
@@ -1994,39 +3259,37 @@ execute_case_command (case_command)
       QUIT;
       for (list = clauses->patterns; list; list = list->next)
        {
-         /* Posix.2 specifies to tilde expand each member of the pattern
-            list. */
-         if (member ('~', list->word->word))
-           {
-             pattern = bash_tilde_expand (list->word->word);
-             free (list->word->word);
-             list->word->word = pattern;
-           }
-
          es = expand_word_leave_quoted (list->word, 0);
 
          if (es && es->word && es->word->word && *(es->word->word))
            pattern = quote_string_for_globbing (es->word->word, QGLOB_CVTNULL);
          else
            {
-             pattern = xmalloc (1);
+             pattern = (char *)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
+            Posix.2, section 3.9.4.3), the strmatch () call must be able
             to recognize backslashes as escape characters. */
-         match = fnmatch (pattern, word, FNMATCH_EXTFLAG) != FNM_NOMATCH;
+         match = strmatch (pattern, word, FNMATCH_EXTFLAG|FNMATCH_IGNCASE) != FNM_NOMATCH;
          free (pattern);
 
          dispose_words (es);
 
          if (match)
            {
-             if (clauses->action && ignore_return)
-               clauses->action->flags |= CMD_IGNORE_RETURN;
-             retval = execute_command (clauses->action);
-             EXIT_CASE ();
+             do
+               {
+                 if (clauses->action && ignore_return)
+                   clauses->action->flags |= CMD_IGNORE_RETURN;
+                 retval = execute_command (clauses->action);
+               }
+             while ((clauses->flags & CASEPAT_FALLTHROUGH) && (clauses = clauses->next));
+             if (clauses == 0 || (clauses->flags & CASEPAT_TESTNEXT) == 0)
+               EXIT_CASE ();
+             else
+               break;
            }
 
          QUIT;
@@ -2036,6 +3299,7 @@ execute_case_command (case_command)
 exit_case_command:
   free (word);
   discard_unwind_frame ("case");
+  line_number = save_line_number;
   return (retval);
 }
 
@@ -2084,10 +3348,23 @@ execute_while_or_until (while_command, type)
       return_value = execute_command (while_command->test);
       REAP ();
 
+      /* Need to handle `break' in the test when we would break out of the
+         loop.  The job control code will set `breaking' to loop_level
+         when a job in a loop is stopped with SIGTSTP.  If the stopped job
+         is in the loop test, `breaking' will not be reset unless we do
+         this, and the shell will cease to execute commands. */
       if (type == CMD_WHILE && return_value != EXECUTION_SUCCESS)
-       break;
+       {
+         if (breaking)
+           breaking--;
+         break;
+       }
       if (type == CMD_UNTIL && return_value == EXECUTION_SUCCESS)
-       break;
+       {
+         if (breaking)
+           breaking--;
+         break;
+       }
 
       QUIT;
       body_status = execute_command (while_command->action);
@@ -2118,10 +3395,12 @@ static int
 execute_if_command (if_command)
      IF_COM *if_command;
 {
-  int return_value;
+  int return_value, save_line_number;
 
+  save_line_number = line_number;
   if_command->test->flags |= CMD_IGNORE_RETURN;
   return_value = execute_command (if_command->test);
+  line_number = save_line_number;
 
   if (return_value == EXECUTION_SUCCESS)
     {
@@ -2148,47 +3427,97 @@ static int
 execute_arith_command (arith_command)
      ARITH_COM *arith_command;
 {
-  int result, expok, expresult;
-  WORD_LIST *new, *p, *printit;
-  WORD_DESC *w;
+  int expok, save_line_number, retval;
+  intmax_t expresult;
+  WORD_LIST *new;
+  char *exp;
 
-  result = 0;
+  expresult = 0;
 
+  save_line_number = line_number;
   this_command_name = "((";    /* )) */
+  line_number = arith_command->line;
   /* If we're in a function, update the line number information. */
-  if (variable_context)
-    line_number = arith_command->line - function_line_number;
+  if (variable_context && interactive_shell)
+    line_number -= function_line_number;
+
+  command_string_index = 0;
+  print_arith_command (arith_command->exp);
+
+  if (signal_in_progress (DEBUG_TRAP) == 0)
+    {
+      FREE (the_printed_command_except_trap);
+      the_printed_command_except_trap = savestring (the_printed_command);
+    }
+
+  /* Run the debug trap before each arithmetic command, but do it after we
+     update the line number information and before we expand the various
+     words in the expression. */
+  retval = run_debug_trap ();
+#if defined (DEBUGGER)
+  /* In debugging mode, if the DEBUG trap returns a non-zero status, we
+     skip the command. */
+  if (debugging_mode && retval != EXECUTION_SUCCESS)
+    {
+      line_number = save_line_number;
+      return (EXECUTION_SUCCESS);
+    }
+#endif
 
-  new = expand_words (arith_command->exp);
+  new = expand_words_no_vars (arith_command->exp);
 
   /* If we're tracing, make a new word list with `((' at the front and `))'
      at the back and print it. */
   if (echo_command_at_execute)
     xtrace_print_arith_cmd (new);
 
-  result = evalexp (new->word->word, &expok);
-  dispose_words (new);
+  if (new)
+    {
+      exp = new->next ? string_list (new) : new->word->word;
+      expresult = evalexp (exp, &expok);
+      line_number = save_line_number;
+      if (exp != new->word->word)
+       free (exp);
+      dispose_words (new);
+    }
+  else
+    {
+      expresult = 0;
+      expok = 1;
+    }
 
   if (expok == 0)
     return (EXECUTION_FAILURE);
 
-  return (result == 0 ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
+  return (expresult == 0 ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
 }
 #endif /* DPAREN_ARITHMETIC */
 
 #if defined (COND_COMMAND)
 
-static char *nullstr = "";
+static char * const nullstr = "";
 
+/* XXX - can COND ever be NULL when this is called? */
 static int
 execute_cond_node (cond)
      COND_COM *cond;
 {
-  int result, invert, patmatch, flags;
-  char *arg1, *arg2, *print2;
+  int result, invert, patmatch, rmatch, mflags, ignore;
+  char *arg1, *arg2;
+#if 0
+  char *t1, *t2;
+#endif
 
   invert = (cond->flags & CMD_INVERT_RETURN);
-
+  ignore = (cond->flags & CMD_IGNORE_RETURN);
+  if (ignore)
+    {
+      if (cond->left)
+       cond->left->flags |= CMD_IGNORE_RETURN;
+      if (cond->right)
+       cond->right->flags |= CMD_IGNORE_RETURN;
+    }
+      
   if (cond->type == COND_EXPR)
     result = execute_cond_node (cond->left);
   else if (cond->type == COND_OR)
@@ -2205,7 +3534,11 @@ execute_cond_node (cond)
     }
   else if (cond->type == COND_UNARY)
     {
+      if (ignore)
+       comsub_ignore_return++;
       arg1 = cond_expand_word (cond->left->op, 0);
+      if (ignore)
+       comsub_ignore_return--;
       if (arg1 == 0)
        arg1 = nullstr;
       if (echo_command_at_execute)
@@ -2216,23 +3549,63 @@ execute_cond_node (cond)
     }
   else if (cond->type == COND_BINARY)
     {
-      patmatch = ((cond->op->word[1] == '=') && (cond->op->word[2] == '\0') &&
-                 (cond->op->word[0] == '!' || cond->op->word[0] == '=') ||
+      rmatch = 0;
+      patmatch = (((cond->op->word[1] == '=') && (cond->op->word[2] == '\0') &&
+                  (cond->op->word[0] == '!' || cond->op->word[0] == '=')) ||
                  (cond->op->word[0] == '=' && cond->op->word[1] == '\0'));
+#if defined (COND_REGEXP)
+      rmatch = (cond->op->word[0] == '=' && cond->op->word[1] == '~' &&
+               cond->op->word[2] == '\0');
+#endif
 
+      if (ignore)
+       comsub_ignore_return++;
       arg1 = cond_expand_word (cond->left->op, 0);
+      if (ignore)
+       comsub_ignore_return--;
       if (arg1 == 0)
        arg1 = nullstr;
-      arg2 = cond_expand_word (cond->right->op, patmatch);
+      if (ignore)
+       comsub_ignore_return++;
+      arg2 = cond_expand_word (cond->right->op,
+                              (rmatch && shell_compatibility_level > 31) ? 2 : (patmatch ? 1 : 0));
+      if (ignore)
+       comsub_ignore_return--;
       if (arg2 == 0)
        arg2 = nullstr;
 
       if (echo_command_at_execute)
        xtrace_print_cond_term (cond->type, invert, cond->op, arg1, arg2);
 
-      result = binary_test (cond->op->word, arg1, arg2, TEST_PATMATCH|TEST_ARITHEXP)
-                               ? EXECUTION_SUCCESS
-                               : EXECUTION_FAILURE;
+#if defined (COND_REGEXP)
+      if (rmatch)
+       {
+         mflags = SHMAT_PWARN;
+#if defined (ARRAY_VARS)
+         mflags |= SHMAT_SUBEXP;
+#endif
+
+#if 0
+         t1 = strescape(arg1);
+         t2 = strescape(arg2);
+         itrace("execute_cond_node: sh_regmatch on `%s' and `%s'", t1, t2);
+         free(t1);
+         free(t2);
+#endif
+
+         result = sh_regmatch (arg1, arg2, mflags);
+       }
+      else
+#endif /* COND_REGEXP */
+       {
+         int oe;
+         oe = extended_glob;
+         extended_glob = 1;
+         result = binary_test (cond->op->word, arg1, arg2, TEST_PATMATCH|TEST_ARITHEXP|TEST_LOCALE)
+                                 ? EXECUTION_SUCCESS
+                                 : EXECUTION_FAILURE;
+         extended_glob = oe;
+       }
       if (arg1 != nullstr)
        free (arg1);
       if (arg2 != nullstr)
@@ -2255,20 +3628,45 @@ static int
 execute_cond_command (cond_command)
      COND_COM *cond_command;
 {
-  int result;
+  int retval, save_line_number;
 
-  result = EXECUTION_SUCCESS;
+  retval = EXECUTION_SUCCESS;
+  save_line_number = line_number;
+
+  this_command_name = "[[";
+  line_number = cond_command->line;
+  /* If we're in a function, update the line number information. */
+  if (variable_context && interactive_shell)
+    line_number -= function_line_number;
+  command_string_index = 0;
+  print_cond_command (cond_command);
+
+  if (signal_in_progress (DEBUG_TRAP) == 0)
+    {
+      FREE (the_printed_command_except_trap);
+      the_printed_command_except_trap = savestring (the_printed_command);
+    }
 
-  this_command_name = "[[";
-  /* If we're in a function, update the line number information. */
-  if (variable_context)
-    line_number = cond_command->line - function_line_number;
+  /* Run the debug trap before each conditional command, but do it after we
+     update the line number information. */
+  retval = run_debug_trap ();
+#if defined (DEBUGGER)
+  /* In debugging mode, if the DEBUG trap returns a non-zero status, we
+     skip the command. */
+  if (debugging_mode && retval != EXECUTION_SUCCESS)
+    {
+      line_number = save_line_number;
+      return (EXECUTION_SUCCESS);
+    }
+#endif
 
 #if 0
   debug_print_cond_command (cond_command);
 #endif
-  last_command_exit_value = result = execute_cond_node (cond_command);  
-  return (result);
+
+  last_command_exit_value = retval = execute_cond_node (cond_command);
+  line_number = save_line_number;
+  return (retval);
 }
 #endif /* COND_COMMAND */
 
@@ -2280,7 +3678,7 @@ bind_lastarg (arg)
 
   if (arg == 0)
     arg = "";
-  var = bind_variable ("_", arg);
+  var = bind_variable ("_", arg, 0);
   VUNSETATTR (var, att_exported);
 }
 
@@ -2288,12 +3686,18 @@ bind_lastarg (arg)
    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)
+execute_null_command (redirects, pipe_in, pipe_out, async)
      REDIRECT *redirects;
      int pipe_in, pipe_out, async;
-     pid_t old_last_command_subst_pid;
 {
-  if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
+  int r;
+  int forcefork;
+  REDIRECT *rd;
+
+  for (forcefork = 0, rd = redirects; rd; rd = rd->next)
+    forcefork += rd->rflags & REDIR_VARASSIGN;
+
+  if (forcefork || 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. */
@@ -2304,9 +3708,17 @@ execute_null_command (redirects, pipe_in, pipe_out, async, old_last_command_subs
 
          do_piping (pipe_in, pipe_out);
 
-         subshell_environment = SUBSHELL_ASYNC;
+#if defined (COPROCESS_SUPPORT)
+         coproc_closeall ();
+#endif
+
+         subshell_environment = 0;
+         if (async)
+           subshell_environment |= SUBSHELL_ASYNC;
+         if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
+           subshell_environment |= SUBSHELL_PIPE;
 
-         if (do_redirections (redirects, 1, 0, 0) == 0)
+         if (do_redirections (redirects, RX_ACTIVE) == 0)
            exit (EXECUTION_SUCCESS);
          else
            exit (EXECUTION_FAILURE);
@@ -2315,7 +3727,8 @@ execute_null_command (redirects, pipe_in, pipe_out, async, old_last_command_subs
        {
          close_pipes (pipe_in, pipe_out);
 #if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
-         unlink_fifo_list ();
+         if (pipe_out == NO_PIPE)
+           unlink_fifo_list ();
 #endif
          return (EXECUTION_SUCCESS);
        }
@@ -2329,9 +3742,13 @@ execute_null_command (redirects, pipe_in, pipe_out, async, old_last_command_subs
         expanding the command or a redirection, return the value of that
         substitution.  Otherwise, return EXECUTION_SUCCESS. */
 
-      if (do_redirections (redirects, 0, 0, 0) != 0)
+      r = do_redirections (redirects, RX_ACTIVE|RX_UNDOABLE);
+      cleanup_redirects (redirection_undo_list);
+      redirection_undo_list = (REDIRECT *)NULL;
+
+      if (r != 0)
        return (EXECUTION_FAILURE);
-      else if (old_last_command_subst_pid != last_command_subst_pid)
+      else if (last_command_subst_pid != NO_PID)
        return (last_command_exit_value);
       else
        return (EXECUTION_SUCCESS);
@@ -2344,19 +3761,94 @@ static void
 fix_assignment_words (words)
      WORD_LIST *words;
 {
-  WORD_LIST *w;
+  WORD_LIST *w, *wcmd;
   struct builtin *b;
+  int assoc, global, array, integer;
 
   if (words == 0)
     return;
 
-  b = builtin_address_internal (words->word->word, 0);
-  if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
-    return;
+  b = 0;
+  assoc = global = array = integer = 0;
 
-  for (w = words; w; w = w->next)
+  /* Skip over assignment statements preceding a command name */
+  wcmd = words;
+  for (wcmd = words; wcmd; wcmd = wcmd->next)
+    if ((wcmd->word->flags & W_ASSIGNMENT) == 0)
+      break;
+
+  for (w = wcmd; w; w = w->next)
     if (w->word->flags & W_ASSIGNMENT)
-      w->word->flags |= (W_NOSPLIT|W_NOGLOB);
+      {
+       if (b == 0)
+         {
+           /* Posix (post-2008) says that `command' doesn't change whether
+              or not the builtin it shadows is a `declaration command', even
+              though it removes other special builtin properties.  In Posix
+              mode, we skip over one or more instances of `command' and
+              deal with the next word as the assignment builtin. */
+           while (posixly_correct && wcmd && wcmd->word && wcmd->word->word && STREQ (wcmd->word->word, "command"))
+             wcmd = wcmd->next;
+           b = builtin_address_internal (wcmd->word->word, 0);
+           if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
+             return;
+           else if (b && (b->flags & ASSIGNMENT_BUILTIN))
+             wcmd->word->flags |= W_ASSNBLTIN;
+         }
+       w->word->flags |= (W_NOSPLIT|W_NOGLOB|W_TILDEEXP|W_ASSIGNARG);
+#if defined (ARRAY_VARS)
+       if (assoc)
+         w->word->flags |= W_ASSIGNASSOC;
+       if (array)
+         w->word->flags |= W_ASSIGNARRAY;
+#endif
+       if (global)
+         w->word->flags |= W_ASSNGLOBAL;
+       if (integer)
+         w->word->flags |= W_ASSIGNINT;
+      }
+#if defined (ARRAY_VARS)
+    /* Note that we saw an associative array option to a builtin that takes
+       assignment statements.  This is a bit of a kludge. */
+    else if (w->word->word[0] == '-' && (strchr (w->word->word+1, 'A') || strchr (w->word->word+1, 'a') || strchr (w->word->word+1, 'g')))
+#else
+    else if (w->word->word[0] == '-' && strchr (w->word->word+1, 'g'))
+#endif
+      {
+       if (b == 0)
+         {
+           while (posixly_correct && wcmd && wcmd->word && wcmd->word->word && STREQ (wcmd->word->word, "command"))
+             wcmd = wcmd->next;
+           b = builtin_address_internal (wcmd->word->word, 0);
+           if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
+             return;
+           else if (b && (b->flags & ASSIGNMENT_BUILTIN))
+             wcmd->word->flags |= W_ASSNBLTIN;
+         }
+       if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'A'))
+         assoc = 1;
+       else if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'a'))
+         array = 1;
+       if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'g'))
+         global = 1;
+       if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'i'))
+         integer = 1;
+      }
+}
+
+/* Return 1 if the file found by searching $PATH for PATHNAME, defaulting
+   to PATHNAME, is a directory.  Used by the autocd code below. */
+static int
+is_dirname (pathname)
+     char *pathname;
+{
+  char *temp;
+  int ret;
+
+  temp = search_for_command (pathname, 0);
+  ret = (temp ? file_isdir (temp) : file_isdir (pathname));
+  free (temp);
+  return ret;
 }
 
 /* The meaty part of all the executions.  We have to start hacking the
@@ -2371,26 +3863,49 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
   WORD_LIST *words, *lastword;
   char *command_line, *lastarg, *temp;
   int first_word_quoted, result, builtin_is_special, already_forked, dofork;
-  pid_t old_last_command_subst_pid, old_last_async_pid;
-  Function *builtin;
+  pid_t old_last_async_pid;
+  sh_builtin_func_t *builtin;
   SHELL_VAR *func;
+  volatile int old_builtin, old_command_builtin;
 
   result = EXECUTION_SUCCESS;
   special_builtin_failed = builtin_is_special = 0;
   command_line = (char *)0;
 
+  QUIT;
+
   /* If we're in a function, update the line number information. */
-  if (variable_context)
-    line_number = simple_command->line - function_line_number;
+  if (variable_context && interactive_shell && sourcelevel == 0)
+    line_number -= function_line_number;
 
   /* Remember what this command line looks like at invocation. */
   command_string_index = 0;
   print_simple_command (simple_command);
 
+#if 0
+  if (signal_in_progress (DEBUG_TRAP) == 0 && (this_command_name == 0 || (STREQ (this_command_name, "trap") == 0)))
+#else
+  if (signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
+#endif
+    {
+      FREE (the_printed_command_except_trap);
+      the_printed_command_except_trap = the_printed_command ? savestring (the_printed_command) : (char *)0;
+    }
+
+  /* Run the debug trap before each simple command, but do it after we
+     update the line number information. */
+  result = run_debug_trap ();
+#if defined (DEBUGGER)
+  /* In debugging mode, if the DEBUG trap returns a non-zero status, we
+     skip the command. */
+  if (debugging_mode && result != EXECUTION_SUCCESS)
+    return (EXECUTION_SUCCESS);
+#endif
+
   first_word_quoted =
-    simple_command->words ? (simple_command->words->word->flags & W_QUOTED): 0;
+    simple_command->words ? (simple_command->words->word->flags & W_QUOTED) : 0;
 
-  old_last_command_subst_pid = last_command_subst_pid;
+  last_command_subst_pid = NO_PID;
   old_last_async_pid = last_asynchronous_pid;
 
   already_forked = dofork = 0;
@@ -2411,21 +3926,22 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
 
   if (dofork)
     {
-      /* XXX memory leak if expand_words() error causes a jump_to_top_level */
-      command_line = savestring (the_printed_command);
-
       /* Do this now, because execute_disk_command will do it anyway in the
         vast majority of cases. */
       maybe_make_export_env ();
 
-      if (make_child (command_line, async) == 0)
+      /* Don't let a DEBUG trap overwrite the command string to be saved with
+        the process/job associated with this child. */
+      if (make_child (savestring (the_printed_command_except_trap), async) == 0)
        {
          already_forked = 1;
          simple_command->flags |= CMD_NO_FORK;
 
-         subshell_environment = (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
-                                       ? (SUBSHELL_PIPE|SUBSHELL_FORK)
-                                       : (SUBSHELL_ASYNC|SUBSHELL_FORK);
+         subshell_environment = SUBSHELL_FORK;
+         if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
+           subshell_environment |= SUBSHELL_PIPE;
+         if (async)
+           subshell_environment |= SUBSHELL_ASYNC;
 
          /* We need to do this before piping to handle some really
             pathological cases where one of the pipe file descriptors
@@ -2434,15 +3950,28 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
            close_fd_bitmap (fds_to_close);
 
          do_piping (pipe_in, pipe_out);
-         pipe_in = pipe_out = -1;
+         pipe_in = pipe_out = NO_PIPE;
+#if defined (COPROCESS_SUPPORT)
+         coproc_closeall ();
+#endif
 
          last_asynchronous_pid = old_last_async_pid;
+
+         CHECK_SIGTERM;
        }
       else
        {
+         /* Don't let simple commands that aren't the last command in a
+            pipeline change $? for the rest of the pipeline (or at all). */
+         if (pipe_out != NO_PIPE)
+           result = last_command_exit_value;
          close_pipes (pipe_in, pipe_out);
 #if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
-         unlink_fifo_list ();
+         /* Close /dev/fd file descriptors in the parent after forking the
+            last child in a (possibly one-element) pipeline.  Defer this
+            until any running shell function completes. */
+         if (pipe_out == NO_PIPE && variable_context == 0)     /* XXX */
+           unlink_fifo_list ();                /* XXX */
 #endif
          command_line = (char *)NULL;      /* don't free this. */
          bind_lastarg ((char *)NULL);
@@ -2456,7 +3985,12 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
     {
       current_fds_to_close = fds_to_close;
       fix_assignment_words (simple_command->words);
+      /* Pass the ignore return flag down to command substitutions */
+      if (simple_command->flags & CMD_IGNORE_RETURN)   /* XXX */
+       comsub_ignore_return++;
       words = expand_words (simple_command->words);
+      if (simple_command->flags & CMD_IGNORE_RETURN)
+       comsub_ignore_return--;
       current_fds_to_close = (struct fd_bitmap *)NULL;
     }
   else
@@ -2467,10 +4001,10 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
      no variable `$foo'. */
   if (words == 0)
     {
+      this_command_name = 0;
       result = execute_null_command (simple_command->redirects,
                                     pipe_in, pipe_out,
-                                    already_forked ? 0 : async,
-                                    old_last_command_subst_pid);
+                                    already_forked ? 0 : async);
       if (already_forked)
        exit (result);
       else
@@ -2486,9 +4020,9 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
   begin_unwind_frame ("simple-command");
 
   if (echo_command_at_execute)
-    xtrace_print_word_list (words);
+    xtrace_print_word_list (words, 1);
 
-  builtin = (Function *)NULL;
+  builtin = (sh_builtin_func_t *)NULL;
   func = (SHELL_VAR *)NULL;
   if ((simple_command->flags & CMD_NO_FUNCTIONS) == 0)
     {
@@ -2508,6 +4042,15 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
        func = find_function (words->word->word);
     }
 
+  /* In POSIX mode, assignment errors in the temporary environment cause a
+     non-interactive shell to exit. */
+  if (posixly_correct && builtin_is_special && interactive_shell == 0 && tempenv_assign_error)
+    {
+      last_command_exit_value = EXECUTION_FAILURE;
+      jump_to_top_level (ERREXIT);
+    }
+  tempenv_assign_error = 0;    /* don't care about this any more */
+
   add_unwind_protect (dispose_words, words);
   QUIT;
 
@@ -2527,7 +4070,7 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
       goto return_result;
     }
 
-  /* One other possiblilty.  The user may want to resume an existing job.
+  /* One other possibililty.  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 && already_forked == 0 && async == 0 &&
@@ -2539,52 +4082,30 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
        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;
+      int job, jflags, started_status;
 
-      word = words->word->word;
-      exact = STREQ (temp, "exact");
-      substring = STREQ (temp, "substring");
-      wl = strlen (word);
-      for (i = job_slots - 1; i > -1; i--)
+      jflags = JM_STOPPED|JM_FIRSTMATCH;
+      if (STREQ (temp, "exact"))
+       jflags |= JM_EXACT;
+      else if (STREQ (temp, "substring"))
+       jflags |= JM_SUBSTRING;
+      else
+       jflags |= JM_PREFIX;
+      job = get_job_by_name (words->word->word, jflags);
+      if (job != NO_JOB)
        {
-         if (jobs[i] == 0 || (JOBSTATE (i) != JSTOPPED))
-           continue;
+         run_unwind_frame ("simple-command");
+         this_command_name = "fg";
+         last_shell_builtin = this_shell_builtin;
+         this_shell_builtin = builtin_address ("fg");
 
-         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");
-             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);
+         started_status = start_job (job, 1);
+         return ((started_status < 0) ? EXECUTION_FAILURE : started_status);
        }
     }
 #endif /* JOB_CONTROL */
 
+run_builtin:
   /* Remember the name of this command globally. */
   this_command_name = words->word->word;
 
@@ -2603,11 +4124,21 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
 
   if (builtin || func)
     {
+      if (builtin)
+        {
+         old_builtin = executing_builtin;
+         old_command_builtin = executing_command_builtin;
+         unwind_protect_int (executing_builtin);       /* modified in execute_builtin */
+         unwind_protect_int (executing_command_builtin);       /* ditto */
+        }
       if (already_forked)
        {
          /* reset_terminating_signals (); */   /* XXX */
-         /* Cancel traps, in trap.c. */
-         restore_original_signals ();
+         /* Reset the signal handlers in the child, but don't free the
+            trap strings.  Set a flag noting that we have to free the
+            trap strings if we run trap to change a signal disposition. */
+         reset_signal_handlers ();
+         subshell_environment |= SUBSHELL_RESETTRAP;
 
          if (async)
            {
@@ -2618,10 +4149,12 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
              setup_async_signals ();
            }
 
+         subshell_level++;
          execute_subshell_builtin_or_function
            (words, simple_command->redirects, builtin, func,
             pipe_in, pipe_out, async, fds_to_close,
             simple_command->flags);
+         subshell_level--;
        }
       else
        {
@@ -2632,6 +4165,18 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
            {
              if (result > EX_SHERRBASE)
                {
+                 switch (result)
+                   {
+                   case EX_REDIRFAIL:
+                   case EX_BADASSIGN:
+                   case EX_EXPFAIL:
+                     /* These errors cause non-interactive posix mode shells to exit */
+                     if (posixly_correct && builtin_is_special && interactive_shell == 0)
+                       {
+                         last_command_exit_value = EXECUTION_FAILURE;
+                         jump_to_top_level (ERREXIT);
+                       }
+                   }
                  result = builtin_status (result);
                  if (builtin_is_special)
                    special_builtin_failed = 1;
@@ -2656,17 +4201,36 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
        }
     }
 
+  if (autocd && interactive && words->word && is_dirname (words->word->word))
+    {
+      words = make_word_list (make_word ("cd"), words);
+      xtrace_print_word_list (words, 0);
+      goto run_builtin;
+    }
+
   if (command_line == 0)
-    command_line = savestring (the_printed_command);
+    command_line = savestring (the_printed_command_except_trap ? the_printed_command_except_trap : "");
+
+#if defined (PROCESS_SUBSTITUTION)
+  if ((subshell_environment & SUBSHELL_COMSUB) && (simple_command->flags & CMD_NO_FORK) && fifos_pending() > 0)
+    simple_command->flags &= ~CMD_NO_FORK;
+#endif
 
-  execute_disk_command (words, simple_command->redirects, command_line,
+  result = execute_disk_command (words, simple_command->redirects, command_line,
                        pipe_in, pipe_out, async, fds_to_close,
                        simple_command->flags);
 
  return_result:
   bind_lastarg (lastarg);
   FREE (command_line);
-  run_unwind_frame ("simple-command");
+  dispose_words (words);
+  if (builtin)
+    {
+      executing_builtin = old_builtin;
+      executing_command_builtin = old_command_builtin;
+    }
+  discard_unwind_frame ("simple-command");
+  this_command_name = (char *)NULL;    /* points to freed memory now */
   return (result);
 }
 
@@ -2698,24 +4262,40 @@ builtin_status (result)
 
 static int
 execute_builtin (builtin, words, flags, subshell)
-     Function *builtin;
+     sh_builtin_func_t *builtin;
      WORD_LIST *words;
      int flags, subshell;
 {
   int old_e_flag, result, eval_unwind;
+  int isbltinenv;
+  char *error_trap;
 
+  error_trap = 0;
   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
      eval builtin is being called, and we're supposed to ignore the exit
-     value of the command, we turn the -e flag off ourselves, then
-     restore it when the command completes. */
-  if (subshell == 0 && builtin == eval_builtin && (flags & CMD_IGNORE_RETURN))
+     value of the command, we turn the -e flag off ourselves and disable
+     the ERR trap, then restore them when the command completes.  This is
+     also a problem (as below) for the command and source/. builtins. */
+  if (subshell == 0 && (flags & CMD_IGNORE_RETURN) &&
+       (builtin == eval_builtin || builtin == command_builtin || builtin == source_builtin))
     {
       begin_unwind_frame ("eval_builtin");
       unwind_protect_int (exit_immediately_on_error);
+      unwind_protect_int (builtin_ignoring_errexit);
+      error_trap = TRAP_STRING (ERROR_TRAP);
+      if (error_trap)
+       {
+         error_trap = savestring (error_trap);
+         add_unwind_protect (xfree, error_trap);
+         add_unwind_protect (set_error_trap, error_trap);
+         restore_default_signal (ERROR_TRAP);
+       }
       exit_immediately_on_error = 0;
+      builtin_ignoring_errexit = 1;
       eval_unwind = 1;
     }
   else
@@ -2723,20 +4303,23 @@ execute_builtin (builtin, words, flags, subshell)
 
   /* The temporary environment for a builtin is supposed to apply to
      all commands executed by that builtin.  Currently, this is a
-     problem only with the `source' and `eval' builtins. */
-  if (builtin == source_builtin || builtin == eval_builtin)
+     problem only with the `unset', `source' and `eval' builtins.
+     `mapfile' is a special case because it uses evalstring (same as
+     eval or source) to run its callbacks. */
+  isbltinenv = (builtin == source_builtin || builtin == eval_builtin || builtin == unset_builtin || builtin == mapfile_builtin);
+
+  if (isbltinenv)
     {
       if (subshell == 0)
        begin_unwind_frame ("builtin_env");
 
       if (temporary_env)
        {
-         builtin_env = copy_array (temporary_env);
+         push_scope (VC_BLTNENV, temporary_env);
          if (subshell == 0)
-           add_unwind_protect (dispose_builtin_env, (char *)NULL);
-         dispose_used_env_vars ();
+           add_unwind_protect (pop_scope, (flags & CMD_COMMAND_BUILTIN) ? 0 : "1");
+          temporary_env = (HASH_TABLE *)NULL;    
        }
-      /* Otherwise we inherit builtin_env from our caller. */
     }
 
   /* `return' does a longjmp() back to a saved environment in execute_function.
@@ -2749,6 +4332,8 @@ execute_builtin (builtin, words, flags, subshell)
       add_unwind_protect (merge_temporary_env, (char *)NULL);
     }
 
+  executing_builtin++;
+  executing_command_builtin |= builtin == command_builtin;
   result = ((*builtin) (words->next));
 
   /* This shouldn't happen, but in case `return' comes back instead of
@@ -2756,20 +4341,18 @@ execute_builtin (builtin, words, flags, subshell)
   if (posixly_correct && subshell == 0 && builtin == return_builtin && temporary_env)
     discard_unwind_frame ("return_temp_env");
 
-  if (subshell == 0 && (builtin == source_builtin || builtin == eval_builtin))
-    {
-      /* In POSIX mode, if any variable assignments precede the `.' or
-        `eval' builtin, they persist after the builtin completes, since `.'
-        and `eval' are special builtins. */
-      if (posixly_correct && builtin_env)
-       merge_builtin_env ();
-
-      run_unwind_frame ("builtin_env");
-    }
+  if (subshell == 0 && isbltinenv)
+    run_unwind_frame ("builtin_env");
 
   if (eval_unwind)
     {
-      exit_immediately_on_error += old_e_flag;
+      exit_immediately_on_error = errexit_flag;
+      builtin_ignoring_errexit = 0;
+      if (error_trap)
+       {
+         set_error_trap (error_trap);
+         xfree (error_trap);
+       }
       discard_unwind_frame ("eval_builtin");
     }
 
@@ -2780,13 +4363,36 @@ static int
 execute_function (var, words, flags, fds_to_close, async, subshell)
      SHELL_VAR *var;
      WORD_LIST *words;
-     int flags, subshell, async;
+     int flags;
      struct fd_bitmap *fds_to_close;
+     int async, subshell;
 {
   int return_val, result;
-  COMMAND *tc, *fc;
-  char *debug_trap;
-  SHELL_VAR *old_shell_function;
+  COMMAND *tc, *fc, *save_current;
+  char *debug_trap, *error_trap, *return_trap;
+#if defined (ARRAY_VARS)
+  SHELL_VAR *funcname_v, *nfv, *bash_source_v, *bash_lineno_v;
+  ARRAY *funcname_a;
+  volatile ARRAY *bash_source_a;
+  volatile ARRAY *bash_lineno_a;
+#endif
+  FUNCTION_DEF *shell_fn;
+  char *sfile, *t;
+
+  USE_VAR(fc);
+
+  if (funcnest_max > 0 && funcnest >= funcnest_max)
+    {
+      internal_error (_("%s: maximum function nesting level exceeded (%d)"), var->name, funcnest);
+      funcnest = 0;    /* XXX - should we reset it somewhere else? */
+      jump_to_top_level (DISCARD);
+    }
+
+#if defined (ARRAY_VARS)
+  GET_ARRAY_FROM_VAR ("FUNCNAME", funcname_v, funcname_a);
+  GET_ARRAY_FROM_VAR ("BASH_SOURCE", bash_source_v, bash_source_a);
+  GET_ARRAY_FROM_VAR ("BASH_LINENO", bash_lineno_v, bash_lineno_a);
+#endif
 
   tc = (COMMAND *)copy_command (function_cell (var));
   if (tc && (flags & CMD_IGNORE_RETURN))
@@ -2795,7 +4401,7 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
   if (subshell == 0)
     {
       begin_unwind_frame ("function_calling");
-      push_context ();
+      push_context (var->name, subshell, temporary_env);
       add_unwind_protect (pop_context, (char *)NULL);
       unwind_protect_int (line_number);
       unwind_protect_int (return_catch_flag);
@@ -2803,82 +4409,169 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
       add_unwind_protect (dispose_command, (char *)tc);
       unwind_protect_pointer (this_shell_function);
       unwind_protect_int (loop_level);
+      unwind_protect_int (funcnest);
     }
+  else
+    push_context (var->name, subshell, temporary_env); /* don't unwind-protect for subshells */
+
+  temporary_env = (HASH_TABLE *)NULL;
 
   this_shell_function = var;
   make_funcname_visible (1);
 
-  debug_trap = (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
-                       ? trap_list[DEBUG_TRAP]
-                       : (char *)NULL;
-  if (debug_trap)
+  debug_trap = TRAP_STRING(DEBUG_TRAP);
+  error_trap = TRAP_STRING(ERROR_TRAP);
+  return_trap = TRAP_STRING(RETURN_TRAP);
+  
+  /* The order of the unwind protects for debug_trap, error_trap and
+     return_trap is important here!  unwind-protect commands are run
+     in reverse order of registration.  If this causes problems, take
+     out the xfree unwind-protect calls and live with the small memory leak. */
+
+  /* function_trace_mode != 0 means that all functions inherit the DEBUG trap.
+     if the function has the trace attribute set, it inherits the DEBUG trap */
+  if (debug_trap && ((trace_p (var) == 0) && function_trace_mode == 0))
     {
       if (subshell == 0)
        {
          debug_trap = savestring (debug_trap);
-         /* XXX order is important here!  unwind-protect commands are run
-            in reverse order of registering.  If this causes problems,
-            take out the xfree unwind-protect and live with the small
-            memory leak. */
          add_unwind_protect (xfree, debug_trap);
          add_unwind_protect (set_debug_trap, debug_trap);
        }
       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)
+  /* error_trace_mode != 0 means that functions inherit the ERR trap. */
+  if (error_trap && error_trace_mode == 0)
     {
-      function_env = copy_array (temporary_env);
-      /* In POSIX mode, variable assignments preceding function names are
-        supposed to persist in the environment after the function returns,
-        as if a special builtin command had been executed. */
       if (subshell == 0)
        {
-         if (posixly_correct)
-           add_unwind_protect (merge_function_env, (char *)NULL);
-         else
-           add_unwind_protect (dispose_function_env, (char *)NULL);
+         error_trap = savestring (error_trap);
+         add_unwind_protect (xfree, error_trap);
+         add_unwind_protect (set_error_trap, error_trap);
+       }
+      restore_default_signal (ERROR_TRAP);
+    }
+
+  /* Shell functions inherit the RETURN trap if function tracing is on
+     globally or on individually for this function. */
+#if 0
+  if (return_trap && ((trace_p (var) == 0) && function_trace_mode == 0))
+#else
+  if (return_trap && (signal_in_progress (DEBUG_TRAP) || ((trace_p (var) == 0) && function_trace_mode == 0)))
+#endif
+    {
+      if (subshell == 0)
+       {
+         return_trap = savestring (return_trap);
+         add_unwind_protect (xfree, return_trap);
+         add_unwind_protect (set_return_trap, return_trap);
        }
-      dispose_used_env_vars ();
+      restore_default_signal (RETURN_TRAP);
     }
-    /* Otherwise, we inherit function_env from our caller. */
+  
+  funcnest++;
+#if defined (ARRAY_VARS)
+  /* This is quite similar to the code in shell.c and elsewhere. */
+  shell_fn = find_function_def (this_shell_function->name);
+  sfile = shell_fn ? shell_fn->source_file : "";
+  array_push ((ARRAY *)funcname_a, this_shell_function->name);
+
+  array_push ((ARRAY *)bash_source_a, sfile);
+  t = itos (executing_line_number ());
+  array_push ((ARRAY *)bash_lineno_a, t);
+  free (t);
+#endif
+
+  /* The temporary environment for a function is supposed to apply to
+     all commands executed within the function body. */
 
   remember_args (words->next, 1);
 
+  /* Update BASH_ARGV and BASH_ARGC */
+  if (debugging_mode)
+    push_args (words->next);
+
   /* Number of the line on which the function body starts. */
   line_number = function_line_number = tc->line;
 
-  if (subshell)
-    {
 #if defined (JOB_CONTROL)
-      stop_pipeline (async, (COMMAND *)NULL);
+  if (subshell)
+    stop_pipeline (async, (COMMAND *)NULL);
 #endif
-      fc = (tc->type == cm_group) ? tc->value.Group->command : tc;
-
-      if (fc && (flags & CMD_IGNORE_RETURN))
-       fc->flags |= CMD_IGNORE_RETURN;
 
-      variable_context++;
-    }
-  else
-    fc = tc;
+  fc = tc;
 
   return_catch_flag++;
-  return_val = setjmp (return_catch);
+  return_val = setjmp_nosigs (return_catch);
 
   if (return_val)
-    result = return_catch_value;
+    {
+      result = return_catch_value;
+      /* Run the RETURN trap in the function's context. */
+      save_current = currently_executing_command;
+      run_return_trap ();
+      currently_executing_command = save_current;
+    }
   else
-    result = execute_command_internal (fc, 0, NO_PIPE, NO_PIPE, fds_to_close);
+    {
+      /* Run the debug trap here so we can trap at the start of a function's
+        execution rather than the execution of the body's first command. */
+      showing_function_line = 1;
+      save_current = currently_executing_command;
+      result = run_debug_trap ();
+#if defined (DEBUGGER)
+      /* In debugging mode, if the DEBUG trap returns a non-zero status, we
+        skip the command. */
+      if (debugging_mode == 0 || result == EXECUTION_SUCCESS)
+       {
+         showing_function_line = 0;
+         currently_executing_command = save_current;
+         result = execute_command_internal (fc, 0, NO_PIPE, NO_PIPE, fds_to_close);
+
+         /* Run the RETURN trap in the function's context */
+         save_current = currently_executing_command;
+         run_return_trap ();
+         currently_executing_command = save_current;
+       }
+#else
+      result = execute_command_internal (fc, 0, NO_PIPE, NO_PIPE, fds_to_close);
+
+      save_current = currently_executing_command;
+      run_return_trap ();
+      currently_executing_command = save_current;
+#endif
+      showing_function_line = 0;
+    }
+
+  /* Restore BASH_ARGC and BASH_ARGV */
+  if (debugging_mode)
+    pop_args ();
 
   if (subshell == 0)
     run_unwind_frame ("function_calling");
 
+#if defined (ARRAY_VARS)
+  /* These two variables cannot be unset, and cannot be affected by the
+     function. */
+  array_pop ((ARRAY *)bash_source_a);
+  array_pop ((ARRAY *)bash_lineno_a);
+
+  /* FUNCNAME can be unset, and so can potentially be changed by the
+     function. */
+  GET_ARRAY_FROM_VAR ("FUNCNAME", nfv, funcname_a);
+  if (nfv == funcname_v)
+    array_pop (funcname_a);
+#endif
+  
   if (variable_context == 0 || this_shell_function == 0)
-    make_funcname_visible (0);
-
+    {
+      make_funcname_visible (0);
+#if defined (PROCESS_SUBSTITUTION)
+      unlink_fifo_list ();
+#endif
+    }
+  
   return (result);
 }
 
@@ -2916,13 +4609,13 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
                                      flags)
      WORD_LIST *words;
      REDIRECT *redirects;
-     Function *builtin;
+     sh_builtin_func_t *builtin;
      SHELL_VAR *var;
      int pipe_in, pipe_out, async;
      struct fd_bitmap *fds_to_close;
      int flags;
 {
-  int result, r;
+  int result, r, funcvalue;
 #if defined (JOB_CONTROL)
   int jobs_hack;
 
@@ -2933,7 +4626,10 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
   /* A subshell is neither a login shell nor interactive. */
   login_shell = interactive = 0;
 
-  subshell_environment = SUBSHELL_ASYNC;
+  if (async)
+    subshell_environment |= SUBSHELL_ASYNC;
+  if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
+    subshell_environment |= SUBSHELL_PIPE;
 
   maybe_make_export_env ();    /* XXX - is this needed? */
 
@@ -2958,29 +4654,42 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
 
   do_piping (pipe_in, pipe_out);
 
-  if (do_redirections (redirects, 1, 0, 0) != 0)
+  if (do_redirections (redirects, RX_ACTIVE) != 0)
     exit (EXECUTION_FAILURE);
 
   if (builtin)
     {
       /* Give builtins a place to jump back to on failure,
         so we don't go back up to main(). */
-      result = setjmp (top_level);
+      result = setjmp_nosigs (top_level);
+
+      /* Give the return builtin a place to jump to when executed in a subshell
+         or pipeline */
+      funcvalue = 0;
+      if (return_catch_flag && builtin == return_builtin)
+        funcvalue = setjmp_nosigs (return_catch);
 
       if (result == EXITPROG)
        exit (last_command_exit_value);
       else if (result)
        exit (EXECUTION_FAILURE);
+      else if (funcvalue)
+       exit (return_catch_value);
       else
        {
          r = execute_builtin (builtin, words, flags, 1);
+         fflush (stdout);
          if (r == EX_USAGE)
            r = EX_BADUSAGE;
          exit (r);
        }
     }
   else
-    exit (execute_function (var, words, flags, fds_to_close, async, 1));
+    {
+      r = execute_function (var, words, flags, fds_to_close, async, 1);
+      fflush (stdout);
+      exit (r);
+    }
 }
 
 /* Execute a builtin or function in the current shell context.  If BUILTIN
@@ -2995,7 +4704,7 @@ static int
 execute_builtin_or_function (words, builtin, var, redirects,
                             fds_to_close, flags)
      WORD_LIST *words;
-     Function *builtin;
+     sh_builtin_func_t *builtin;
      SHELL_VAR *var;
      REDIRECT *redirects;
      struct fd_bitmap *fds_to_close;
@@ -3003,17 +4712,28 @@ execute_builtin_or_function (words, builtin, var, redirects,
 {
   int result;
   REDIRECT *saved_undo_list;
-  Function *saved_this_shell_builtin;
+#if defined (PROCESS_SUBSTITUTION)
+  int ofifo, nfifo, osize;
+  char *ofifo_list;
+#endif
 
-  if (do_redirections (redirects, 1, 1, 0) != 0)
+
+#if defined (PROCESS_SUBSTITUTION)  
+  ofifo = num_fifos ();
+  ofifo_list = copy_fifo_list (&osize);
+#endif
+
+  if (do_redirections (redirects, RX_ACTIVE|RX_UNDOABLE) != 0)
     {
       cleanup_redirects (redirection_undo_list);
       redirection_undo_list = (REDIRECT *)NULL;
       dispose_exec_redirects ();
+#if defined (PROCESS_SUBSTITUTION)
+      free (ofifo_list);
+#endif
       return (EX_REDIRFAIL);   /* was EXECUTION_FAILURE */
     }
 
-  saved_this_shell_builtin = this_shell_builtin;
   saved_undo_list = redirection_undo_list;
 
   /* Calling the "exec" builtin changes redirections forever. */
@@ -3039,6 +4759,12 @@ execute_builtin_or_function (words, builtin, var, redirects,
   else
     result = execute_function (var, words, flags, fds_to_close, 0, 0);
 
+  /* We do this before undoing the effects of any redirections. */
+  fflush (stdout);
+  fpurge (stdout);
+  if (ferror (stdout))
+    clearerr (stdout);  
+
   /* If we are executing the `command' builtin, but this_shell_builtin is
      set to `exec_builtin', we know that we have something like
      `command exec [redirection]', since otherwise `exec' would have
@@ -3047,11 +4773,18 @@ execute_builtin_or_function (words, builtin, var, redirects,
      and preserve the redirections. */
   if (builtin == command_builtin && this_shell_builtin == exec_builtin)
     {
+      int discard;
+
+      discard = 0;
       if (saved_undo_list)
-       dispose_redirects (saved_undo_list);
+       {
+         dispose_redirects (saved_undo_list);
+         discard = 1;
+       }
       redirection_undo_list = exec_redirection_undo_list;
       saved_undo_list = exec_redirection_undo_list = (REDIRECT *)NULL;      
-      discard_unwind_frame ("saved_redirects");
+      if (discard)
+       discard_unwind_frame ("saved redirects");
     }
 
   if (saved_undo_list)
@@ -3066,6 +4799,14 @@ execute_builtin_or_function (words, builtin, var, redirects,
       redirection_undo_list = (REDIRECT *)NULL;
     }
 
+#if defined (PROCESS_SUBSTITUTION)
+  /* Close any FIFOs created by this builtin or function. */
+  nfifo = num_fifos ();
+  if (nfifo > ofifo)
+    close_new_fifos (ofifo_list, osize);
+  free (ofifo_list);
+#endif
+
   return (result);
 }
 
@@ -3080,10 +4821,15 @@ setup_async_signals ()
   if (job_control == 0)
 #endif
     {
+      /* Make sure we get the original signal dispositions now so we don't
+        confuse the trap builtin later if the subshell tries to use it to
+        reset SIGINT/SIGQUIT.  Don't call set_signal_ignored; that sets
+        the value of original_signals to SIG_IGN. Posix interpretation 751. */
+      get_original_signal (SIGINT);
       set_signal_handler (SIGINT, SIG_IGN);
-      set_signal_ignored (SIGINT);
+
+      get_original_signal (SIGQUIT);
       set_signal_handler (SIGQUIT, SIG_IGN);
-      set_signal_ignored (SIGQUIT);
     }
 }
 
@@ -3102,8 +4848,16 @@ setup_async_signals ()
    Note that the filename hashing stuff has to take place up here,
    in the parent.  This is probably why the Bourne style shells
    don't handle it, since that would require them to go through
-   this gnarly hair, for no good reason.  */
-static void
+   this gnarly hair, for no good reason.
+
+   NOTE: callers expect this to fork or exit(). */
+
+/* Name of a shell function to call when a command name is not found. */
+#ifndef NOTFOUND_HOOK
+#  define NOTFOUND_HOOK "command_not_found_handle"
+#endif
+
+static int
 execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
                      async, fds_to_close, cmdflags)
      WORD_LIST *words;
@@ -3114,23 +4868,33 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
      int cmdflags;
 {
   char *pathname, *command, **args;
-  int nofork;
+  int nofork, result;
   pid_t pid;
+  SHELL_VAR *hookf;
+  WORD_LIST *wl;
 
   nofork = (cmdflags & CMD_NO_FORK);  /* Don't fork, just exec, if no pipes */
   pathname = words->word->word;
 
+  result = EXECUTION_SUCCESS;
 #if defined (RESTRICTED_SHELL)
-  if (restricted && strchr (pathname, '/'))
+  command = (char *)NULL;
+  if (restricted && mbschr (pathname, '/'))
     {
-      internal_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;
+      result = last_command_exit_value = EXECUTION_FAILURE;
+
+      /* If we're not going to fork below, we must already be in a child
+         process or a context in which it's safe to call exit(2).  */
+      if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE)
+       exit (last_command_exit_value);
+      else
+       goto parent_return;
     }
 #endif /* RESTRICTED_SHELL */
 
-  command = search_for_command (pathname);
+  command = search_for_command (pathname, 1);
 
   if (command)
     {
@@ -3151,17 +4915,12 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
     {
       int old_interactive;
 
-#if 0
-      /* This has been disabled for the time being. */
-#if !defined (ARG_MAX) || ARG_MAX >= 10240
-      if (posixly_correct == 0)
-       put_gnu_argv_flags_into_env ((int)getpid (), glob_argv_flags);
-#endif
-#endif
-
+      reset_terminating_signals ();    /* XXX */
       /* Cancel traps, in trap.c. */
       restore_original_signals ();
 
+      CHECK_SIGTERM;
+
       /* restore_original_signals may have undone the work done
         by make_child to ensure that SIGINT and SIGQUIT are ignored
         in asynchronous children. */
@@ -3184,15 +4943,13 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
 
       do_piping (pipe_in, pipe_out);
 
+      old_interactive = interactive;
       if (async)
-       {
-         old_interactive = interactive;
-         interactive = 0;
-       }
+       interactive = 0;
 
       subshell_environment = SUBSHELL_FORK;
 
-      if (redirects && (do_redirections (redirects, 1, 0, 0) != 0))
+      if (redirects && (do_redirections (redirects, RX_ACTIVE) != 0))
        {
 #if defined (PROCESS_SUBSTITUTION)
          /* Try to remove named pipes that may have been created as the
@@ -3207,38 +4964,49 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
 
       if (command == 0)
        {
-         internal_error ("%s: command not found", pathname);
-         exit (EX_NOTFOUND);   /* Posix.2 says the exit status is 127 */
+         hookf = find_function (NOTFOUND_HOOK);
+         if (hookf == 0)
+           {
+             /* Make sure filenames are displayed using printable characters */
+             if (ansic_shouldquote (pathname))
+               pathname = ansic_quote (pathname, 0, NULL);
+             internal_error (_("%s: command not found"), pathname);
+             exit (EX_NOTFOUND);       /* Posix.2 says the exit status is 127 */
+           }
+
+#if defined (JOB_CONTROL)
+         /* May need to reinitialize more of the job control state here. */
+         kill_current_pipeline ();
+#endif
+
+         wl = make_word_list (make_word (NOTFOUND_HOOK), words);
+         exit (execute_shell_function (hookf, wl));
        }
 
+      CHECK_SIGTERM;
+
       /* 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);
+      args = strvec_from_word_list (words, 0, 0, (int *)NULL);
       exit (shell_execve (command, args, export_env));
     }
   else
     {
+parent_return:
+      QUIT;
+
       /* Make sure that the pipes are closed in the parent. */
       close_pipes (pipe_in, pipe_out);
 #if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
-      unlink_fifo_list ();
+      if (variable_context == 0)
+        unlink_fifo_list ();
 #endif
       FREE (command);
+      return (result);
     }
 }
 
-#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
-   the script; it and ARGS, the arguments given by the user, will
-   become arguments to the specified interpreter.  ENV is the environment
-   to pass to the interpreter.
-
-   The word immediately following the #! is the interpreter to execute.
-   A single argument to the interpreter is allowed. */
-
 /* CPP defines to decide whether a particular index into the #! line
    corresponds to a valid interpreter name or argument character, or
    whitespace.  The MSDOS define is to allow \r to be treated the same
@@ -3246,35 +5014,62 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
 
 #if !defined (MSDOS)
 #  define STRINGCHAR(ind) \
-    (!whitespace (sample[ind]) && sample[ind] != '\n' && ind < sample_len)
+    (ind < sample_len && !whitespace (sample[ind]) && sample[ind] != '\n')
 #  define WHITECHAR(ind) \
-    (whitespace (sample[ind]) && sample[ind] != '\n' && ind < sample_len)
+    (ind < sample_len && whitespace (sample[ind]))
 #else  /* MSDOS */
 #  define STRINGCHAR(ind) \
-    (!whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r' && ind < sample_len)
+    (ind < sample_len && !whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r')
 #  define WHITECHAR(ind) \
-    (whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r' && ind < sample_len)
+    (ind < sample_len && whitespace (sample[ind]))
 #endif /* MSDOS */
 
-static int
-execute_shell_script (sample, sample_len, command, args, env)
-     unsigned char *sample;
-     int sample_len;
-     char *command;
-     char **args, **env;
+static char *
+getinterp (sample, sample_len, endp)
+     char *sample;
+     int sample_len, *endp;
 {
   register int i;
-  char *execname, *firstarg;
-  int start, size_increment, larry;
+  char *execname;
+  int start;
 
   /* Find the name of the interpreter to exec. */
-  for (i = 2; whitespace (sample[i]) && i < sample_len; i++)
+  for (i = 2; i < sample_len && whitespace (sample[i]); i++)
     ;
 
   for (start = i; STRINGCHAR(i); i++)
     ;
 
-  execname = substring ((char *)sample, start, i);
+  execname = substring (sample, start, i);
+
+  if (endp)
+    *endp = i;
+  return execname;
+}
+
+#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
+   the script; it and ARGS, the arguments given by the user, will
+   become arguments to the specified interpreter.  ENV is the environment
+   to pass to the interpreter.
+
+   The word immediately following the #! is the interpreter to execute.
+   A single argument to the interpreter is allowed. */
+
+static int
+execute_shell_script (sample, sample_len, command, args, env)
+     char *sample;
+     int sample_len;
+     char *command;
+     char **args, **env;
+{
+  char *execname, *firstarg;
+  int i, start, size_increment, larry;
+
+  /* Find the name of the interpreter to exec. */
+  execname = getinterp (sample, sample_len, &i);
   size_increment = 1;
 
   /* Now the argument, if any. */
@@ -3292,9 +5087,8 @@ execute_shell_script (sample, sample_len, command, args, env)
       size_increment = 2;
     }
 
-  larry = array_len (args) + size_increment;
-
-  args = (char **)xrealloc ((char *)args, (1 + larry) * sizeof (char *));
+  larry = strvec_len (args) + size_increment;
+  args = strvec_resize (args, larry + 1);
 
   for (i = larry - 1; i; i--)
     args[i] = args[i - size_increment];
@@ -3335,6 +5129,7 @@ initialize_subshell ()
   /* Forget about the way job control was working. We are in a subshell. */
   without_job_control ();
   set_sigchld_handler ();
+  init_job_stats ();
 #endif /* JOB_CONTROL */
 
   /* Reset the values of the shell flags and options. */
@@ -3345,18 +5140,24 @@ initialize_subshell ()
   /* Zero out builtin_env, since this could be a shell script run from a
      sourced file with a temporary environment supplied to the `source/.'
      builtin.  Such variables are not supposed to be exported (empirical
-     testing with sh and ksh). */
-  builtin_env = 0;
+     testing with sh and ksh).  Just throw it away; don't worry about a
+     memory leak. */
+  if (vc_isbltnenv (shell_variables))
+    shell_variables = shell_variables->down;
 
   clear_unwind_protect_list (0);
+  /* XXX -- are there other things we should be resetting here? */
+  parse_and_execute_level = 0;         /* nothing left to restore it */
 
   /* We're no longer inside a shell function. */
-  variable_context = return_catch_flag = 0;
+  variable_context = return_catch_flag = funcnest = 0;
+
+  executing_list = 0;          /* XXX */
 
   /* If we're not interactive, close the file descriptor from which we're
      reading the current shell script. */
   if (interactive_shell == 0)
-    unset_bash_input (1);
+    unset_bash_input (0);
 }
 
 #if defined (HAVE_SETOSTYPE) && defined (_POSIX_SOURCE)
@@ -3371,7 +5172,7 @@ initialize_subshell ()
       fd = open(file, O_RDONLY); \
       if (fd >= 0) \
        { \
-         len = read (fd, (char *)buf, 80); \
+         len = read (fd, buf, 80); \
          close (fd); \
        } \
       else \
@@ -3386,30 +5187,61 @@ shell_execve (command, args, env)
      char *command;
      char **args, **env;
 {
-  struct stat finfo;
   int larray, i, fd;
-  unsigned char sample[80];
+  char sample[80];
   int sample_len;
 
   SETOSTYPE (0);               /* Some systems use for USG/POSIX semantics */
   execve (command, args, env);
   i = errno;                   /* error from execve() */
+  CHECK_TERMSIG;
   SETOSTYPE (1);
 
   /* If we get to this point, then start checking out the file.
      Maybe it is something we can hack ourselves. */
   if (i != ENOEXEC)
     {
-      if ((stat (command, &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
-       internal_error ("%s: is a directory", command);
+      if (file_isdir (command))
+#if defined (EISDIR)
+       internal_error (_("%s: %s"), command, strerror (EISDIR));
+#else
+       internal_error (_("%s: is a directory"), command);
+#endif
+      else if (executable_file (command) == 0)
+       {
+         errno = i;
+         file_error (command);
+       }
+      /* errors not involving the path argument to execve. */
+      else if (i == E2BIG || i == ENOMEM)
+       {
+         errno = i;
+         file_error (command);
+       }
       else
        {
+         /* The file has the execute bits set, but the kernel refuses to
+            run it for some reason.  See why. */
 #if defined (HAVE_HASH_BANG_EXEC)
          READ_SAMPLE_BUF (command, sample, sample_len);
+         sample[sample_len - 1] = '\0';
          if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
            {
+             char *interp;
+             int ilen;
+
+             interp = getinterp (sample, sample_len, (int *)NULL);
+             ilen = strlen (interp);
              errno = i;
-             sys_error ("%s: bad interpreter", command);
+             if (interp[ilen - 1] == '\r')
+               {
+                 interp = xrealloc (interp, ilen + 2);
+                 interp[ilen - 1] = '^';
+                 interp[ilen] = 'M';
+                 interp[ilen + 1] = '\0';
+               }
+             sys_error (_("%s: %s: bad interpreter"), command, interp ? interp : "");
+             FREE (interp);
              return (EX_NOEXEC);
            }
 #endif
@@ -3445,7 +5277,7 @@ shell_execve (command, args, env)
 #endif
       if (check_binary_file (sample, sample_len))
        {
-         internal_error ("%s: cannot execute binary file", command);
+         internal_error (_("%s: cannot execute binary file: %s"), command, strerror (i));
          return (EX_BINARY_FILE);
        }
     }
@@ -3458,8 +5290,8 @@ shell_execve (command, args, env)
   set_sigint_handler ();
 
   /* Insert the name of this shell into the argument list. */
-  larray = array_len (args) + 1;
-  args = (char **)xrealloc ((char *)args, (1 + larray) * sizeof (char *));
+  larray = strvec_len (args) + 1;
+  args = strvec_resize (args, larray + 1);
 
   for (i = larray - 1; i; i--)
     args[i] = args[i - 1];
@@ -3494,12 +5326,13 @@ shell_execve (command, args, env)
   unbind_args ();      /* remove the positional parameters */
 
   longjmp (subshell_top_level, 1);
+  /*NOTREACHED*/
 }
 
 static int
-execute_intern_function (name, function)
+execute_intern_function (name, funcdef)
      WORD_DESC *name;
-     COMMAND *function;
+     FUNCTION_DEF *funcdef;
 {
   SHELL_VAR *var;
 
@@ -3507,21 +5340,33 @@ execute_intern_function (name, function)
     {
       if (posixly_correct && interactive_shell == 0)
        {
-         last_command_exit_value = EX_USAGE;
-         jump_to_top_level (EXITPROG);
+         last_command_exit_value = EX_BADUSAGE;
+         jump_to_top_level (ERREXIT);
        }
       return (EXECUTION_FAILURE);
     }
 
+  /* Posix interpretation 383 */
+  if (posixly_correct && find_special_builtin (name->word))
+    {
+      internal_error (_("`%s': is a special builtin"), name->word);
+      last_command_exit_value = EX_BADUSAGE;
+      jump_to_top_level (ERREXIT);
+    }
+
   var = find_function (name->word);
   if (var && (readonly_p (var) || noassign_p (var)))
     {
       if (readonly_p (var))
-       internal_error ("%s: readonly function", var->name);
+       internal_error (_("%s: readonly function"), var->name);
       return (EXECUTION_FAILURE);
     }
 
-  bind_function (name->word, function);
+#if defined (DEBUGGER)
+  bind_function_def (name->word, funcdef);
+#endif
+
+  bind_function (name->word, funcdef->command);
   return (EXECUTION_SUCCESS);
 }
 
@@ -3534,7 +5379,7 @@ close_all_files ()
 
   fd_table_size = getdtablesize ();
   if (fd_table_size > 256)     /* clamp to a reasonable value */
-       fd_table_size = 256;
+    fd_table_size = 256;
 
   for (i = 3; i < fd_table_size; i++)
     close (i);
@@ -3552,6 +5397,13 @@ close_pipes (in, out)
     close (out);
 }
 
+static void
+dup_error (oldd, newd)
+     int oldd, newd;
+{
+  sys_error (_("cannot duplicate fd %d to fd %d"), oldd, newd);
+}
+
 /* Redirect input and output to be from and to the specified pipes.
    NO_PIPE and REDIRECT_BOTH are handled correctly. */
 static void
@@ -3561,23 +5413,33 @@ do_piping (pipe_in, pipe_out)
   if (pipe_in != NO_PIPE)
     {
       if (dup2 (pipe_in, 0) < 0)
-       sys_error ("cannot duplicate fd %d to fd 0", pipe_in);
+       dup_error (pipe_in, 0);
       if (pipe_in > 0)
        close (pipe_in);
+#ifdef __CYGWIN__
+      /* Let stdio know the fd may have changed from text to binary mode. */
+      freopen (NULL, "r", stdin);
+#endif /* __CYGWIN__ */
     }
   if (pipe_out != NO_PIPE)
     {
       if (pipe_out != REDIRECT_BOTH)
        {
          if (dup2 (pipe_out, 1) < 0)
-           sys_error ("cannot duplicate fd %d to fd 1", pipe_out);
+           dup_error (pipe_out, 1);
          if (pipe_out == 0 || pipe_out > 1)
            close (pipe_out);
        }
       else
        {
          if (dup2 (1, 2) < 0)
-           sys_error ("cannot duplicate fd 1 to fd 2");
+           dup_error (1, 2);
        }
+#ifdef __CYGWIN__
+      /* Let stdio know the fd may have changed from text to binary mode, and
+        make sure to preserve stdout line buffering. */
+      freopen (NULL, "w", stdout);
+      sh_setlinebuf (stdout);
+#endif /* __CYGWIN__ */
     }
 }