Imported from ../bash-3.2.48.tar.gz.
[platform/upstream/bash.git] / trap.c
diff --git a/trap.c b/trap.c
index 139dc2a..2d5934f 100644 (file)
--- a/trap.c
+++ b/trap.c
@@ -1,13 +1,13 @@
 /* trap.c -- Not the trap command, but useful functions for manipulating
    those objects.  The trap command is in builtins/trap.def. */
 
 /* trap.c -- Not the trap command, but useful functions for manipulating
    those objects.  The trap command is in builtins/trap.def. */
 
-/* Copyright (C) 1987, 1991 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2006 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
 
    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 1, or (at your option) any later
+   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 ANY
    version.
 
    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
 
    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
 
    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. */
+   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
 
 #include "config.h"
 
 
 #include "config.h"
 
-#include <stdio.h>
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
 
 #include "bashtypes.h"
 
 #include "bashtypes.h"
-#include "trap.h"
+#include "bashansi.h"
 
 
-#if defined (HAVE_STRING_H)
-#  include <string.h>
-#else /* !HAVE_STRING_H */
-#  include <strings.h>
-#endif /* !HAVE_STRING_H */
+#include <stdio.h>
+#include <errno.h>
 
 
-#if defined (HAVE_UNISTD_H)
-#  include <unistd.h>
-#endif
+#include "bashintl.h"
+
+#include "trap.h"
 
 #include "shell.h"
 
 #include "shell.h"
+#include "flags.h"
+#include "input.h"     /* for save_token_state, restore_token_state */
 #include "signames.h"
 #include "signames.h"
+#include "builtins.h"
 #include "builtins/common.h"
 #include "builtins/common.h"
+#include "builtins/builtext.h"
+
+#ifndef errno
+extern int errno;
+#endif
 
 /* Flags which describe the current handling state of a signal. */
 #define SIG_INHERITED   0x0    /* Value inherited from parent. */
 
 /* Flags which describe the current handling state of a signal. */
 #define SIG_INHERITED   0x0    /* Value inherited from parent. */
 #define SIG_CHANGED    0x20    /* Trap value changed in trap handler. */
 #define SIG_IGNORED    0x40    /* The signal is currently being ignored. */
 
 #define SIG_CHANGED    0x20    /* Trap value changed in trap handler. */
 #define SIG_IGNORED    0x40    /* The signal is currently being ignored. */
 
+#define SPECIAL_TRAP(s)        ((s) == EXIT_TRAP || (s) == DEBUG_TRAP || (s) == ERROR_TRAP || (s) == RETURN_TRAP)
+
 /* An array of such flags, one for each signal, describing what the
    shell will do with a signal.  DEBUG_TRAP == NSIG; some code below
    assumes this. */
 /* An array of such flags, one for each signal, describing what the
    shell will do with a signal.  DEBUG_TRAP == NSIG; some code below
    assumes this. */
-static int sigmodes[NSIG+1];
+static int sigmodes[BASH_NSIG];
 
 
-static void change_signal (), restore_signal ();
+static void free_trap_command __P((int));
+static void change_signal __P((int, char *));
+
+static void get_original_signal __P((int));
+
+static int _run_trap_internal __P((int, char *));
+
+static void reset_signal __P((int));
+static void restore_signal __P((int));
+static void reset_or_restore_signal_handlers __P((sh_resetsig_func_t *));
 
 /* Variables used here but defined in other files. */
 
 /* Variables used here but defined in other files. */
-extern int interactive_shell, interactive;
-extern int interrupt_immediately;
 extern int last_command_exit_value;
 extern int last_command_exit_value;
+extern int line_number;
+
+extern char *this_command_name;
+extern sh_builtin_func_t *this_shell_builtin;
+extern procenv_t wait_intr_buf;
+extern int return_catch_flag, return_catch_value;
+extern int subshell_level;
 
 /* The list of things to do originally, before we started trapping. */
 SigHandler *original_signals[NSIG];
 
 /* The list of things to do originally, before we started trapping. */
 SigHandler *original_signals[NSIG];
@@ -70,7 +93,7 @@ SigHandler *original_signals[NSIG];
    DEFAULT_SIG, which means do whatever you were going to do before
    you were so rudely interrupted, or IGNORE_SIG, which says ignore
    this signal. */
    DEFAULT_SIG, which means do whatever you were going to do before
    you were so rudely interrupted, or IGNORE_SIG, which says ignore
    this signal. */
-char *trap_list[NSIG+1];
+char *trap_list[BASH_NSIG];
 
 /* A bitmap of signals received for which we have trap handlers. */
 int pending_traps[NSIG];
 
 /* A bitmap of signals received for which we have trap handlers. */
 int pending_traps[NSIG];
@@ -81,16 +104,36 @@ int pending_traps[NSIG];
    trap command (e.g., when `return' is executed in the trap command). */
 int running_trap;
 
    trap command (e.g., when `return' is executed in the trap command). */
 int running_trap;
 
+/* Set to last_command_exit_value before running a trap. */
+int trap_saved_exit_value;
+
+/* The (trapped) signal received while executing in the `wait' builtin */
+int wait_signal_received;
+
 /* A value which can never be the target of a trap handler. */
 #define IMPOSSIBLE_TRAP_HANDLER (SigHandler *)initialize_traps
 
 /* A value which can never be the target of a trap handler. */
 #define IMPOSSIBLE_TRAP_HANDLER (SigHandler *)initialize_traps
 
+#define GETORIGSIG(sig) \
+  do { \
+    original_signals[sig] = (SigHandler *)set_signal_handler (sig, SIG_DFL); \
+    set_signal_handler (sig, original_signals[sig]); \
+    if (original_signals[sig] == SIG_IGN) \
+      sigmodes[sig] |= SIG_HARD_IGNORE; \
+  } while (0)
+
+#define GET_ORIGINAL_SIGNAL(sig) \
+  if (sig && sig < NSIG && original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER) \
+    GETORIGSIG(sig)
+
 void
 initialize_traps ()
 {
   register int i;
 
 void
 initialize_traps ()
 {
   register int i;
 
-  trap_list[EXIT_TRAP] = trap_list[DEBUG_TRAP] = (char *)NULL;
-  sigmodes[EXIT_TRAP] = sigmodes[DEBUG_TRAP] = SIG_INHERITED;
+  initialize_signames();
+
+  trap_list[EXIT_TRAP] = trap_list[DEBUG_TRAP] = trap_list[ERROR_TRAP] = trap_list[RETURN_TRAP] = (char *)NULL;
+  sigmodes[EXIT_TRAP] = sigmodes[DEBUG_TRAP] = sigmodes[ERROR_TRAP] = sigmodes[RETURN_TRAP] = SIG_INHERITED;
   original_signals[EXIT_TRAP] = IMPOSSIBLE_TRAP_HANDLER;
 
   for (i = 1; i < NSIG; i++)
   original_signals[EXIT_TRAP] = IMPOSSIBLE_TRAP_HANDLER;
 
   for (i = 1; i < NSIG; i++)
@@ -103,37 +146,61 @@ initialize_traps ()
 
   /* Show which signals are treated specially by the shell. */
 #if defined (SIGCHLD)
 
   /* Show which signals are treated specially by the shell. */
 #if defined (SIGCHLD)
-  original_signals[SIGCHLD] =
-    (SigHandler *) set_signal_handler (SIGCHLD, SIG_DFL);
-  set_signal_handler (SIGCHLD, original_signals[SIGCHLD]);
+  GETORIGSIG (SIGCHLD);
   sigmodes[SIGCHLD] |= (SIG_SPECIAL | SIG_NO_TRAP);
 #endif /* SIGCHLD */
 
   sigmodes[SIGCHLD] |= (SIG_SPECIAL | SIG_NO_TRAP);
 #endif /* SIGCHLD */
 
-  original_signals[SIGINT] =
-    (SigHandler *) set_signal_handler (SIGINT, SIG_DFL);
-  set_signal_handler (SIGINT, original_signals[SIGINT]);
+  GETORIGSIG (SIGINT);
   sigmodes[SIGINT] |= SIG_SPECIAL;
 
   sigmodes[SIGINT] |= SIG_SPECIAL;
 
-  original_signals[SIGQUIT] =
-    (SigHandler *) set_signal_handler (SIGQUIT, SIG_DFL);
-  set_signal_handler (SIGQUIT, original_signals[SIGQUIT]);
+#if defined (__BEOS__)
+  /* BeOS sets SIGINT to SIG_IGN! */
+  original_signals[SIGINT] = SIG_DFL;
+  sigmodes[SIGINT] &= ~SIG_HARD_IGNORE;
+#endif
+
+  GETORIGSIG (SIGQUIT);
   sigmodes[SIGQUIT] |= SIG_SPECIAL;
 
   if (interactive)
     {
   sigmodes[SIGQUIT] |= SIG_SPECIAL;
 
   if (interactive)
     {
-      original_signals[SIGTERM] =
-       (SigHandler *)set_signal_handler (SIGTERM, SIG_DFL);
-      set_signal_handler (SIGTERM, original_signals[SIGTERM]);
+      GETORIGSIG (SIGTERM);
       sigmodes[SIGTERM] |= SIG_SPECIAL;
     }
 }
 
       sigmodes[SIGTERM] |= SIG_SPECIAL;
     }
 }
 
+#ifdef INCLUDE_UNUSED
+/* Return a printable representation of the trap handler for SIG. */
+static char *
+trap_handler_string (sig)
+     int sig;
+{
+  if (trap_list[sig] == (char *)DEFAULT_SIG)
+    return "DEFAULT_SIG";
+  else if (trap_list[sig] == (char *)IGNORE_SIG)
+    return "IGNORE_SIG";
+  else if (trap_list[sig] == (char *)IMPOSSIBLE_TRAP_HANDLER)
+    return "IMPOSSIBLE_TRAP_HANDLER";
+  else if (trap_list[sig])
+    return trap_list[sig];
+  else
+    return "NULL";
+}
+#endif
+
 /* Return the print name of this signal. */
 char *
 signal_name (sig)
      int sig;
 {
 /* Return the print name of this signal. */
 char *
 signal_name (sig)
      int sig;
 {
-  return ((sig > NSIG || sig < 0) ? "bad signal number" : signal_names[sig]);
+  char *ret;
+
+  /* on cygwin32, signal_names[sig] could be null */
+  ret = (sig >= BASH_NSIG || sig < 0 || signal_names[sig] == NULL)
+       ? _("invalid signal number")
+       : signal_names[sig];
+
+  return ret;
 }
 
 /* Turn a string into a signal number, or a number into
 }
 
 /* Turn a string into a signal number, or a number into
@@ -141,18 +208,47 @@ signal_name (sig)
    then (int)2 is returned.  Return NO_SIG if STRING doesn't
    contain a valid signal descriptor. */
 int
    then (int)2 is returned.  Return NO_SIG if STRING doesn't
    contain a valid signal descriptor. */
 int
-decode_signal (string)
+decode_signal (string, flags)
      char *string;
      char *string;
+     int flags;
 {
 {
-  long sig;
+  intmax_t sig;
+  char *name;
 
   if (legal_number (string, &sig))
 
   if (legal_number (string, &sig))
-    return ((sig >= 0 && sig <= NSIG) ? (int)sig : NO_SIG);
+    return ((sig >= 0 && sig < NSIG) ? (int)sig : NO_SIG);
+
+  /* A leading `SIG' may be omitted. */
+  for (sig = 0; sig < BASH_NSIG; sig++)
+    {
+      name = signal_names[sig];
+      if (name == 0 || name[0] == '\0')
+       continue;
 
 
-  for (sig = 0; sig <= NSIG; sig++)
-    if (strcasecmp (string, signal_names[sig]) == 0 ||
-       strcasecmp (string, &(signal_names[sig])[3]) == 0)
-      return ((int)sig);
+      /* Check name without the SIG prefix first case sensitivly or
+        insensitively depending on whether flags includes DSIG_NOCASE */
+      if (STREQN (name, "SIG", 3))
+       {
+         name += 3;
+
+         if ((flags & DSIG_NOCASE) && strcasecmp (string, name) == 0)
+           return ((int)sig);
+         else if ((flags & DSIG_NOCASE) == 0 && strcmp (string, name) == 0)
+           return ((int)sig);
+         /* If we can't use the `SIG' prefix to match, punt on this
+            name now. */
+         else if ((flags & DSIG_SIGPREFIX) == 0)
+           continue;
+       }
+
+      /* Check name with SIG prefix case sensitively or insensitively
+        depending on whether flags includes DSIG_NOCASE */
+      name = signal_names[sig];
+      if ((flags & DSIG_NOCASE) && strcasecmp (string, name) == 0)
+       return ((int)sig);
+      else if ((flags & DSIG_NOCASE) == 0 && strcmp (string, name) == 0)
+       return ((int)sig);
+    }
 
   return (NO_SIG);
 }
 
   return (NO_SIG);
 }
@@ -164,7 +260,7 @@ void
 run_pending_traps ()
 {
   register int sig;
 run_pending_traps ()
 {
   register int sig;
-  int old_exit_value;
+  int old_exit_value, *token_state;
 
   if (catch_flag == 0)         /* simple optimization */
     return;
 
   if (catch_flag == 0)         /* simple optimization */
     return;
@@ -177,7 +273,7 @@ run_pending_traps ()
   for (sig = 1; sig < NSIG; sig++)
     {
       /* XXX this could be made into a counter by using
   for (sig = 1; sig < NSIG; sig++)
     {
       /* XXX this could be made into a counter by using
-         while (pending_traps[sig]--) instead of the if statement. */
+        while (pending_traps[sig]--) instead of the if statement. */
       if (pending_traps[sig])
        {
 #if defined (HAVE_POSIX_SIGNALS)
       if (pending_traps[sig])
        {
 #if defined (HAVE_POSIX_SIGNALS)
@@ -199,8 +295,38 @@ run_pending_traps ()
              run_interrupt_trap ();
              CLRINTERRUPT;
            }
              run_interrupt_trap ();
              CLRINTERRUPT;
            }
+         else if (trap_list[sig] == (char *)DEFAULT_SIG ||
+                  trap_list[sig] == (char *)IGNORE_SIG ||
+                  trap_list[sig] == (char *)IMPOSSIBLE_TRAP_HANDLER)
+           {
+             /* This is possible due to a race condition.  Say a bash
+                process has SIGTERM trapped.  A subshell is spawned
+                using { list; } & and the parent does something and kills
+                the subshell with SIGTERM.  It's possible for the subshell
+                to set pending_traps[SIGTERM] to 1 before the code in
+                execute_cmd.c eventually calls restore_original_signals
+                to reset the SIGTERM signal handler in the subshell.  The
+                next time run_pending_traps is called, pending_traps[SIGTERM]
+                will be 1, but the trap handler in trap_list[SIGTERM] will
+                be invalid (probably DEFAULT_SIG, but it could be IGNORE_SIG).
+                Unless we catch this, the subshell will dump core when
+                trap_list[SIGTERM] == DEFAULT_SIG, because DEFAULT_SIG is
+                usually 0x0. */
+             internal_warning (_("run_pending_traps: bad value in trap_list[%d]: %p"),
+                               sig, trap_list[sig]);
+             if (trap_list[sig] == (char *)DEFAULT_SIG)
+               {
+                 internal_warning (_("run_pending_traps: signal handler is SIG_DFL, resending %d (%s) to myself"), sig, signal_name (sig));
+                 kill (getpid (), sig);
+               }
+           }
          else
          else
-           parse_and_execute (savestring (trap_list[sig]), "trap", 0);
+           {
+             token_state = save_token_state ();
+             parse_and_execute (savestring (trap_list[sig]), "trap", SEVAL_NONINT|SEVAL_NOHIST);
+             restore_token_state (token_state);
+             free (token_state);
+           }
 
          pending_traps[sig] = 0;
 
 
          pending_traps[sig] = 0;
 
@@ -221,12 +347,15 @@ sighandler
 trap_handler (sig)
      int sig;
 {
 trap_handler (sig)
      int sig;
 {
+  int oerrno;
+
   if ((sig >= NSIG) ||
       (trap_list[sig] == (char *)DEFAULT_SIG) ||
       (trap_list[sig] == (char *)IGNORE_SIG))
   if ((sig >= NSIG) ||
       (trap_list[sig] == (char *)DEFAULT_SIG) ||
       (trap_list[sig] == (char *)IGNORE_SIG))
-    programming_error ("trap_handler: bad signal %d", sig);
+    programming_error (_("trap_handler: bad signal %d"), sig);
   else
     {
   else
     {
+      oerrno = errno;
 #if defined (MUST_REINSTALL_SIGHANDLERS)
       set_signal_handler (sig, trap_handler);
 #endif /* MUST_REINSTALL_SIGHANDLERS */
 #if defined (MUST_REINSTALL_SIGHANDLERS)
       set_signal_handler (sig, trap_handler);
 #endif /* MUST_REINSTALL_SIGHANDLERS */
@@ -234,14 +363,24 @@ trap_handler (sig)
       catch_flag = 1;
       pending_traps[sig]++;
 
       catch_flag = 1;
       pending_traps[sig]++;
 
+      if (interrupt_immediately && this_shell_builtin && (this_shell_builtin == wait_builtin))
+       {
+         wait_signal_received = sig;
+         longjmp (wait_intr_buf, 1);
+       }
+
       if (interrupt_immediately)
        run_pending_traps ();
       if (interrupt_immediately)
        run_pending_traps ();
+
+      errno = oerrno;
     }
 
   SIGRETURN (0);
 }
 
 #if defined (JOB_CONTROL) && defined (SIGCHLD)
     }
 
   SIGRETURN (0);
 }
 
 #if defined (JOB_CONTROL) && defined (SIGCHLD)
+
+#ifdef INCLUDE_UNUSED
 /* Make COMMAND_STRING be executed when SIGCHLD is caught. */
 void
 set_sigchld_trap (command_string)
 /* Make COMMAND_STRING be executed when SIGCHLD is caught. */
 void
 set_sigchld_trap (command_string)
@@ -249,9 +388,10 @@ set_sigchld_trap (command_string)
 {
   set_signal (SIGCHLD, command_string);
 }
 {
   set_signal (SIGCHLD, command_string);
 }
+#endif
 
 
-/* Make COMMAND_STRING be executed when SIGCHLD is caught iff the current
-   SIGCHLD trap handler is DEFAULT_SIG. */
+/* Make COMMAND_STRING be executed when SIGCHLD is caught iff SIGCHLD
+   is not already trapped. */
 void
 maybe_set_sigchld_trap (command_string)
      char *command_string;
 void
 maybe_set_sigchld_trap (command_string)
      char *command_string;
@@ -269,11 +409,27 @@ set_debug_trap (command)
 }
 
 void
 }
 
 void
+set_error_trap (command)
+     char *command;
+{
+  set_signal (ERROR_TRAP, command);
+}
+
+void
+set_return_trap (command)
+     char *command;
+{
+  set_signal (RETURN_TRAP, command);
+}
+
+#ifdef INCLUDE_UNUSED
+void
 set_sigint_trap (command)
      char *command;
 {
   set_signal (SIGINT, command);
 }
 set_sigint_trap (command)
      char *command;
 {
   set_signal (SIGINT, command);
 }
+#endif
 
 /* Reset the SIGINT handler so that subshells that are doing `shellsy'
    things, like waiting for command substitution or executing commands
 
 /* Reset the SIGINT handler so that subshells that are doing `shellsy'
    things, like waiting for command substitution or executing commands
@@ -295,7 +451,21 @@ set_sigint_handler ()
   else if (interactive)        /* XXX - was interactive_shell */
     return (set_signal_handler (SIGINT, sigint_sighandler));
   else
   else if (interactive)        /* XXX - was interactive_shell */
     return (set_signal_handler (SIGINT, sigint_sighandler));
   else
-    return (set_signal_handler (SIGINT, termination_unwind_protect));
+    return (set_signal_handler (SIGINT, termsig_sighandler));
+}
+
+/* Return the correct handler for signal SIG according to the values in
+   sigmodes[SIG]. */
+SigHandler *
+trap_to_sighandler (sig)
+     int sig;
+{
+  if (sigmodes[sig] & (SIG_IGNORED|SIG_HARD_IGNORE))
+    return (SIG_IGN);
+  else if (sigmodes[sig] & SIG_TRAPPED)
+    return (trap_handler);
+  else
+    return (SIG_DFL);
 }
 
 /* Set SIG to call STRING as a command. */
 }
 
 /* Set SIG to call STRING as a command. */
@@ -304,9 +474,11 @@ set_signal (sig, string)
      int sig;
      char *string;
 {
      int sig;
      char *string;
 {
-  if (sig == DEBUG_TRAP || sig == EXIT_TRAP)
+  if (SPECIAL_TRAP (sig))
     {
       change_signal (sig, savestring (string));
     {
       change_signal (sig, savestring (string));
+      if (sig == EXIT_TRAP && interactive == 0)
+       initialize_terminating_signals ();
       return;
     }
 
       return;
     }
 
@@ -321,17 +493,9 @@ set_signal (sig, string)
     {
       /* If we aren't sure of the original value, check it. */
       if (original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER)
     {
       /* If we aren't sure of the original value, check it. */
       if (original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER)
-       {
-         original_signals[sig] = (SigHandler *)set_signal_handler (sig, SIG_DFL);
-         set_signal_handler (sig, original_signals[sig]);
-       }
-
-      /* Signals ignored on entry to the shell cannot be trapped or reset. */
+        GETORIGSIG (sig);
       if (original_signals[sig] == SIG_IGN)
       if (original_signals[sig] == SIG_IGN)
-       {
-         sigmodes[sig] |= SIG_HARD_IGNORE;
-         return;
-       }
+       return;
     }
 
   /* Only change the system signal handler if SIG_NO_TRAP is not set.
     }
 
   /* Only change the system signal handler if SIG_NO_TRAP is not set.
@@ -366,7 +530,8 @@ change_signal (sig, value)
      int sig;
      char *value;
 {
      int sig;
      char *value;
 {
-  free_trap_command (sig);
+  if ((sigmodes[sig] & SIG_INPROGRESS) == 0)
+    free_trap_command (sig);
   trap_list[sig] = value;
 
   sigmodes[sig] |= SIG_TRAPPED;
   trap_list[sig] = value;
 
   sigmodes[sig] |= SIG_TRAPPED;
@@ -378,25 +543,13 @@ change_signal (sig, value)
     sigmodes[sig] |= SIG_CHANGED;
 }
 
     sigmodes[sig] |= SIG_CHANGED;
 }
 
-#define GET_ORIGINAL_SIGNAL(sig) \
-  if (sig && sig < NSIG && original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER) \
-    get_original_signal (sig)
-
 static void
 get_original_signal (sig)
      int sig;
 {
   /* If we aren't sure the of the original value, then get it. */
   if (original_signals[sig] == (SigHandler *)IMPOSSIBLE_TRAP_HANDLER)
 static void
 get_original_signal (sig)
      int sig;
 {
   /* If we aren't sure the of the original value, then get it. */
   if (original_signals[sig] == (SigHandler *)IMPOSSIBLE_TRAP_HANDLER)
-    {
-      original_signals[sig] =
-       (SigHandler *) set_signal_handler (sig, SIG_DFL);
-      set_signal_handler (sig, original_signals[sig]);
-
-      /* Signals ignored on entry to the shell cannot be trapped. */
-      if (original_signals[sig] == SIG_IGN)
-       sigmodes[sig] |= SIG_HARD_IGNORE;
-    }
+    GETORIGSIG (sig);
 }
 
 /* Restore the default action for SIG; i.e., the action the shell
 }
 
 /* Restore the default action for SIG; i.e., the action the shell
@@ -407,11 +560,15 @@ void
 restore_default_signal (sig)
      int sig;
 {
 restore_default_signal (sig)
      int sig;
 {
-  if (sig == DEBUG_TRAP || sig == EXIT_TRAP)
+  if (SPECIAL_TRAP (sig))
     {
     {
-      free_trap_command (sig);
+      if ((sig != DEBUG_TRAP && sig != ERROR_TRAP && sig != RETURN_TRAP) ||
+         (sigmodes[sig] & SIG_INPROGRESS) == 0)
+       free_trap_command (sig);
       trap_list[sig] = (char *)NULL;
       sigmodes[sig] &= ~SIG_TRAPPED;
       trap_list[sig] = (char *)NULL;
       sigmodes[sig] &= ~SIG_TRAPPED;
+      if (sigmodes[sig] & SIG_INPROGRESS)
+       sigmodes[sig] |= SIG_CHANGED;
       return;
     }
 
       return;
     }
 
@@ -442,7 +599,7 @@ void
 ignore_signal (sig)
      int sig;
 {
 ignore_signal (sig)
      int sig;
 {
-  if ((sig == EXIT_TRAP || sig == DEBUG_TRAP) && ((sigmodes[sig] & SIG_IGNORED) == 0))
+  if (SPECIAL_TRAP (sig) && ((sigmodes[sig] & SIG_IGNORED) == 0))
     {
       change_signal (sig, (char *)IGNORE_SIG);
       return;
     {
       change_signal (sig, (char *)IGNORE_SIG);
       return;
@@ -474,9 +631,10 @@ int
 run_exit_trap ()
 {
   char *trap_command;
 run_exit_trap ()
 {
   char *trap_command;
-  int code, old_exit_value;
+  int code, function_code, retval;
 
 
-  old_exit_value = last_command_exit_value;
+  trap_saved_exit_value = last_command_exit_value;
+  function_code = 0;
 
   /* Run the trap only if signal 0 is trapped and not ignored, and we are not
      currently running in the trap handler (call to exit in the list of
 
   /* Run the trap only if signal 0 is trapped and not ignored, and we are not
      currently running in the trap handler (call to exit in the list of
@@ -488,17 +646,34 @@ run_exit_trap ()
       sigmodes[EXIT_TRAP] &= ~SIG_TRAPPED;
       sigmodes[EXIT_TRAP] |= SIG_INPROGRESS;
 
       sigmodes[EXIT_TRAP] &= ~SIG_TRAPPED;
       sigmodes[EXIT_TRAP] |= SIG_INPROGRESS;
 
+      retval = trap_saved_exit_value;
+      running_trap = 1;
+
       code = setjmp (top_level);
 
       code = setjmp (top_level);
 
-      if (code == 0)
-       parse_and_execute (trap_command, "exit trap", 0);
+      /* If we're in a function, make sure return longjmps come here, too. */
+      if (return_catch_flag)
+       function_code = setjmp (return_catch);
+
+      if (code == 0 && function_code == 0)
+       {
+         reset_parser ();
+         parse_and_execute (trap_command, "exit trap", SEVAL_NONINT|SEVAL_NOHIST);
+       }
+      else if (code == ERREXIT)
+       retval = last_command_exit_value;
       else if (code == EXITPROG)
       else if (code == EXITPROG)
-        return (last_command_exit_value);
+       retval = last_command_exit_value;
+      else if (function_code != 0)
+        retval = return_catch_value;
       else
       else
-       return (old_exit_value);
+       retval = trap_saved_exit_value;
+
+      running_trap = 0;
+      return retval;
     }
 
     }
 
-  return (old_exit_value);
+  return (trap_saved_exit_value);
 }
 
 void
 }
 
 void
@@ -509,15 +684,18 @@ run_trap_cleanup (sig)
 }
 
 /* Run a trap command for SIG.  SIG is one of the signals the shell treats
 }
 
 /* Run a trap command for SIG.  SIG is one of the signals the shell treats
-   specially. */
-static void
+   specially.  Returns the exit status of the executed trap command list. */
+static int
 _run_trap_internal (sig, tag)
      int sig;
      char *tag;
 {
   char *trap_command, *old_trap;
 _run_trap_internal (sig, tag)
      int sig;
      char *tag;
 {
   char *trap_command, *old_trap;
-  int old_exit_value;
+  int trap_exit_value, *token_state;
+  int save_return_catch_flag, function_code;
+  procenv_t save_return_catch;
 
 
+  trap_exit_value = function_code = 0;
   /* Run the trap only if SIG is trapped and not ignored, and we are not
      currently executing in the trap handler. */
   if ((sigmodes[sig] & SIG_TRAPPED) && ((sigmodes[sig] & SIG_IGNORED) == 0) &&
   /* Run the trap only if SIG is trapped and not ignored, and we are not
      currently executing in the trap handler. */
   if ((sigmodes[sig] & SIG_TRAPPED) && ((sigmodes[sig] & SIG_IGNORED) == 0) &&
@@ -530,26 +708,102 @@ _run_trap_internal (sig, tag)
       trap_command =  savestring (old_trap);
 
       running_trap = sig + 1;
       trap_command =  savestring (old_trap);
 
       running_trap = sig + 1;
-      old_exit_value = last_command_exit_value;
-      parse_and_execute (trap_command, tag, 0);
-      last_command_exit_value = old_exit_value;
+      trap_saved_exit_value = last_command_exit_value;
+
+      token_state = save_token_state ();
+
+      /* If we're in a function, make sure return longjmps come here, too. */
+      save_return_catch_flag = return_catch_flag;
+      if (return_catch_flag)
+       {
+         COPY_PROCENV (return_catch, save_return_catch);
+         function_code = setjmp (return_catch);
+       }
+
+      if (function_code == 0)
+       parse_and_execute (trap_command, tag, SEVAL_NONINT|SEVAL_NOHIST);
+
+      restore_token_state (token_state);
+      free (token_state);
+
+      trap_exit_value = last_command_exit_value;
+      last_command_exit_value = trap_saved_exit_value;
       running_trap = 0;
 
       sigmodes[sig] &= ~SIG_INPROGRESS;
 
       if (sigmodes[sig] & SIG_CHANGED)
        {
       running_trap = 0;
 
       sigmodes[sig] &= ~SIG_INPROGRESS;
 
       if (sigmodes[sig] & SIG_CHANGED)
        {
-         free (old_trap);
+#if 0
+         /* Special traps like EXIT, DEBUG, RETURN are handled explicitly in
+            the places where they can be changed using unwind-protects.  For
+            example, look at execute_cmd.c:execute_function(). */
+         if (SPECIAL_TRAP (sig) == 0)
+#endif
+           free (old_trap);
          sigmodes[sig] &= ~SIG_CHANGED;
        }
          sigmodes[sig] &= ~SIG_CHANGED;
        }
+
+      if (save_return_catch_flag)
+       {
+         return_catch_flag = save_return_catch_flag;
+         return_catch_value = trap_exit_value;
+         COPY_PROCENV (save_return_catch, return_catch);
+         if (function_code)
+           longjmp (return_catch, 1);
+       }
     }
     }
+
+  return trap_exit_value;
 }
 
 }
 
-void
+int
 run_debug_trap ()
 {
 run_debug_trap ()
 {
-  if ((sigmodes[DEBUG_TRAP] & SIG_TRAPPED) && (sigmodes[DEBUG_TRAP] & SIG_INPROGRESS) == 0)
-    _run_trap_internal (DEBUG_TRAP, "debug trap");
+  int trap_exit_value;
+
+  /* XXX - question:  should the DEBUG trap inherit the RETURN trap? */
+  trap_exit_value = 0;
+  if ((sigmodes[DEBUG_TRAP] & SIG_TRAPPED) && ((sigmodes[DEBUG_TRAP] & SIG_IGNORED) == 0) && ((sigmodes[DEBUG_TRAP] & SIG_INPROGRESS) == 0))
+    {
+      trap_exit_value = _run_trap_internal (DEBUG_TRAP, "debug trap");
+      
+#if defined (DEBUGGER)
+      /* If we're in the debugger and the DEBUG trap returns 2 while we're in
+        a function or sourced script, we force a `return'. */
+      if (debugging_mode && trap_exit_value == 2 && return_catch_flag)
+       {
+         return_catch_value = trap_exit_value;
+         longjmp (return_catch, 1);
+       }
+#endif
+    }
+  return trap_exit_value;
+}
+
+void
+run_error_trap ()
+{
+  if ((sigmodes[ERROR_TRAP] & SIG_TRAPPED) && ((sigmodes[ERROR_TRAP] & SIG_IGNORED) == 0) && (sigmodes[ERROR_TRAP] & SIG_INPROGRESS) == 0)
+    _run_trap_internal (ERROR_TRAP, "error trap");
+}
+
+void
+run_return_trap ()
+{
+  int old_exit_value;
+
+#if 0
+  if ((sigmodes[DEBUG_TRAP] & SIG_TRAPPED) && (sigmodes[DEBUG_TRAP] & SIG_INPROGRESS))
+    return;
+#endif
+
+  if ((sigmodes[RETURN_TRAP] & SIG_TRAPPED) && ((sigmodes[RETURN_TRAP] & SIG_IGNORED) == 0) && (sigmodes[RETURN_TRAP] & SIG_INPROGRESS) == 0)
+    {
+      old_exit_value = last_command_exit_value;
+      _run_trap_internal (RETURN_TRAP, "return trap");
+      last_command_exit_value = old_exit_value;
+    }
 }
 
 /* Run a trap set on SIGINT.  This is called from throw_to_top_level (), and
 }
 
 /* Run a trap set on SIGINT.  This is called from throw_to_top_level (), and
@@ -560,6 +814,7 @@ run_interrupt_trap ()
   _run_trap_internal (SIGINT, "interrupt trap");
 }
 
   _run_trap_internal (SIGINT, "interrupt trap");
 }
 
+#ifdef INCLUDE_UNUSED
 /* Free all the allocated strings in the list of traps and reset the trap
    values to the default. */
 void
 /* Free all the allocated strings in the list of traps and reset the trap
    values to the default. */
 void
@@ -567,14 +822,15 @@ free_trap_strings ()
 {
   register int i;
 
 {
   register int i;
 
-  for (i = 0; i < NSIG+1; i++)
+  for (i = 0; i < BASH_NSIG; i++)
     {
       free_trap_command (i);
       trap_list[i] = (char *)DEFAULT_SIG;
       sigmodes[i] &= ~SIG_TRAPPED;
     }
     {
       free_trap_command (i);
       trap_list[i] = (char *)DEFAULT_SIG;
       sigmodes[i] &= ~SIG_TRAPPED;
     }
-  trap_list[DEBUG_TRAP] = trap_list[EXIT_TRAP] = (char *)NULL;
+  trap_list[DEBUG_TRAP] = trap_list[EXIT_TRAP] = trap_list[ERROR_TRAP] = trap_list[RETURN_TRAP] = (char *)NULL;
 }
 }
+#endif
 
 /* Reset the handler for SIG to the original value. */
 static void
 
 /* Reset the handler for SIG to the original value. */
 static void
@@ -582,6 +838,7 @@ reset_signal (sig)
      int sig;
 {
   set_signal_handler (sig, original_signals[sig]);
      int sig;
 {
   set_signal_handler (sig, original_signals[sig]);
+  sigmodes[sig] &= ~SIG_TRAPPED;
 }
 
 /* Set the handler signal SIG to the original and free any trap
 }
 
 /* Set the handler signal SIG to the original and free any trap
@@ -597,17 +854,21 @@ restore_signal (sig)
 
 static void
 reset_or_restore_signal_handlers (reset)
 
 static void
 reset_or_restore_signal_handlers (reset)
-     VFunction *reset;
+     sh_resetsig_func_t *reset;
 {
   register int i;
 
   /* Take care of the exit trap first */
   if (sigmodes[EXIT_TRAP] & SIG_TRAPPED)
     {
 {
   register int i;
 
   /* Take care of the exit trap first */
   if (sigmodes[EXIT_TRAP] & SIG_TRAPPED)
     {
-      free_trap_command (EXIT_TRAP);
-      trap_list[EXIT_TRAP] = (char *)NULL;
       sigmodes[EXIT_TRAP] &= ~SIG_TRAPPED;
       sigmodes[EXIT_TRAP] &= ~SIG_TRAPPED;
+      if (reset != reset_signal)
+       {
+         free_trap_command (EXIT_TRAP);
+         trap_list[EXIT_TRAP] = (char *)NULL;
+       }
     }
     }
+
   for (i = 1; i < NSIG; i++)
     {
       if (sigmodes[i] & SIG_TRAPPED)
   for (i = 1; i < NSIG; i++)
     {
       if (sigmodes[i] & SIG_TRAPPED)
@@ -620,8 +881,23 @@ reset_or_restore_signal_handlers (reset)
       else if (sigmodes[i] & SIG_SPECIAL)
        (*reset) (i);
     }
       else if (sigmodes[i] & SIG_SPECIAL)
        (*reset) (i);
     }
+
+  /* Command substitution and other child processes don't inherit the
+     debug, error, or return traps.  If we're in the debugger, and the
+     `functrace' or `errtrace' options have been set, then let command
+     substitutions inherit them.  Let command substitution inherit the
+     RETURN trap if we're in the debugger and tracing functions. */
+  if (function_trace_mode == 0)
+    {
+      sigmodes[DEBUG_TRAP] &= ~SIG_TRAPPED;
+      sigmodes[RETURN_TRAP] &= ~SIG_TRAPPED;
+    }
+  if (error_trace_mode == 0)
+    sigmodes[ERROR_TRAP] &= ~SIG_TRAPPED;
 }
 
 }
 
+/* Reset trapped signals to their original values, but don't free the
+   trap strings.  Called by the command substitution code. */
 void
 reset_signal_handlers ()
 {
 void
 reset_signal_handlers ()
 {
@@ -657,6 +933,9 @@ maybe_call_trap_handler (sig)
        case DEBUG_TRAP:
          run_debug_trap ();
          break;
        case DEBUG_TRAP:
          run_debug_trap ();
          break;
+       case ERROR_TRAP:
+         run_error_trap ();
+         break;
        default:
          trap_handler (sig);
          break;
        default:
          trap_handler (sig);
          break;
@@ -695,3 +974,10 @@ set_signal_ignored (sig)
   sigmodes[sig] |= SIG_HARD_IGNORE;
   original_signals[sig] = SIG_IGN;
 }
   sigmodes[sig] |= SIG_HARD_IGNORE;
   original_signals[sig] = SIG_IGN;
 }
+
+int
+signal_in_progress (sig)
+     int sig;
+{
+  return (sigmodes[sig] & SIG_INPROGRESS);
+}