]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - execute_cmd.c
Bash-4.2 patch 34
[thirdparty/bash.git] / execute_cmd.c
index 4843f4ba69cfb118683bb4e182230bef87373e11..7432c85fe7b24041eabdad235e1a37f34a9913a8 100644 (file)
@@ -1,6 +1,6 @@
 /* execute_cmd.c -- Execute a COMMAND structure. */
 
-/* Copyright (C) 1987-2009 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2010 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -57,6 +57,8 @@
 extern int errno;
 #endif
 
+#define NEED_FPURGE_DECL
+
 #include "bashansi.h"
 #include "bashintl.h"
 
@@ -96,11 +98,12 @@ extern int errno;
 #  include "bashhist.h"
 #endif
 
+extern int dollar_dollar_pid;
 extern int posixly_correct;
 extern int expand_aliases;
 extern int autocd;
 extern int breaking, continuing, loop_level;
-extern int parse_and_execute_level, running_trap;
+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;
@@ -110,6 +113,7 @@ extern pid_t last_command_subst_pid;
 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
@@ -133,6 +137,7 @@ static int builtin_status __P((int));
 
 static int execute_for_command __P((FOR_COM *));
 #if defined (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));
@@ -176,7 +181,7 @@ static void execute_subshell_builtin_or_function __P((WORD_LIST *, REDIRECT *,
                                                      int, int, int,
                                                      struct fd_bitmap *,
                                                      int));
-static void execute_disk_command __P((WORD_LIST *, REDIRECT *, char *,
+static int execute_disk_command __P((WORD_LIST *, REDIRECT *, char *,
                                      int, int, int, struct fd_bitmap *, int));
 
 static char *getinterp __P((char *, int, int *));
@@ -251,6 +256,8 @@ 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;
@@ -267,6 +274,12 @@ static int showing_function_line;
 
 static int line_number_for_err_trap;
 
+/* A sort of function nesting level counter */
+int funcnest = 0;
+int funcnest_max = 0;          /* XXX - bash-4.2 */
+
+int lastpipe_opt = 0;
+
 struct fd_bitmap *current_fds_to_close = (struct fd_bitmap *)NULL;
 
 #define FD_BITMAP_DEFAULT_SIZE 32
@@ -568,6 +581,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 
       /* Fork a subshell, turn off the subshell bit, turn off job
         control and call execute_command () on the command again. */
+      line_number_for_err_trap = line_number;
       paren_pid = make_child (savestring (make_command_string (command)),
                              asynchronous);
       if (paren_pid == 0)
@@ -596,31 +610,30 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
              invert = (command->flags & CMD_INVERT_RETURN) != 0;
              ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
 
-             last_command_exit_value = wait_for (paren_pid);
+             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;
-
 
+             last_command_exit_value = exec_result;
              if (user_subshell && 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 (user_subshell && 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);
                }
 
-             return (last_command_exit_value = exec_result);
+             return (last_command_exit_value);
            }
          else
            {
@@ -668,6 +681,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 
   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;
@@ -677,6 +691,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;
@@ -766,7 +781,9 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
       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;
+         line_number = line_number_for_err_trap;
          run_error_trap ();
+         line_number = save_line_number;
        }
 
       if (ignore_return == 0 && invert == 0 &&
@@ -877,19 +894,58 @@ 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;
-      save_line_number = line_number;
+
+      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
     
@@ -1132,7 +1188,7 @@ 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;
+  int rv, posix_time, old_flags, nullcmd;
   time_t rs, us, ss;
   int rsf, usf, ssf;
   int cpu;
@@ -1168,6 +1224,20 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
 
   posix_time = (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);
@@ -1221,8 +1291,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);
 
@@ -1242,7 +1316,8 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
 {
   int user_subshell, return_code, function_value, should_redir_stdin, invert;
   int ois, user_coproc;
-  COMMAND *tcom;
+  int result;
+  volatile COMMAND *tcom;
 
   USE_VAR(user_subshell);
   USE_VAR(user_coproc);
@@ -1316,7 +1391,11 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
 
   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
@@ -1408,13 +1487,21 @@ 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 (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)
+  /* 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);
@@ -1465,7 +1552,7 @@ static struct cpelement *cpl_search __P((pid_t));
 static struct cpelement *cpl_searchbyname __P((char *));
 static void cpl_prune __P((void));
 
-Coproc sh_coproc = { 0, NO_PID, -1, -1, 0, 0 };
+Coproc sh_coproc = { 0, NO_PID, -1, -1, 0, 0, 0, 0 };
 
 cplist_t coproc_list = {0, 0, 0};
 
@@ -1922,7 +2009,7 @@ execute_coproc (command, pipe_in, pipe_out, fds_to_close)
      int pipe_in, pipe_out;
      struct fd_bitmap *fds_to_close;
 {
-  int rpipe[2], wpipe[2];
+  int rpipe[2], wpipe[2], estat;
   pid_t coproc_pid;
   Coproc *cp;
   char *tcmd;
@@ -1951,7 +2038,12 @@ execute_coproc (command, pipe_in, pipe_out, fds_to_close)
       close (rpipe[0]);
       close (wpipe[1]);
 
-      exit (execute_in_subshell (command, 1, wpipe[0], rpipe[1], fds_to_close));
+      estat = execute_in_subshell (command, 1, wpipe[0], rpipe[1], fds_to_close);
+
+      fflush (stdout);
+      fflush (stderr);
+
+      exit (estat);
     }
 
   close (rpipe[1]);
@@ -1982,6 +2074,22 @@ execute_coproc (command, pipe_in, pipe_out, fds_to_close)
 }
 #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;
@@ -1989,8 +2097,10 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
      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;
@@ -2080,11 +2190,46 @@ 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;
+
+#if defined (JOB_CONTROL)
+  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);
+#endif
+
   exec_result = execute_command_internal (cmd, asynchronous, prev, pipe_out, fds_to_close);
 
+#if defined (JOB_CONTROL)
+  if (lstdin > 0)
+    restore_stdin (lstdin);
+#endif
+
   if (prev >= 0)
     close (prev);
 
@@ -2093,6 +2238,23 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
 #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 ();
+    }
+
+#if defined (JOB_CONTROL)
+  discard_unwind_frame ("lastpipe-exec");
+#endif
+
   return (exec_result);
 }
 
@@ -2102,9 +2264,9 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
      int asynchronous, pipe_in, pipe_out;
      struct fd_bitmap *fds_to_close;
 {
-  REDIRECT *rp;
   COMMAND *tc, *second;
   int ignore_return, exec_result, was_error_trap, invert;
+  volatile int save_line_number;
 
   ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
 
@@ -2116,8 +2278,6 @@ 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;
@@ -2174,12 +2334,16 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
       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)
@@ -2550,6 +2714,28 @@ 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 wclen, slen;
+
+  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 ((int)wclen);
+#else
+  return (STRLEN (s));
+#endif
+}
+
 static int
 print_index_and_element (len, ind, list)
       int len, ind;
@@ -2563,7 +2749,7 @@ print_index_and_element (len, ind, list)
   for (i = ind, l = list; l && --i; l = l->next)
     ;
   fprintf (stderr, "%*d%s%s", len, ind, RP_SPACE, l->word->word);
-  return (STRLEN (l->word->word));
+  return (displen (l->word->word));
 }
 
 static void
@@ -2650,8 +2836,10 @@ select_query (list, list_len, prompt, print_menu)
   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;
 
@@ -2667,7 +2855,7 @@ select_query (list, list_len, prompt, print_menu)
   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;
     }
@@ -2682,7 +2870,7 @@ select_query (list, list_len, prompt, print_menu)
       fflush (stderr);
       QUIT;
 
-      if (read_builtin ((WORD_LIST *)NULL) == EXECUTION_FAILURE)
+      if (read_builtin ((WORD_LIST *)NULL) != EXECUTION_SUCCESS)
        {
          putchar ('\n');
          return ((char *)NULL);
@@ -2930,7 +3118,7 @@ execute_case_command (case_command)
                  retval = execute_command (clauses->action);
                }
              while ((clauses->flags & CASEPAT_FALLTHROUGH) && (clauses = clauses->next));
-             if ((clauses->flags & CASEPAT_TESTNEXT) == 0)
+             if (clauses == 0 || (clauses->flags & CASEPAT_TESTNEXT) == 0)
                EXIT_CASE ();
              else
                break;
@@ -3145,11 +3333,19 @@ static int
 execute_cond_node (cond)
      COND_COM *cond;
 {
-  int result, invert, patmatch, rmatch, mflags;
+  int result, invert, patmatch, rmatch, mflags, ignore;
   char *arg1, *arg2;
 
   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)
@@ -3166,7 +3362,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)
@@ -3186,11 +3386,19 @@ execute_cond_node (cond)
                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;
+      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;
 
@@ -3213,7 +3421,7 @@ execute_cond_node (cond)
          int oe;
          oe = extended_glob;
          extended_glob = 1;
-         result = binary_test (cond->op->word, arg1, arg2, TEST_PATMATCH|TEST_ARITHEXP)
+         result = binary_test (cond->op->word, arg1, arg2, TEST_PATMATCH|TEST_ARITHEXP|TEST_LOCALE)
                                  ? EXECUTION_SUCCESS
                                  : EXECUTION_FAILURE;
          extended_glob = oe;
@@ -3250,7 +3458,6 @@ execute_cond_command (cond_command)
   /* 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);
 
@@ -3304,8 +3511,13 @@ execute_null_command (redirects, pipe_in, pipe_out, async)
      int pipe_in, pipe_out, async;
 {
   int r;
+  int forcefork;
+  REDIRECT *rd;
 
-  if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
+  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. */
@@ -3370,13 +3582,13 @@ fix_assignment_words (words)
 {
   WORD_LIST *w;
   struct builtin *b;
-  int assoc;
+  int assoc, global;
 
   if (words == 0)
     return;
 
   b = 0;
-  assoc = 0;
+  assoc = global = 0;
 
   for (w = words; w; w = w->next)
     if (w->word->flags & W_ASSIGNMENT)
@@ -3393,12 +3605,17 @@ fix_assignment_words (words)
 #if defined (ARRAY_VARS)
        if (assoc)
          w->word->flags |= W_ASSIGNASSOC;
+       if (global)
+         w->word->flags |= W_ASSNGLOBAL;
 #endif
       }
 #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, 'A'))
+    else if (w->word->word[0] == '-' && (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)
          {
@@ -3408,10 +3625,11 @@ fix_assignment_words (words)
            else if (b && (b->flags & ASSIGNMENT_BUILTIN))
              words->word->flags |= W_ASSNBLTIN;
          }
-       if (words->word->flags & W_ASSNBLTIN)
+       if ((words->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'A'))
          assoc = 1;
+       if ((words->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'g'))
+         global = 1;
       }
-#endif
 }
 
 /* Return 1 if the file found by searching $PATH for PATHNAME, defaulting
@@ -3440,13 +3658,14 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
   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;
 
   /* If we're in a function, update the line number information. */
-  if (variable_context && interactive_shell)
+  if (variable_context && interactive_shell && sourcelevel == 0)
     line_number -= function_line_number;
 
   /* Remember what this command line looks like at invocation. */
@@ -3530,6 +3749,10 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
        }
       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 ();
@@ -3685,12 +3908,20 @@ run_builtin:
   if (builtin || func)
     {
       if (builtin)
-       unwind_protect_int (executing_builtin); /* modified in execute_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)
            {
@@ -3756,7 +3987,7 @@ run_builtin:
     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);
 
@@ -3764,6 +3995,11 @@ run_builtin:
   bind_lastarg (lastarg);
   FREE (command_line);
   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);
@@ -3803,25 +4039,35 @@ execute_builtin (builtin, words, flags, subshell)
 {
   int old_e_flag, result, eval_unwind;
   int isbltinenv;
+  char *error_trap;
 
 #if 0
   /* XXX -- added 12/11 */
   terminate_immediately++;
 #endif
 
+  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.  This is also a problem (as
-     below) for the command and source/. builtins. */
+     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);
+      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;
       eval_unwind = 1;
     }
@@ -3859,6 +4105,7 @@ execute_builtin (builtin, words, flags, subshell)
     }
 
   executing_builtin++;
+  executing_command_builtin |= builtin == command_builtin;
   result = ((*builtin) (words->next));
 
   /* This shouldn't happen, but in case `return' comes back instead of
@@ -3872,6 +4119,11 @@ execute_builtin (builtin, words, flags, subshell)
   if (eval_unwind)
     {
       exit_immediately_on_error += old_e_flag;
+      if (error_trap)
+       {
+         set_error_trap (error_trap);
+         xfree (error_trap);
+       }
       discard_unwind_frame ("eval_builtin");
     }
 
@@ -3896,14 +4148,22 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
   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, *bash_source_a, *bash_lineno_a;
+  ARRAY *funcname_a;
+  volatile ARRAY *bash_source_a;
+  volatile ARRAY *bash_lineno_a;
 #endif
   FUNCTION_DEF *shell_fn;
   char *sfile, *t;
-  static int funcnest = 0;
 
   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);
@@ -3925,6 +4185,7 @@ 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 */
@@ -4066,7 +4327,6 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
   if (subshell == 0)
     run_unwind_frame ("function_calling");
 
-  funcnest--;
 #if defined (ARRAY_VARS)
   /* These two variables cannot be unset, and cannot be affected by the
      function. */
@@ -4081,8 +4341,13 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
 #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);
 }
 
@@ -4223,17 +4488,28 @@ execute_builtin_or_function (words, builtin, var, redirects,
 {
   int result;
   REDIRECT *saved_undo_list;
-  sh_builtin_func_t *saved_this_shell_builtin;
+#if defined (PROCESS_SUBSTITUTION)
+  int ofifo, nfifo, osize;
+  char *ofifo_list;
+#endif
+
+
+#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. */
@@ -4273,11 +4549,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)
@@ -4292,6 +4575,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);
 }
 
@@ -4337,7 +4628,7 @@ setup_async_signals ()
 #  define NOTFOUND_HOOK "command_not_found_handle"
 #endif
 
-static void
+static int
 execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
                      async, fds_to_close, cmdflags)
      WORD_LIST *words;
@@ -4348,7 +4639,7 @@ 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;
@@ -4356,13 +4647,14 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
   nofork = (cmdflags & CMD_NO_FORK);  /* Don't fork, just exec, if no pipes */
   pathname = words->word->word;
 
+  result = EXECUTION_SUCCESS;
 #if defined (RESTRICTED_SHELL)
   command = (char *)NULL;
-  if (restricted && xstrchr (pathname, '/'))
+  if (restricted && mbschr (pathname, '/'))
     {
       internal_error (_("%s: restricted: cannot specify `/' in command names"),
                    pathname);
-      last_command_exit_value = EXECUTION_FAILURE;
+      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).  */
@@ -4402,6 +4694,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
 #endif
 #endif
 
+      reset_terminating_signals ();    /* XXX */
       /* Cancel traps, in trap.c. */
       restore_original_signals ();
 
@@ -4451,6 +4744,9 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
          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 */
            }
@@ -4471,9 +4767,11 @@ parent_return:
       /* 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);
     }
 }
 
@@ -4620,7 +4918,7 @@ initialize_subshell ()
   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 */
 
@@ -4672,7 +4970,11 @@ shell_execve (command, args, env)
   if (i != ENOEXEC)
     {
       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;
@@ -4869,6 +5171,10 @@ do_piping (pipe_in, pipe_out)
        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)
     {
@@ -4884,5 +5190,11 @@ do_piping (pipe_in, pipe_out)
          if (dup2 (1, 2) < 0)
            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__ */
     }
 }