Imported from ../bash-2.05.tar.gz.
[platform/upstream/bash.git] / trap.c
diff --git a/trap.c b/trap.c
index ac4d6eb..a24130f 100644 (file)
--- a/trap.c
+++ b/trap.c
@@ -7,7 +7,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) 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
 
    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 <stdio.h>
+#if defined (HAVE_UNISTD_H)
+#  include <unistd.h>
+#endif
 
 #include "bashtypes.h"
-#include "trap.h"
-
 #include "bashansi.h"
 
-#if defined (HAVE_UNISTD_H)
-#  include <unistd.h>
-#endif
+#include <stdio.h>
+#include <errno.h>
+
+#include "trap.h"
 
 #include "shell.h"
+#include "input.h"     /* for save_token_state, restore_token_state */
 #include "signames.h"
 #include "builtins/common.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. */
 #define SIG_TRAPPED     0x1    /* Currently trapped. */
@@ -116,6 +122,11 @@ initialize_traps ()
   set_signal_handler (SIGINT, original_signals[SIGINT]);
   sigmodes[SIGINT] |= SIG_SPECIAL;
 
+#if defined (__BEOS__)
+  /* BeOS sets SIGINT to SIG_IGN! */
+  original_signals[SIGINT] = SIG_DFL;
+#endif
+
   original_signals[SIGQUIT] =
     (SigHandler *) set_signal_handler (SIGQUIT, SIG_DFL);
   set_signal_handler (SIGQUIT, original_signals[SIGQUIT]);
@@ -130,12 +141,37 @@ initialize_traps ()
     }
 }
 
+#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 ((sig > NSIG || sig < 0) ? "bad signal number" : signal_names[sig]);
+  char *ret;
+
+  /* on cygwin32, signal_names[sig] could be null */
+  ret = (sig > NSIG || sig < 0) ? "bad signal number" : signal_names[sig];
+  if (ret == NULL)
+    ret = "unrecognized signal number";
+  return ret;
 }
 
 /* Turn a string into a signal number, or a number into
@@ -151,10 +187,16 @@ decode_signal (string)
   if (legal_number (string, &sig))
     return ((sig >= 0 && sig <= NSIG) ? (int)sig : NO_SIG);
 
+  /* A leading `SIG' may be omitted. */
   for (sig = 0; sig <= NSIG; sig++)
-    if (strcasecmp (string, signal_names[sig]) == 0 ||
-       strcasecmp (string, &(signal_names[sig])[3]) == 0)
-      return ((int)sig);
+    {
+      if (signal_names[sig] == 0 || signal_names[sig][0] == '\0')
+       continue;
+      if (strcasecmp (string, signal_names[sig]) == 0 ||
+         (STREQN (signal_names[sig], "SIG", 3) &&
+           strcasecmp (string, &(signal_names[sig])[3]) == 0))
+       return ((int)sig);
+    }
 
   return (NO_SIG);
 }
@@ -166,7 +208,7 @@ void
 run_pending_traps ()
 {
   register int sig;
-  int old_exit_value;
+  int old_exit_value, *token_state;
 
   if (catch_flag == 0)         /* simple optimization */
     return;
@@ -179,7 +221,7 @@ run_pending_traps ()
   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)
@@ -201,8 +243,38 @@ run_pending_traps ()
              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]: 0x%x",
+                               sig, (int)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
-           parse_and_execute (savestring (trap_list[sig]), "trap", SEVAL_NONINT|SEVAL_NOHIST);
+           {
+             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;
 
@@ -223,12 +295,15 @@ sighandler
 trap_handler (sig)
      int sig;
 {
+  int oerrno;
+
   if ((sig >= NSIG) ||
       (trap_list[sig] == (char *)DEFAULT_SIG) ||
       (trap_list[sig] == (char *)IGNORE_SIG))
     programming_error ("trap_handler: bad signal %d", sig);
   else
     {
+      oerrno = errno;
 #if defined (MUST_REINSTALL_SIGHANDLERS)
       set_signal_handler (sig, trap_handler);
 #endif /* MUST_REINSTALL_SIGHANDLERS */
@@ -238,12 +313,16 @@ trap_handler (sig)
 
       if (interrupt_immediately)
        run_pending_traps ();
+
+      errno = oerrno;
     }
 
   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)
@@ -251,6 +330,7 @@ set_sigchld_trap (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. */
@@ -270,12 +350,14 @@ set_debug_trap (command)
   set_signal (DEBUG_TRAP, command);
 }
 
+#ifdef INCLUDE_UNUSED
 void
 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
@@ -300,6 +382,20 @@ set_sigint_handler ()
     return (set_signal_handler (SIGINT, termination_unwind_protect));
 }
 
+/* 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. */
 void
 set_signal (sig, string)
@@ -499,9 +595,12 @@ run_exit_trap ()
       code = setjmp (top_level);
 
       if (code == 0)
-       parse_and_execute (trap_command, "exit trap", SEVAL_NONINT|SEVAL_NOHIST);
+       {
+         reset_parser ();
+         parse_and_execute (trap_command, "exit trap", SEVAL_NONINT|SEVAL_NOHIST);
+       }
       else if (code == EXITPROG)
-        return (last_command_exit_value);
+       return (last_command_exit_value);
       else
        return (old_exit_value);
     }
@@ -524,7 +623,7 @@ _run_trap_internal (sig, tag)
      char *tag;
 {
   char *trap_command, *old_trap;
-  int old_exit_value, old_line_number;
+  int old_exit_value, old_line_number, *token_state;
 
   /* Run the trap only if SIG is trapped and not ignored, and we are not
      currently executing in the trap handler. */
@@ -542,7 +641,12 @@ _run_trap_internal (sig, tag)
       /* Need to copy the value of line_number because parse_and_execute
         resets it to 1, and the trap command might want it. */
       trap_line_number = line_number;
+
+      token_state = save_token_state ();
       parse_and_execute (trap_command, tag, SEVAL_NONINT|SEVAL_NOHIST);
+      restore_token_state (token_state);
+      free (token_state);
+
       last_command_exit_value = old_exit_value;
       running_trap = 0;
 
@@ -571,6 +675,7 @@ run_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
@@ -586,6 +691,7 @@ free_trap_strings ()
     }
   trap_list[DEBUG_TRAP] = trap_list[EXIT_TRAP] = (char *)NULL;
 }
+#endif
 
 /* Reset the handler for SIG to the original value. */
 static void