Imported from ../bash-2.05.tar.gz.
[platform/upstream/bash.git] / execute_cmd.c
index f2a2392..79abc28 100644 (file)
@@ -6,7 +6,7 @@
 
    Bash is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 1, or (at your option)
+   the Free Software Foundation; either version 2, or (at your option)
    any later version.
 
    Bash is distributed in the hope that it will be useful, but WITHOUT
@@ -16,7 +16,7 @@
 
    You should have received a copy of the GNU General Public License
    along with Bash; see the file COPYING.  If not, write to the Free
-   Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
 #include "config.h"
 
 #if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
 #  include <limits.h>
 #endif
 
-/* Some systems require this, mostly for the definition of `struct timezone'.
-   For example, Dynix/ptx has that definition in <time.h> rather than
-   sys/time.h */
-#if defined (TIME_WITH_SYS_TIME)
-#  include <sys/time.h>
-#  include <time.h>
-#else
-#  if defined (HAVE_SYS_TIME_H)
-#    include <sys/time.h>
-#  else 
-#    include <time.h>
-#  endif
-#endif
+#include "posixtime.h"
 
 #if defined (HAVE_SYS_RESOURCE_H) && !defined (RLIMTYPE)
 #  include <sys/resource.h>
@@ -145,6 +133,9 @@ static int execute_cond_command ();
 #if defined (COMMAND_TIMING)
 static int time_command ();
 #endif
+#if defined (ARITH_FOR_COMMAND)
+static int execute_arith_for_command ();
+#endif
 static int execute_case_command ();
 static int execute_while_command (), execute_until_command ();
 static int execute_while_or_until ();
@@ -158,6 +149,8 @@ static void execute_disk_command ();
 static int execute_connection ();
 static int execute_intern_function ();
 
+static int execute_in_subshell ();
+
 /* The line number that the currently executing function starts on. */
 static int function_line_number;
 
@@ -196,6 +189,9 @@ REDIRECT *exec_redirection_undo_list = (REDIRECT *)NULL;
    environment. */
 int subshell_environment;
 
+/* Currently-executing shell function. */
+SHELL_VAR *this_shell_function;
+
 struct fd_bitmap *current_fds_to_close = (struct fd_bitmap *)NULL;
 
 #define FD_BITMAP_DEFAULT_SIZE 32L
@@ -287,7 +283,10 @@ execute_command (command)
   discard_unwind_frame ("execute-command");
 
 #if defined (PROCESS_SUBSTITUTION)
-  unlink_fifo_list ();
+  /* don't unlink fifos if we're in a shell function; wait until the function
+     returns. */
+  if (variable_context == 0)
+    unlink_fifo_list ();
 #endif /* PROCESS_SUBSTITUTION */
 
   return (result);
@@ -301,6 +300,9 @@ shell_control_structure (type)
   switch (type)
     {
     case cm_for:
+#if defined (ARITH_FOR_COMMAND)
+    case cm_arith_for:
+#endif
 #if defined (SELECT_COMMAND)
     case cm_select:
 #endif
@@ -443,8 +445,11 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
   /* If a command was being explicitly run in a subshell, or if it is
      a shell control-structure, and it has a pipe, then we do the command
      in a subshell. */
+  if (command->type == cm_subshell && (command->flags & CMD_NO_FORK))
+    return (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
 
-  if ((command->flags & (CMD_WANT_SUBSHELL|CMD_FORCE_SUBSHELL)) ||
+  if (command->type == cm_subshell ||
+      (command->flags & (CMD_WANT_SUBSHELL|CMD_FORCE_SUBSHELL)) ||
       (shell_control_structure (command->type) &&
        (pipe_out != NO_PIPE || pipe_in != NO_PIPE || asynchronous)))
     {
@@ -455,134 +460,8 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
       paren_pid = make_child (savestring (make_command_string (command)),
                              asynchronous);
       if (paren_pid == 0)
-       {
-         int user_subshell, return_code, function_value, should_redir_stdin;
-
-         should_redir_stdin = (asynchronous && (command->flags & CMD_STDIN_REDIR) &&
-                                 pipe_in == NO_PIPE &&
-                                 stdin_redirects (command->redirects) == 0);
-
-         user_subshell = (command->flags & CMD_WANT_SUBSHELL) != 0;
-         command->flags &= ~(CMD_FORCE_SUBSHELL | CMD_WANT_SUBSHELL | CMD_INVERT_RETURN);
-
-         /* If a command is asynchronous in a subshell (like ( foo ) & or
-            the special case of an asynchronous GROUP command where the
-            the subshell bit is turned on down in case cm_group: below),
-            turn off `asynchronous', so that two subshells aren't spawned.
-
-            This seems semantically correct to me.  For example,
-            ( foo ) & seems to say ``do the command `foo' in a subshell
-            environment, but don't wait for that subshell to finish'',
-            and "{ foo ; bar } &" seems to me to be like functions or
-            builtins in the background, which executed in a subshell
-            environment.  I just don't see the need to fork two subshells. */
-
-         /* Don't fork again, we are already in a subshell.  A `doubly
-            async' shell is not interactive, however. */
-         if (asynchronous)
-           {
-#if defined (JOB_CONTROL)
-             /* If a construct like ( exec xxx yyy ) & is given while job
-                control is active, we want to prevent exec from putting the
-                subshell back into the original process group, carefully
-                undoing all the work we just did in make_child. */
-             original_pgrp = -1;
-#endif /* JOB_CONTROL */
-             interactive_shell = 0;
-             expand_aliases = 0;
-             asynchronous = 0;
-           }
-
-         /* Subshells are neither login nor interactive. */
-         login_shell = interactive = 0;
-
-         subshell_environment = user_subshell ? SUBSHELL_PAREN : SUBSHELL_ASYNC;
-
-         reset_terminating_signals ();         /* in shell.c */
-         /* Cancel traps, in trap.c. */
-         restore_original_signals ();
-         if (asynchronous)
-           setup_async_signals ();
-
-#if defined (JOB_CONTROL)
-         set_sigchld_handler ();
-#endif /* JOB_CONTROL */
-
-         set_sigint_handler ();
-
-#if defined (JOB_CONTROL)
-         /* Delete all traces that there were any jobs running.  This is
-            only for subshells. */
-         without_job_control ();
-#endif /* JOB_CONTROL */
-         do_piping (pipe_in, pipe_out);
-
-         /* If this is a user subshell, set a flag if stdin was redirected.
-            This is used later to decide whether to redirect fd 0 to
-            /dev/null for async commands in the subshell.  This adds more
-            sh compatibility, but I'm not sure it's the right thing to do. */
-         if (user_subshell)
-           {
-             stdin_redir = stdin_redirects (command->redirects);
-             restore_default_signal (0);
-           }
-
-         if (fds_to_close)
-           close_fd_bitmap (fds_to_close);
-
-         /* If this is an asynchronous command (command &), we want to
-            redirect the standard input from /dev/null in the absence of
-            any specific redirection involving stdin. */
-         if (should_redir_stdin && stdin_redir == 0)
-           async_redirect_stdin ();
-
-         /* Do redirections, then dispose of them before recursive call. */
-         if (command->redirects)
-           {
-             if (do_redirections (command->redirects, 1, 0, 0) != 0)
-               exit (EXECUTION_FAILURE);
-
-             dispose_redirects (command->redirects);
-             command->redirects = (REDIRECT *)NULL;
-           }
-
-         /* If this is a simple command, tell execute_disk_command that it
-            might be able to get away without forking and simply exec.
-            This means things like ( sleep 10 ) will only cause one fork.
-            If we're timing the command, however, we cannot do this
-            optimization. */
-#if 0
-         if (user_subshell && command->type == cm_simple)
-#else
-         if (user_subshell && command->type == cm_simple && (command->flags & CMD_TIME_PIPELINE) == 0)
-#endif
-           {
-             command->flags |= CMD_NO_FORK;
-             command->value.Simple->flags |= CMD_NO_FORK;
-           }
-
-         /* If we're inside a function while executing this subshell, we
-            need to handle a possible `return'. */
-         function_value = 0;
-         if (return_catch_flag)
-           function_value = setjmp (return_catch);
-
-         if (function_value)
-           return_code = return_catch_value;
-         else
-           return_code = execute_command_internal
-             (command, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
-
-         /* If we were explicitly placed in a subshell with (), we need
-            to do the `shell cleanup' things, such as running traps[0]. */
-         if (user_subshell && signal_is_trapped (0))
-           {
-             last_command_exit_value = return_code;
-             return_code = run_exit_trap ();
-           }
-
-         exit (return_code);
-       }
+       exit (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
+       /* NOTREACHED */
       else
        {
          close_pipes (pipe_in, pipe_out);
@@ -606,11 +485,13 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 
              /* If we have to, invert the return value. */
              if (invert)
-               return ((last_command_exit_value == EXECUTION_SUCCESS)
-                         ? EXECUTION_FAILURE
-                         : EXECUTION_SUCCESS);
+               exec_result = ((last_command_exit_value == EXECUTION_SUCCESS)
+                               ? EXECUTION_FAILURE
+                               : EXECUTION_SUCCESS);
              else
-               return (last_command_exit_value);
+               exec_result = last_command_exit_value;
+
+             return (last_command_exit_value = exec_result);
            }
          else
            {
@@ -748,7 +629,10 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
       }
 
       if (was_debug_trap)
-       run_debug_trap ();
+       {
+         last_command_exit_value = exec_result;
+         run_debug_trap ();
+       }
 
       if (ignore_return == 0 && invert == 0 &&
          ((posixly_correct && interactive == 0 && special_builtin_failed) ||
@@ -767,6 +651,14 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
       exec_result = execute_for_command (command->value.For);
       break;
 
+#if defined (ARITH_FOR_COMMAND)
+    case cm_arith_for:
+      if (ignore_return)
+       command->value.ArithFor->flags |= CMD_IGNORE_RETURN;
+      exec_result = execute_arith_for_command (command->value.ArithFor);
+      break;
+#endif
+
 #if defined (SELECT_COMMAND)
     case cm_select:
       if (ignore_return)
@@ -899,77 +791,12 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
 }
 
 #if defined (COMMAND_TIMING)
-#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
-static struct timeval *
-difftimeval (d, t1, t2)
-     struct timeval *d, *t1, *t2;
-{
-  d->tv_sec = t2->tv_sec - t1->tv_sec;
-  d->tv_usec = t2->tv_usec - t1->tv_usec;
-  if (d->tv_usec < 0)
-    {
-      d->tv_usec += 1000000;
-      d->tv_sec -= 1;
-      if (d->tv_sec < 0)               /* ??? -- BSD/OS does this */
-       {
-         d->tv_sec = 0;
-         d->tv_usec = 0;
-       }
-    }
-  return d;
-}
-
-static struct timeval *
-addtimeval (d, t1, t2)
-     struct timeval *d, *t1, *t2;
-{
-  d->tv_sec = t1->tv_sec + t2->tv_sec;
-  d->tv_usec = t1->tv_usec + t2->tv_usec;
-  if (d->tv_usec >= 1000000)
-    {
-      d->tv_usec -= 1000000;
-      d->tv_sec += 1;
-    }
-  return d;
-}
-
-/* Do "cpu = ((user + sys) * 10000) / real;" with timevals.
-   Barely-tested code from Deven T. Corzine <deven@ties.org>. */
-static int
-timeval_to_cpu (rt, ut, st)
-     struct timeval *rt, *ut, *st;     /* real, user, sys */
-{
-  struct timeval t1, t2;
-  register int i;
-
-  addtimeval (&t1, ut, st);
-  t2.tv_sec = rt->tv_sec;
-  t2.tv_usec = rt->tv_usec;
 
-  for (i = 0; i < 6; i++)
-    {
-      if ((t1.tv_sec > 99999999) || (t2.tv_sec > 99999999))
-       break;
-      t1.tv_sec *= 10;
-      t1.tv_sec += t1.tv_usec / 100000;
-      t1.tv_usec *= 10;
-      t1.tv_usec %= 1000000;
-      t2.tv_sec *= 10;
-      t2.tv_sec += t2.tv_usec / 100000;
-      t2.tv_usec *= 10;
-      t2.tv_usec %= 1000000;
-    }
-  for (i = 0; i < 4; i++)
-    {
-      if (t1.tv_sec < 100000000)
-       t1.tv_sec *= 10;
-      else
-       t2.tv_sec /= 10;
-    }
-
-  return ((t2.tv_sec == 0) ? 0 : t1.tv_sec / t2.tv_sec);
-}  
-#endif /* HAVE_GETRUSAGE && HAVE_GETTIMEOFDAY */
+#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
+extern struct timeval *difftimeval();
+extern struct timeval *addtimeval();
+extern int timeval_to_cpu();
+#endif
 
 #define POSIX_TIMEFORMAT "real %2R\nuser %2U\nsys %2S"
 #define BASH_TIMEFORMAT  "\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS"
@@ -1229,6 +1056,165 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
 }
 #endif /* COMMAND_TIMING */
 
+/* Execute a command that's supposed to be in a subshell.  This must be
+   called after make_child and we must be running in the child process.
+   The caller will return or exit() immediately with the value this returns. */
+static int
+execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
+     COMMAND *command;
+     int asynchronous;
+     int pipe_in, pipe_out;
+     struct fd_bitmap *fds_to_close;
+{
+  int user_subshell, return_code, function_value, should_redir_stdin, invert;
+  int ois;
+  COMMAND *tcom;
+
+  should_redir_stdin = (asynchronous && (command->flags & CMD_STDIN_REDIR) &&
+                         pipe_in == NO_PIPE &&
+                         stdin_redirects (command->redirects) == 0);
+
+  invert = (command->flags & CMD_INVERT_RETURN) != 0;
+  user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0);
+
+  command->flags &= ~(CMD_FORCE_SUBSHELL | CMD_WANT_SUBSHELL | CMD_INVERT_RETURN);
+
+  /* If a command is asynchronous in a subshell (like ( foo ) & or
+     the special case of an asynchronous GROUP command where the
+     the subshell bit is turned on down in case cm_group: below),
+     turn off `asynchronous', so that two subshells aren't spawned.
+
+     This seems semantically correct to me.  For example,
+     ( foo ) & seems to say ``do the command `foo' in a subshell
+     environment, but don't wait for that subshell to finish'',
+     and "{ foo ; bar ; } &" seems to me to be like functions or
+     builtins in the background, which executed in a subshell
+     environment.  I just don't see the need to fork two subshells. */
+
+  /* Don't fork again, we are already in a subshell.  A `doubly
+     async' shell is not interactive, however. */
+  if (asynchronous)
+    {
+#if defined (JOB_CONTROL)
+      /* If a construct like ( exec xxx yyy ) & is given while job
+        control is active, we want to prevent exec from putting the
+        subshell back into the original process group, carefully
+        undoing all the work we just did in make_child. */
+      original_pgrp = -1;
+#endif /* JOB_CONTROL */
+      ois = interactive_shell;
+      interactive_shell = 0;
+      /* This test is to prevent alias expansion by interactive shells that
+        run `(command) &' but to allow scripts that have enabled alias
+        expansion with `shopt -s expand_alias' to continue to expand
+        aliases. */
+      if (ois != interactive_shell)
+       expand_aliases = 0;
+      asynchronous = 0;
+    }
+
+  /* Subshells are neither login nor interactive. */
+  login_shell = interactive = 0;
+
+  subshell_environment = user_subshell ? SUBSHELL_PAREN : SUBSHELL_ASYNC;
+
+  reset_terminating_signals ();                /* in sig.c */
+  /* Cancel traps, in trap.c. */
+  restore_original_signals ();
+  if (asynchronous)
+    setup_async_signals ();
+
+#if defined (JOB_CONTROL)
+  set_sigchld_handler ();
+#endif /* JOB_CONTROL */
+
+  set_sigint_handler ();
+
+#if defined (JOB_CONTROL)
+  /* Delete all traces that there were any jobs running.  This is
+     only for subshells. */
+  without_job_control ();
+#endif /* JOB_CONTROL */
+
+  if (fds_to_close)
+    close_fd_bitmap (fds_to_close);
+
+  do_piping (pipe_in, pipe_out);
+
+  /* If this is a user subshell, set a flag if stdin was redirected.
+     This is used later to decide whether to redirect fd 0 to
+     /dev/null for async commands in the subshell.  This adds more
+     sh compatibility, but I'm not sure it's the right thing to do. */
+  if (user_subshell)
+    {
+      stdin_redir = stdin_redirects (command->redirects);
+      restore_default_signal (0);
+    }
+
+  /* If this is an asynchronous command (command &), we want to
+     redirect the standard input from /dev/null in the absence of
+     any specific redirection involving stdin. */
+  if (should_redir_stdin && stdin_redir == 0)
+    async_redirect_stdin ();
+
+  /* Do redirections, then dispose of them before recursive call. */
+  if (command->redirects)
+    {
+      if (do_redirections (command->redirects, 1, 0, 0) != 0)
+       exit (invert ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
+
+      dispose_redirects (command->redirects);
+      command->redirects = (REDIRECT *)NULL;
+    }
+
+  tcom = (command->type == cm_subshell) ? command->value.Subshell->command : command;
+
+  /* If this is a simple command, tell execute_disk_command that it
+     might be able to get away without forking and simply exec.
+     This means things like ( sleep 10 ) will only cause one fork.
+     If we're timing the command or inverting its return value, however,
+     we cannot do this optimization. */
+  if (user_subshell && (tcom->type == cm_simple || tcom->type == cm_subshell) &&
+      ((tcom->flags & CMD_TIME_PIPELINE) == 0) &&
+      ((tcom->flags & CMD_INVERT_RETURN) == 0))
+    {
+      tcom->flags |= CMD_NO_FORK;
+      if (tcom->type == cm_simple)
+       tcom->value.Simple->flags |= CMD_NO_FORK;
+    }
+
+  invert = (tcom->flags & CMD_INVERT_RETURN) != 0;
+  tcom->flags &= ~CMD_INVERT_RETURN;
+
+  /* If we're inside a function while executing this subshell, we
+     need to handle a possible `return'. */
+  function_value = 0;
+  if (return_catch_flag)
+    function_value = setjmp (return_catch);
+
+  if (function_value)
+    return_code = return_catch_value;
+  else
+    return_code = execute_command_internal
+      (tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
+
+  /* If we are asked to, invert the return value. */
+  if (invert)
+    return_code = (return_code == EXECUTION_SUCCESS) ? EXECUTION_FAILURE
+                                                    : EXECUTION_SUCCESS;
+
+  /* If we were explicitly placed in a subshell with (), we need
+     to do the `shell cleanup' things, such as running traps[0]. */
+  if (user_subshell && signal_is_trapped (0))
+    {
+      last_command_exit_value = return_code;
+      return_code = run_exit_trap ();
+    }
+
+  return (return_code);
+  /* NOTREACHED */
+}
+
 static int
 execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
      COMMAND *command;
@@ -1489,24 +1475,13 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
   return exec_result;
 }
 
-#if defined (JOB_CONTROL)
-#  define REAP() \
-       do \
-         { \
-           if (!interactive_shell) \
-             reap_dead_jobs (); \
-         } \
-       while (0)
-#else /* !JOB_CONTROL */
-#  define REAP() \
-       do \
-         { \
-           if (!interactive_shell) \
-             cleanup_dead_jobs (); \
-         } \
-       while (0)
-#endif /* !JOB_CONTROL */
-
+#define REAP() \
+  do \
+    { \
+      if (!interactive_shell) \
+       reap_dead_jobs (); \
+    } \
+  while (0)
 
 /* Execute a FOR command.  The syntax is: FOR word_desc IN word_list;
    DO command; DONE */
@@ -1557,9 +1532,9 @@ execute_for_command (for_command)
       QUIT;
       this_command_name = (char *)NULL;
       v = bind_variable (identifier, list->word->word);
-      if (readonly_p (v))
+      if (readonly_p (v) || noassign_p (v))
        {
-         if (interactive_shell == 0 && posixly_correct)
+         if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
            {
              last_command_exit_value = EXECUTION_FAILURE;
              jump_to_top_level (FORCE_EOF);
@@ -1612,6 +1587,107 @@ execute_for_command (for_command)
   return (retval);
 }
 
+#if defined (ARITH_FOR_COMMAND)
+/* Execute an arithmetic for command.  The syntax is
+
+       for (( init ; step ; test ))
+       do
+               body
+       done
+
+   The execution should be exactly equivalent to
+
+       eval \(\( init \)\)
+       while eval \(\( test \)\) ; do
+               body;
+               eval \(\( step \)\)
+       done
+*/
+static long
+eval_arith_for_expr (l, okp)
+     WORD_LIST *l;
+     int *okp;
+{
+  WORD_LIST *new;
+  long expresult;
+
+  new = expand_words_no_vars (l);
+  if (echo_command_at_execute)
+    xtrace_print_arith_cmd (new);
+  expresult = evalexp (new->word->word, okp);
+  dispose_words (new);
+  return (expresult);
+}
+
+static int
+execute_arith_for_command (arith_for_command)
+     ARITH_FOR_COM *arith_for_command;
+{
+  long expresult;
+  int expok, result, body_status;
+
+  body_status = EXECUTION_SUCCESS;
+  loop_level++;
+
+  if (arith_for_command->flags & CMD_IGNORE_RETURN)
+    arith_for_command->action->flags |= CMD_IGNORE_RETURN;
+
+  this_command_name = "((";    /* )) for expression error messages */
+
+  if (variable_context)
+    line_number = arith_for_command->line - function_line_number;
+
+  /* Evaluate the initialization expression. */
+  expresult = eval_arith_for_expr (arith_for_command->init, &expok);
+  if (expok == 0)
+    return (EXECUTION_FAILURE);
+
+  while (1)
+    {
+      /* Evaluate the test expression. */
+      expresult = eval_arith_for_expr (arith_for_command->test, &expok);
+      if (expok == 0)
+       {
+         body_status = EXECUTION_FAILURE;
+         break;
+       }
+      REAP ();
+      if (expresult == 0)
+       break;
+
+      /* Execute the body of the arithmetic for command. */
+      QUIT;
+      body_status = execute_command (arith_for_command->action);
+      QUIT;
+
+      /* Handle any `break' or `continue' commands executed by the body. */
+      if (breaking)
+       {
+         breaking--;
+         break;
+       }
+
+      if (continuing)
+       {
+         continuing--;
+         if (continuing)
+           break;
+       }
+
+      /* Evaluate the step expression. */
+      expresult = eval_arith_for_expr (arith_for_command->step, &expok);
+      if (expok == 0)
+       {
+         body_status = EXECUTION_FAILURE;
+         break;
+       }
+    }
+
+  loop_level--;
+  return (body_status);
+}
+#endif
+
 #if defined (SELECT_COMMAND)
 static int LINES, COLS, tabsize;
 
@@ -1831,9 +1907,9 @@ execute_select_command (select_command)
        break;
 
       v = bind_variable (identifier, selection);
-      if (readonly_p (v))
+      if (readonly_p (v) || noassign_p (v))
        {
-         if (interactive_shell == 0 && posixly_correct)
+         if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
            {
              last_command_exit_value = EXECUTION_FAILURE;
              jump_to_top_level (FORCE_EOF);
@@ -1863,6 +1939,13 @@ execute_select_command (select_command)
          breaking--;
          break;
        }
+
+      if (continuing)
+       {
+         continuing--;
+         if (continuing)
+           break;
+       }
     }
 
   loop_level--;
@@ -1894,7 +1977,7 @@ execute_case_command (case_command)
       case_command->word->word = word;
     }
 
-  wlist = expand_word_no_split (case_command->word, 0);
+  wlist = expand_word_unsplit (case_command->word, 0);
   word = wlist ? string_list (wlist) : savestring ("");
   dispose_words (wlist);
 
@@ -2198,7 +2281,7 @@ bind_lastarg (arg)
   if (arg == 0)
     arg = "";
   var = bind_variable ("_", arg);
-  var->attributes &= ~att_exported;
+  VUNSETATTR (var, att_exported);
 }
 
 /* Execute a null command.  Fork a subshell if the command uses pipes or is
@@ -2207,7 +2290,8 @@ bind_lastarg (arg)
 static int
 execute_null_command (redirects, pipe_in, pipe_out, async, old_last_command_subst_pid)
      REDIRECT *redirects;
-     int pipe_in, pipe_out, async, old_last_command_subst_pid;
+     int pipe_in, pipe_out, async;
+     pid_t old_last_command_subst_pid;
 {
   if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
     {
@@ -2330,16 +2414,29 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
       /* XXX memory leak if expand_words() error causes a jump_to_top_level */
       command_line = savestring (the_printed_command);
 
+      /* Do this now, because execute_disk_command will do it anyway in the
+        vast majority of cases. */
+      maybe_make_export_env ();
+
       if (make_child (command_line, async) == 0)
        {
          already_forked = 1;
          simple_command->flags |= CMD_NO_FORK;
 
+         subshell_environment = (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
+                                       ? (SUBSHELL_PIPE|SUBSHELL_FORK)
+                                       : (SUBSHELL_ASYNC|SUBSHELL_FORK);
+
+         /* We need to do this before piping to handle some really
+            pathological cases where one of the pipe file descriptors
+            is < 2. */
+         if (fds_to_close)
+           close_fd_bitmap (fds_to_close);
+
          do_piping (pipe_in, pipe_out);
          pipe_in = pipe_out = -1;
 
          last_asynchronous_pid = old_last_async_pid;
-         subshell_environment = async ? SUBSHELL_ASYNC : SUBSHELL_FORK;
        }
       else
        {
@@ -2506,7 +2603,7 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
 
   if (builtin || func)
     {
-       if (already_forked)
+      if (already_forked)
        {
          /* reset_terminating_signals (); */   /* XXX */
          /* Cancel traps, in trap.c. */
@@ -2639,14 +2736,26 @@ execute_builtin (builtin, words, flags, subshell)
            add_unwind_protect (dispose_builtin_env, (char *)NULL);
          dispose_used_env_vars ();
        }
-#if 0
-      else
-       builtin_env = (char **)NULL;
-#endif
+      /* Otherwise we inherit builtin_env from our caller. */
+    }
+
+  /* `return' does a longjmp() back to a saved environment in execute_function.
+     If a variable assignment list preceded the command, and the shell is
+     running in POSIX mode, we need to merge that into the shell_variables
+     table, since `return' is a POSIX special builtin. */
+  if (posixly_correct && subshell == 0 && builtin == return_builtin && temporary_env)
+    {
+      begin_unwind_frame ("return_temp_env");
+      add_unwind_protect (merge_temporary_env, (char *)NULL);
     }
 
   result = ((*builtin) (words->next));
 
+  /* This shouldn't happen, but in case `return' comes back instead of
+     longjmp'ing, we need to unwind. */
+  if (posixly_correct && subshell == 0 && builtin == return_builtin && temporary_env)
+    discard_unwind_frame ("return_temp_env");
+
   if (subshell == 0 && (builtin == source_builtin || builtin == eval_builtin))
     {
       /* In POSIX mode, if any variable assignments precede the `.' or
@@ -2654,12 +2763,8 @@ execute_builtin (builtin, words, flags, subshell)
         and `eval' are special builtins. */
       if (posixly_correct && builtin_env)
        merge_builtin_env ();
-#if 0
-      dispose_builtin_env ();
-      discard_unwind_frame ("builtin_env");
-#else
+
       run_unwind_frame ("builtin_env");
-#endif
     }
 
   if (eval_unwind)
@@ -2681,6 +2786,7 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
   int return_val, result;
   COMMAND *tc, *fc;
   char *debug_trap;
+  SHELL_VAR *old_shell_function;
 
   tc = (COMMAND *)copy_command (function_cell (var));
   if (tc && (flags & CMD_IGNORE_RETURN))
@@ -2695,9 +2801,13 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
       unwind_protect_int (return_catch_flag);
       unwind_protect_jmp_buf (return_catch);
       add_unwind_protect (dispose_command, (char *)tc);
+      unwind_protect_pointer (this_shell_function);
       unwind_protect_int (loop_level);
     }
 
+  this_shell_function = var;
+  make_funcname_visible (1);
+
   debug_trap = (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
                        ? trap_list[DEBUG_TRAP]
                        : (char *)NULL;
@@ -2721,14 +2831,19 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
   if (temporary_env)
     {
       function_env = copy_array (temporary_env);
+      /* In POSIX mode, variable assignments preceding function names are
+        supposed to persist in the environment after the function returns,
+        as if a special builtin command had been executed. */
       if (subshell == 0)
-       add_unwind_protect (dispose_function_env, (char *)NULL);
+       {
+         if (posixly_correct)
+           add_unwind_protect (merge_function_env, (char *)NULL);
+         else
+           add_unwind_protect (dispose_function_env, (char *)NULL);
+       }
       dispose_used_env_vars ();
     }
-#if 0
-  else
-    function_env = (char **)NULL;
-#endif
+    /* Otherwise, we inherit function_env from our caller. */
 
   remember_args (words->next, 1);
 
@@ -2761,9 +2876,34 @@ execute_function (var, words, flags, fds_to_close, async, subshell)
   if (subshell == 0)
     run_unwind_frame ("function_calling");
 
+  if (variable_context == 0 || this_shell_function == 0)
+    make_funcname_visible (0);
+
   return (result);
 }
 
+/* A convenience routine for use by other parts of the shell to execute
+   a particular shell function. */
+int
+execute_shell_function (var, words)
+     SHELL_VAR *var;
+     WORD_LIST *words;
+{
+  int ret;
+  struct fd_bitmap *bitmap;
+
+  bitmap = new_fd_bitmap (FD_BITMAP_DEFAULT_SIZE);
+  begin_unwind_frame ("execute-shell-function");
+  add_unwind_protect (dispose_fd_bitmap, (char *)bitmap);
+      
+  ret = execute_function (var, words, 0, bitmap, 0, 0);
+
+  dispose_fd_bitmap (bitmap);
+  discard_unwind_frame ("execute-shell-function");
+
+  return ret;
+}
+
 /* Execute a shell builtin or function in a subshell environment.  This
    routine does not return; it only calls exit().  If BUILTIN is non-null,
    it points to a function to call to execute a shell builtin; otherwise
@@ -2782,13 +2922,16 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
      struct fd_bitmap *fds_to_close;
      int flags;
 {
-  int result, r, jobs_hack;
-
-  /* A subshell is neither a login shell nor interactive. */
-  login_shell = interactive = 0;
+  int result, r;
+#if defined (JOB_CONTROL)
+  int jobs_hack;
 
   jobs_hack = (builtin == jobs_builtin) &&
                ((subshell_environment & SUBSHELL_ASYNC) == 0 || pipe_out != NO_PIPE);
+#endif
+
+  /* A subshell is neither a login shell nor interactive. */
+  login_shell = interactive = 0;
 
   subshell_environment = SUBSHELL_ASYNC;
 
@@ -2810,11 +2953,11 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var,
 
   set_sigint_handler ();
 
-  do_piping (pipe_in, pipe_out);
-
   if (fds_to_close)
     close_fd_bitmap (fds_to_close);
 
+  do_piping (pipe_in, pipe_out);
+
   if (do_redirections (redirects, 1, 0, 0) != 0)
     exit (EXECUTION_FAILURE);
 
@@ -2860,6 +3003,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
 {
   int result;
   REDIRECT *saved_undo_list;
+  Function *saved_this_shell_builtin;
 
   if (do_redirections (redirects, 1, 1, 0) != 0)
     {
@@ -2869,6 +3013,7 @@ execute_builtin_or_function (words, builtin, var, redirects,
       return (EX_REDIRFAIL);   /* was EXECUTION_FAILURE */
     }
 
+  saved_this_shell_builtin = this_shell_builtin;
   saved_undo_list = redirection_undo_list;
 
   /* Calling the "exec" builtin changes redirections forever. */
@@ -2894,6 +3039,21 @@ execute_builtin_or_function (words, builtin, var, redirects,
   else
     result = execute_function (var, words, flags, fds_to_close, 0, 0);
 
+  /* If we are executing the `command' builtin, but this_shell_builtin is
+     set to `exec_builtin', we know that we have something like
+     `command exec [redirection]', since otherwise `exec' would have
+     overwritten the shell and we wouldn't get here.  In this case, we
+     want to behave as if the `command' builtin had not been specified
+     and preserve the redirections. */
+  if (builtin == command_builtin && this_shell_builtin == exec_builtin)
+    {
+      if (saved_undo_list)
+       dispose_redirects (saved_undo_list);
+      redirection_undo_list = exec_redirection_undo_list;
+      saved_undo_list = exec_redirection_undo_list = (REDIRECT *)NULL;      
+      discard_unwind_frame ("saved_redirects");
+    }
+
   if (saved_undo_list)
     {
       redirection_undo_list = saved_undo_list;
@@ -2955,7 +3115,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
 {
   char *pathname, *command, **args;
   int nofork;
-  int pid;
+  pid_t pid;
 
   nofork = (cmdflags & CMD_NO_FORK);  /* Don't fork, just exec, if no pipes */
   pathname = words->word->word;
@@ -2978,7 +3138,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
       put_command_name_into_env (command);
     }
 
-  /* We have to make the child before we check for the non-existance
+  /* We have to make the child before we check for the non-existence
      of COMMAND, since we want the error messages to be redirected. */
   /* If we can get away without forking and there are no pipes to deal with,
      don't bother to fork, just directly exec the command. */
@@ -3014,6 +3174,14 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
          setup_async_signals ();
        }
 
+      /* This functionality is now provided by close-on-exec of the
+        file descriptors manipulated by redirection and piping.
+        Some file descriptors still need to be closed in all children
+        because of the way bash does pipes; fds_to_close is a
+        bitmap of all such file descriptors. */
+      if (fds_to_close)
+       close_fd_bitmap (fds_to_close);
+
       do_piping (pipe_in, pipe_out);
 
       if (async)
@@ -3024,14 +3192,6 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
 
       subshell_environment = SUBSHELL_FORK;
 
-      /* This functionality is now provided by close-on-exec of the
-        file descriptors manipulated by redirection and piping.
-        Some file descriptors still need to be closed in all children
-        because of the way bash does pipes; fds_to_close is a
-        bitmap of all such file descriptors. */
-      if (fds_to_close)
-       close_fd_bitmap (fds_to_close);
-
       if (redirects && (do_redirections (redirects, 1, 0, 0) != 0))
        {
 #if defined (PROCESS_SUBSTITUTION)
@@ -3078,6 +3238,24 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
 
    The word immediately following the #! is the interpreter to execute.
    A single argument to the interpreter is allowed. */
+
+/* CPP defines to decide whether a particular index into the #! line
+   corresponds to a valid interpreter name or argument character, or
+   whitespace.  The MSDOS define is to allow \r to be treated the same
+   as \n. */
+
+#if !defined (MSDOS)
+#  define STRINGCHAR(ind) \
+    (!whitespace (sample[ind]) && sample[ind] != '\n' && ind < sample_len)
+#  define WHITECHAR(ind) \
+    (whitespace (sample[ind]) && sample[ind] != '\n' && ind < sample_len)
+#else  /* MSDOS */
+#  define STRINGCHAR(ind) \
+    (!whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r' && ind < sample_len)
+#  define WHITECHAR(ind) \
+    (whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r' && ind < sample_len)
+#endif /* MSDOS */
+
 static int
 execute_shell_script (sample, sample_len, command, args, env)
      unsigned char *sample;
@@ -3093,37 +3271,24 @@ execute_shell_script (sample, sample_len, command, args, env)
   for (i = 2; whitespace (sample[i]) && i < sample_len; i++)
     ;
 
-  for (start = i;
-       !whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
-       i++)
+  for (start = i; STRINGCHAR(i); i++)
     ;
 
-  larry = i - start;
-  execname = xmalloc (1 + larry);
-  strncpy (execname, (char *)(sample + start), larry);
-  execname[larry] = '\0';
+  execname = substring ((char *)sample, start, i);
   size_increment = 1;
 
   /* Now the argument, if any. */
-  firstarg = (char *)NULL;
-  for (start = i;
-       whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
-       i++)
+  for (firstarg = (char *)NULL, start = i; WHITECHAR(i); i++)
     ;
 
   /* If there is more text on the line, then it is an argument for the
      interpreter. */
-  if (i < sample_len && sample[i] != '\n' && !whitespace (sample[i]))
+
+  if (STRINGCHAR(i))  
     {
-      for (start = i;
-          !whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
-          i++)
+      for (start = i; STRINGCHAR(i); i++)
        ;
-      larry = i - start;
-      firstarg = xmalloc (1 + larry);
-      strncpy (firstarg, (char *)(sample + start), larry);
-      firstarg[larry] = '\0';
-
+      firstarg = substring ((char *)sample, start, i);
       size_increment = 2;
     }
 
@@ -3147,6 +3312,9 @@ execute_shell_script (sample, sample_len, command, args, env)
 
   return (shell_execve (execname, args, env));
 }
+#undef STRINGCHAR
+#undef WHITECHAR
+
 #endif /* !HAVE_HASH_BANG_EXEC */
 
 static void
@@ -3174,6 +3342,17 @@ initialize_subshell ()
   reset_shell_options ();
   reset_shopt_options ();
 
+  /* Zero out builtin_env, since this could be a shell script run from a
+     sourced file with a temporary environment supplied to the `source/.'
+     builtin.  Such variables are not supposed to be exported (empirical
+     testing with sh and ksh). */
+  builtin_env = 0;
+
+  clear_unwind_protect_list (0);
+
+  /* We're no longer inside a shell function. */
+  variable_context = return_catch_flag = 0;
+
   /* If we're not interactive, close the file descriptor from which we're
      reading the current shell script. */
   if (interactive_shell == 0)
@@ -3186,6 +3365,20 @@ initialize_subshell ()
 #  define SETOSTYPE(x)
 #endif
 
+#define READ_SAMPLE_BUF(file, buf, len) \
+  do \
+    { \
+      fd = open(file, O_RDONLY); \
+      if (fd >= 0) \
+       { \
+         len = read (fd, (char *)buf, 80); \
+         close (fd); \
+       } \
+      else \
+       len = -1; \
+    } \
+  while (0)
+      
 /* Call execve (), handling interpreting shell scripts, and handling
    exec failures. */
 int
@@ -3195,20 +3388,31 @@ shell_execve (command, args, env)
 {
   struct stat finfo;
   int larray, i, fd;
+  unsigned char sample[80];
+  int sample_len;
 
   SETOSTYPE (0);               /* Some systems use for USG/POSIX semantics */
   execve (command, args, env);
+  i = errno;                   /* error from execve() */
   SETOSTYPE (1);
 
   /* If we get to this point, then start checking out the file.
      Maybe it is something we can hack ourselves. */
-  if (errno != ENOEXEC)
+  if (i != ENOEXEC)
     {
-      i = errno;
       if ((stat (command, &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
        internal_error ("%s: is a directory", command);
       else
        {
+#if defined (HAVE_HASH_BANG_EXEC)
+         READ_SAMPLE_BUF (command, sample, sample_len);
+         if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
+           {
+             errno = i;
+             sys_error ("%s: bad interpreter", command);
+             return (EX_NOEXEC);
+           }
+#endif
          errno = i;
          file_error (command);
        }
@@ -3218,41 +3422,37 @@ shell_execve (command, args, env)
   /* This file is executable.
      If it begins with #!, then help out people with losing operating
      systems.  Otherwise, check to see if it is a binary file by seeing
-     if the first line (or up to 80 characters) are in the ASCII set.
-     Execute the contents as shell commands. */
-  fd = open (command, O_RDONLY);
-  if (fd >= 0)
-    {
-      unsigned char sample[80];
-      int sample_len;
-
-      sample_len = read (fd, (char *)sample, 80);
-      close (fd);
+     if the contents of the first line (or up to 80 characters) are in the
+     ASCII set.  If it's a text file, execute the contents as shell commands,
+     otherwise return 126 (EX_BINARY_FILE). */
+  READ_SAMPLE_BUF (command, sample, sample_len);
 
-      if (sample_len == 0)
-       return (EXECUTION_SUCCESS);
+  if (sample_len == 0)
+    return (EXECUTION_SUCCESS);
 
-      /* Is this supposed to be an executable script?
-        If so, the format of the line is "#! interpreter [argument]".
-        A single argument is allowed.  The BSD kernel restricts
-        the length of the entire line to 32 characters (32 bytes
-        being the size of the BSD exec header), but we allow 80
-        characters. */
-      if (sample_len > 0)
-       {
+  /* Is this supposed to be an executable script?
+     If so, the format of the line is "#! interpreter [argument]".
+     A single argument is allowed.  The BSD kernel restricts
+     the length of the entire line to 32 characters (32 bytes
+     being the size of the BSD exec header), but we allow 80
+     characters. */
+  if (sample_len > 0)
+    {
 #if !defined (HAVE_HASH_BANG_EXEC)
-         if (sample[0] == '#' && sample[1] == '!')
-           return (execute_shell_script (sample, sample_len, command, args, env));
-         else
+      if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
+       return (execute_shell_script (sample, sample_len, command, args, env));
+      else
 #endif
-         if (check_binary_file (sample, sample_len))
-           {
-             internal_error ("%s: cannot execute binary file", command);
-             return (EX_BINARY_FILE);
-           }
+      if (check_binary_file (sample, sample_len))
+       {
+         internal_error ("%s: cannot execute binary file", command);
+         return (EX_BINARY_FILE);
        }
     }
 
+  /* We have committed to attempting to execute the contents of this file
+     as shell commands. */
+
   initialize_subshell ();
 
   set_sigint_handler ();
@@ -3314,9 +3514,10 @@ execute_intern_function (name, function)
     }
 
   var = find_function (name->word);
-  if (var && readonly_p (var))
+  if (var && (readonly_p (var) || noassign_p (var)))
     {
-      internal_error ("%s: readonly function", var->name);
+      if (readonly_p (var))
+       internal_error ("%s: readonly function", var->name);
       return (EXECUTION_FAILURE);
     }
 
@@ -3363,9 +3564,6 @@ do_piping (pipe_in, pipe_out)
        sys_error ("cannot duplicate fd %d to fd 0", pipe_in);
       if (pipe_in > 0)
        close (pipe_in);
-#ifdef __CYGWIN32__
-      setmode (0, O_TEXT);
-#endif
     }
   if (pipe_out != NO_PIPE)
     {
@@ -3375,18 +3573,11 @@ do_piping (pipe_in, pipe_out)
            sys_error ("cannot duplicate fd %d to fd 1", pipe_out);
          if (pipe_out == 0 || pipe_out > 1)
            close (pipe_out);
-#ifdef __CYGWIN32__
-         setmode (1, O_TEXT);
-#endif
        }
       else
        {
          if (dup2 (1, 2) < 0)
            sys_error ("cannot duplicate fd 1 to fd 2");
-#ifdef __CYGWIN32__
-         setmode (1, O_TEXT);
-         setmode (2, O_TEXT);
-#endif
        }
     }
 }