diff --git a/builtins/read.def b/builtins/read.def
index 37e3619..93134fc 100644
--- a/builtins/read.def
+++ b/builtins/read.def
@@ -87,6 +87,8 @@ $END
 extern int errno;
 #endif
 
+extern int trapped_signal_received;
+
 #if defined (READLINE)
 static void reset_attempted_completion_function __P((char *));
 static char *edit_line __P((char *));
@@ -129,7 +131,7 @@ read_builtin (list)
   register char *varname;
   int size, i, nr, pass_next, saw_escape, eof, opt, retval, code, print_ps2;
   int input_is_tty, input_is_pipe, unbuffered_read, skip_ctlesc, skip_ctlnul;
-  int raw, edit, nchars, silent, have_timeout, fd;
+  int raw, edit, nchars, silent, have_timeout, fd, lastsig, t_errno;
   unsigned int tmout;
   intmax_t intval;
   char c;
@@ -165,6 +167,7 @@ read_builtin (list)
 #endif
   USE_VAR(list);
   USE_VAR(ps2);
+  USE_VAR(lastsig);
 
   i = 0;		/* Index into the string that we are reading. */
   raw = edit = 0;	/* Not reading raw input by default. */
@@ -433,6 +436,15 @@ read_builtin (list)
 
       if (retval <= 0)
 	{
+	  if (retval < 0 && errno == EINTR)
+	    {
+	      lastsig = LASTSIG();
+	      if (lastsig == 0)
+		lastsig = trapped_signal_received;
+	      run_pending_traps ();	/* because interrupt_immediately is not set */
+	    }
+	  else
+	    lastsig = 0;
 	  eof = 1;
 	  break;
 	}
@@ -493,9 +505,11 @@ add_char:
 #if 1
   if (retval < 0)
     {
-      builtin_error (_("read error: %d: %s"), fd, strerror (errno));
+      t_errno = errno;
+      if (errno != EINTR)
+	builtin_error (_("read error: %d: %s"), fd, strerror (errno));
       run_unwind_frame ("read_builtin");
-      return (EXECUTION_FAILURE);
+      return ((t_errno != EINTR) ? EXECUTION_FAILURE : 128+lastsig);
     }
 #endif
 
diff --git a/input.c b/input.c
index 7933da2..bd36c29 100644
--- a/input.c
+++ b/input.c
@@ -71,7 +71,9 @@ getc_with_restart (stream)
     {
       while (1)
 	{
-	  CHECK_TERMSIG;
+	  QUIT;
+	  run_pending_traps ();
+
 	  local_bufused = read (fileno (stream), localbuf, sizeof(localbuf));
 	  if (local_bufused > 0)
 	    break;
diff --git a/jobs.c b/jobs.c
index e1968ae..c7ab63b 100644
--- a/jobs.c
+++ b/jobs.c
@@ -213,7 +213,7 @@ int already_making_children = 0;
 
 /* Functions local to this file. */
 
-static void run_sigchld_trap __P((int));
+void run_sigchld_trap __P((int));
 
 static sighandler wait_sigint_handler __P((int));
 static sighandler sigchld_handler __P((int));
@@ -2193,10 +2193,16 @@ wait_sigint_handler (sig)
 	  signal_is_trapped (SIGINT) &&
 	  ((sigint_handler = trap_to_sighandler (SIGINT)) == trap_handler))
 	{
-	  interrupt_immediately = 0;
 	  trap_handler (SIGINT);	/* set pending_traps[SIGINT] */
 	  wait_signal_received = SIGINT;
-	  longjmp (wait_intr_buf, 1);
+	  if (interrupt_immediately)
+	    {
+	      interrupt_immediately = 0;
+	      longjmp (wait_intr_buf, 1);
+	    }
+	  else
+	    /* Let CHECK_WAIT_INTR handle it in wait_for/waitchld */
+	    SIGRETURN (0);
 	}
       
       ADDINTERRUPT;
@@ -2352,6 +2358,9 @@ wait_for (pid)
   if (interactive && job_control == 0)
     QUIT;
 
+  /* Check for a trapped signal interrupting the wait builtin and jump out */
+  CHECK_WAIT_INTR;
+
   /* If we say wait_for (), then we have a record of this child somewhere.
      If it and none of its peers are running, don't call waitchld(). */
 
@@ -2433,6 +2442,9 @@ wait_for (pid)
 	 old SIGINT signal handler. */
       if (interactive && job_control == 0)
 	QUIT;
+
+      /* Check for a trapped signal interrupting the wait builtin and jump out */
+      CHECK_WAIT_INTR;
     }
   while (PRUNNING (child) || (job != NO_JOB && RUNNING (job)));
 
@@ -3027,6 +3039,9 @@ waitchld (wpid, block)
       if (sigchld || block == 0)
 	waitpid_flags |= WNOHANG;
       CHECK_TERMSIG;
+      /* Check for a trapped signal interrupting the wait builtin and jump out */
+      CHECK_WAIT_INTR;
+
       pid = WAITPID (-1, &status, waitpid_flags);
 
       /* WCONTINUED may be rejected by waitpid as invalid even when defined */
@@ -3054,6 +3069,8 @@ waitchld (wpid, block)
       /* If waitpid returns 0, there are running children.  If it returns -1,
 	 the only other error POSIX says it can return is EINTR. */
       CHECK_TERMSIG;
+      CHECK_WAIT_INTR;
+
       if (pid <= 0)
 	continue;	/* jumps right to the test */
 
@@ -3111,7 +3128,31 @@ waitchld (wpid, block)
   /* Call a SIGCHLD trap handler for each child that exits, if one is set. */
   if (job_control && signal_is_trapped (SIGCHLD) && children_exited &&
       trap_list[SIGCHLD] != (char *)IGNORE_SIG)
-    run_sigchld_trap (children_exited);
+    {
+      if (posixly_correct && this_shell_builtin && this_shell_builtin == wait_builtin)
+	{
+	  interrupt_immediately = 0;
+	  /* This was trap_handler (SIGCHLD) but that can lose traps if
+	     children_exited > 1 */
+	  queue_sigchld_trap (children_exited);
+	  wait_signal_received = SIGCHLD;
+	  /* If we're in a signal handler, let CHECK_WAIT_INTR pick it up;
+	     run_pending_traps will call run_sigchld_trap later  */
+	  if (sigchld == 0)
+	    longjmp (wait_intr_buf, 1);
+	}
+      /* If not in posix mode and not executing the wait builtin, queue the
+	 signal for later handling.  Run the trap immediately if we are
+	 executing the wait builtin, but don't break out of `wait'. */
+      else if (sigchld)	/* called from signal handler */
+	queue_sigchld_trap (children_exited);
+      else if (running_trap)
+	queue_sigchld_trap (children_exited);
+      else if (this_shell_builtin == wait_builtin)
+	run_sigchld_trap (children_exited);	/* XXX */
+      else
+	queue_sigchld_trap (children_exited);
+    }
 
   /* We have successfully recorded the useful information about this process
      that has just changed state.  If we notify asynchronously, and the job
@@ -3330,7 +3371,7 @@ setjstatus (j)
 #endif
 }
 
-static void
+void
 run_sigchld_trap (nchild)
      int nchild;
 {
@@ -3362,15 +3403,17 @@ run_sigchld_trap (nchild)
   subst_assign_varlist = (WORD_LIST *)NULL;
   the_pipeline = (PROCESS *)NULL;
 
-  restore_default_signal (SIGCHLD);
+  running_trap = SIGCHLD + 1;
+
+  set_impossible_sigchld_trap ();
   jobs_list_frozen = 1;
   for (i = 0; i < nchild; i++)
     {
-      interrupt_immediately = 1;
       parse_and_execute (savestring (trap_command), "trap", SEVAL_NOHIST|SEVAL_RESETLINE);
     }
 
   run_unwind_frame ("SIGCHLD trap");
+  running_trap = 0;
 }
 
 /* Function to call when you want to notify people of changes
diff --git a/jobs.h b/jobs.h
index 55b8b19..7f8ce4d 100644
--- a/jobs.h
+++ b/jobs.h
@@ -218,6 +218,8 @@ extern int initialize_job_control __P((int));
 extern void initialize_job_signals __P((void));
 extern int give_terminal_to __P((pid_t, int));
 
+extern void run_sigchld_trap __P((int));
+
 extern void unfreeze_jobs_list __P((void));
 extern int set_job_control __P((int));
 extern void without_job_control __P((void));
diff --git a/lib/glob/glob.c b/lib/glob/glob.c
index 08a7da8..173341f 100644
--- a/lib/glob/glob.c
+++ b/lib/glob/glob.c
@@ -74,6 +74,7 @@
 
 extern void throw_to_top_level __P((void));
 extern int sh_eaccess __P((char *, int));
+extern void run_pending_traps __P((void));
 
 extern int extended_glob;
 
@@ -838,6 +839,8 @@ glob_filename (pathname, flags)
 	{
 	  if (free_dirname)
 	    free (directory_name);
+	  QUIT;			/* XXX - shell */
+	  run_pending_traps ();
 	  return (temp_results);
 	}
 
@@ -862,6 +865,7 @@ glob_filename (pathname, flags)
     free (directory_name);
 
   QUIT;
+  run_pending_traps ();
 
   return (NULL);
 }
diff --git a/lib/sh/zread.c b/lib/sh/zread.c
index 460234b..ccf6510 100644
--- a/lib/sh/zread.c
+++ b/lib/sh/zread.c
@@ -34,6 +34,8 @@ extern int errno;
 #  define SEEK_CUR 1
 #endif
 
+extern void check_signals_and_traps (void);
+
 /* Read LEN bytes from FD into BUF.  Retry the read on EINTR.  Any other
    error causes the loop to break. */
 ssize_t
@@ -45,7 +47,8 @@ zread (fd, buf, len)
   ssize_t r;
 
   while ((r = read (fd, buf, len)) < 0 && errno == EINTR)
-    ;
+    check_signals_and_traps ();	/* XXX - should it be check_signals()? */
+
   return r;
 }
 
diff --git a/quit.h b/quit.h
index 65c182c..4f402c7 100644
--- a/quit.h
+++ b/quit.h
@@ -54,4 +54,13 @@ extern volatile sig_atomic_t terminating_signal;
     if (terminating_signal) termsig_handler (terminating_signal); \
   } while (0)
 
+#define LASTSIG() \
+  (terminating_signal ? terminating_signal : (interrupt_state ? SIGINT : 0))
+
+#define CHECK_WAIT_INTR \
+  do { \
+    if (wait_signal_received && this_shell_builtin && (this_shell_builtin == wait_builtin)) \
+      longjmp (wait_intr_buf, 1); \
+  } while (0)
+
 #endif /* _QUIT_H_ */
diff --git a/sig.c b/sig.c
index 650d087..88bfc5d 100644
--- a/sig.c
+++ b/sig.c
@@ -43,6 +43,7 @@
 #include "trap.h"
 
 #include "builtins/common.h"
+#include "builtins/builtext.h"
 
 #if defined (READLINE)
 #  include "bashline.h"
@@ -58,6 +59,8 @@ extern int last_command_exit_signal;
 extern int return_catch_flag;
 extern int loop_level, continuing, breaking;
 extern int parse_and_execute_level, shell_initialized;
+extern int wait_signal_received;
+extern sh_builtin_func_t *this_shell_builtin;
 
 /* Non-zero after SIGINT. */
 volatile sig_atomic_t interrupt_state = 0;
@@ -557,6 +560,15 @@ sigint_sighandler (sig)
   if (interrupt_state == 0)
     ADDINTERRUPT;
 
+  /* We will get here in interactive shells with job control active; allow
+     an interactive wait to be interrupted. */
+  if (this_shell_builtin && this_shell_builtin == wait_builtin)
+    {
+      last_command_exit_value = 128 + sig;
+      wait_signal_received = sig;
+      SIGRETURN (0);
+    }
+      
   if (interrupt_immediately)
     {
       interrupt_immediately = 0;
diff --git a/trap.c b/trap.c
index e75fd59..58cd23d 100644
--- a/trap.c
+++ b/trap.c
@@ -113,6 +113,8 @@ int trap_saved_exit_value;
 /* The (trapped) signal received while executing in the `wait' builtin */
 int wait_signal_received;
 
+int trapped_signal_received;
+
 /* A value which can never be the target of a trap handler. */
 #define IMPOSSIBLE_TRAP_HANDLER (SigHandler *)initialize_traps
 
@@ -268,7 +270,14 @@ run_pending_traps ()
   if (catch_flag == 0)		/* simple optimization */
     return;
 
-  catch_flag = 0;
+  if (running_trap > 0)
+    {
+#if defined (DEBUG)
+      internal_warning ("run_pending_traps: recursive invocation while running trap for signal %d", running_trap-1);
+#endif
+    }
+
+  catch_flag = trapped_signal_received = 0;
 
   /* Preserve $? when running trap. */
   old_exit_value = last_command_exit_value;
@@ -279,25 +288,41 @@ run_pending_traps ()
 	 while (pending_traps[sig]--) instead of the if statement. */
       if (pending_traps[sig])
 	{
-#if defined (HAVE_POSIX_SIGNALS)
-	  sigset_t set, oset;
-
-	  sigemptyset (&set);
-	  sigemptyset (&oset);
-
-	  sigaddset (&set, sig);
-	  sigprocmask (SIG_BLOCK, &set, &oset);
-#else
-#  if defined (HAVE_BSD_SIGNALS)
-	  int oldmask = sigblock (sigmask (sig));
-#  endif
-#endif /* HAVE_POSIX_SIGNALS */
+	  running_trap = sig + 1;
 
 	  if (sig == SIGINT)
 	    {
+	      pending_traps[sig] = 0;	/* XXX */
 	      run_interrupt_trap ();
 	      CLRINTERRUPT;
 	    }
+#if defined (JOB_CONTROL) && defined (SIGCHLD)
+	  else if (sig == SIGCHLD &&
+		   trap_list[SIGCHLD] != (char *)IMPOSSIBLE_TRAP_HANDLER &&
+		   (sigmodes[SIGCHLD] & SIG_INPROGRESS) == 0)
+	    {
+	      sigmodes[SIGCHLD] |= SIG_INPROGRESS;
+	      run_sigchld_trap (pending_traps[sig]);	/* use as counter */
+	      sigmodes[SIGCHLD] &= ~SIG_INPROGRESS;
+	    }
+	  else if (sig == SIGCHLD &&
+		   trap_list[SIGCHLD] == (char *)IMPOSSIBLE_TRAP_HANDLER &&
+		   (sigmodes[SIGCHLD] & SIG_INPROGRESS) != 0)
+	    {
+	      /* This can happen when run_pending_traps is called while
+		 running a SIGCHLD trap handler. */
+	      running_trap = 0;
+	      /* want to leave pending_traps[SIGCHLD] alone here */
+	      continue;					/* XXX */
+	    }
+	  else if (sig == SIGCHLD && (sigmodes[SIGCHLD] & SIG_INPROGRESS))
+	    {
+	      /* whoops -- print warning? */
+	      running_trap = 0;		/* XXX */
+	      /* want to leave pending_traps[SIGCHLD] alone here */
+	      continue;
+	    }
+#endif
 	  else if (trap_list[sig] == (char *)DEFAULT_SIG ||
 		   trap_list[sig] == (char *)IGNORE_SIG ||
 		   trap_list[sig] == (char *)IMPOSSIBLE_TRAP_HANDLER)
@@ -332,14 +357,7 @@ run_pending_traps ()
 	    }
 
 	  pending_traps[sig] = 0;
-
-#if defined (HAVE_POSIX_SIGNALS)
-	  sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
-#else
-#  if defined (HAVE_BSD_SIGNALS)
-	  sigsetmask (oldmask);
-#  endif
-#endif /* POSIX_VERSION */
+	  running_trap = 0;
 	}
     }
 
@@ -366,10 +384,13 @@ trap_handler (sig)
       catch_flag = 1;
       pending_traps[sig]++;
 
-      if (interrupt_immediately && this_shell_builtin && (this_shell_builtin == wait_builtin))
+      trapped_signal_received = sig;
+
+      if (this_shell_builtin && (this_shell_builtin == wait_builtin))
 	{
 	  wait_signal_received = sig;
-	  longjmp (wait_intr_buf, 1);
+	  if (interrupt_immediately)
+	    longjmp (wait_intr_buf, 1);
 	}
 
       if (interrupt_immediately)
@@ -381,6 +402,24 @@ trap_handler (sig)
   SIGRETURN (0);
 }
 
+void
+check_signals ()
+{
+  /* Note: check_signals backported from bash-4.3, we don't have CHECK_ALRM
+   * here so don't call it at all. */
+  //CHECK_ALRM;		/* set by the read builtin */
+  QUIT;
+}
+
+/* Convenience functions the rest of the shell can use */
+void
+check_signals_and_traps ()
+{
+  check_signals ();
+
+  run_pending_traps ();
+}
+
 #if defined (JOB_CONTROL) && defined (SIGCHLD)
 
 #ifdef INCLUDE_UNUSED
@@ -402,6 +441,32 @@ maybe_set_sigchld_trap (command_string)
   if ((sigmodes[SIGCHLD] & SIG_TRAPPED) == 0)
     set_signal (SIGCHLD, command_string);
 }
+
+/* Temporarily set the SIGCHLD trap string to IMPOSSIBLE_TRAP_HANDLER.  Used
+   as a sentinel in run_sigchld_trap and maybe_set_sigchld_trap to see whether
+   or not a SIGCHLD trap handler reset SIGCHLD disposition to the default. */
+void
+set_impossible_sigchld_trap ()
+{
+  restore_default_signal (SIGCHLD);
+  change_signal (SIGCHLD, (char *)IMPOSSIBLE_TRAP_HANDLER);
+  sigmodes[SIGCHLD] &= ~SIG_TRAPPED;	/* maybe_set_sigchld_trap checks this */
+}
+
+/* Act as if we received SIGCHLD NCHILD times and increment
+   pending_traps[SIGCHLD] by that amount.  This allows us to still run the
+   SIGCHLD trap once for each exited child. */
+void
+queue_sigchld_trap (nchild)
+     int nchild;
+{
+  if (nchild > 0)
+    {
+      catch_flag = 1;
+      pending_traps[SIGCHLD] += nchild;
+      trapped_signal_received = SIGCHLD;
+    }
+}
 #endif /* JOB_CONTROL && SIGCHLD */
 
 void
diff --git a/trap.h b/trap.h
index d1600e0..977c7f6 100644
--- a/trap.h
+++ b/trap.h
@@ -62,7 +62,9 @@ extern void initialize_traps __P((void));
 
 extern void run_pending_traps __P((void));
 
+extern void queue_sigchld_trap __P((int));
 extern void maybe_set_sigchld_trap __P((char *));
+extern void set_impossible_sigchld_trap __P((void));
 extern void set_sigchld_trap __P((char *));
 
 extern void set_debug_trap __P((char *));
@@ -95,4 +97,6 @@ extern int signal_is_special __P((int));
 extern void set_signal_ignored __P((int));
 extern int signal_in_progress __P((int));
 
+extern void check_signals_and_traps __P((void));
+
 #endif /* _TRAP_H_ */
Index: bash-3.2/nojobs.c
===================================================================
--- bash-3.2.orig/nojobs.c
+++ bash-3.2/nojobs.c
@@ -430,10 +430,12 @@ reap_zombie_children ()
   WAIT status;
 
   CHECK_TERMSIG;
+  CHECK_WAIT_INTR;
   while ((pid = waitpid (-1, (int *)&status, WNOHANG)) > 0)
     set_pid_status (pid, status);
 #  endif /* WNOHANG */
   CHECK_TERMSIG;
+  CHECK_WAIT_INTR;
 }
 #endif /* WAITPID */
 
@@ -587,6 +589,7 @@ wait_for_single_pid (pid)
   while ((got_pid = WAITPID (pid, &status, 0)) != pid)
     {
       CHECK_TERMSIG;
+      CHECK_WAIT_INTR;
       if (got_pid < 0)
 	{
 	  if (errno != EINTR && errno != ECHILD)
@@ -741,6 +744,7 @@ wait_for (pid)
   while ((got_pid = WAITPID (-1, &status, 0)) != pid) /* XXX was pid now -1 */
     {
       CHECK_TERMSIG;
+      CHECK_WAIT_INTR;
       if (got_pid < 0 && errno == ECHILD)
 	{
 #if !defined (_POSIX_VERSION)
Index: bash-3.2/lib/malloc/malloc.c
===================================================================
--- bash-3.2.orig/lib/malloc/malloc.c
+++ bash-3.2/lib/malloc/malloc.c
@@ -267,7 +267,7 @@ extern char *sbrk ();
 #endif /* !HAVE_DECL_SBRK */
 
 #ifdef SHELL
-extern int interrupt_immediately;
+extern int interrupt_immediately, running_trap;
 extern int signal_is_trapped __P((int));
 #endif
 
@@ -564,7 +564,7 @@ morecore (nu)
   /* Block all signals in case we are executed from a signal handler. */
   blocked_sigs = 0;
 #ifdef SHELL
-  if (interrupt_immediately || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD))
+  if (interrupt_immediately || running_trap || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD))
 #endif
     {
       block_signals (&set, &oset);
Index: bash-3.2/lib/malloc/table.c
===================================================================
--- bash-3.2.orig/lib/malloc/table.c
+++ bash-3.2/lib/malloc/table.c
@@ -27,6 +27,11 @@
 #include "imalloc.h"
 #include "table.h"
 
+#ifdef SHELL
+extern int interrupt_immediately, running_trap;
+extern int signal_is_trapped __P((int));
+#endif
+
 extern int malloc_register;
 
 #ifdef MALLOC_REGISTER
@@ -167,6 +172,18 @@ mregister_alloc (tag, mem, size, file, l
      int line;
 {
   mr_table_t *tentry;
+  sigset_t set, oset;
+  int blocked_sigs;
+
+  /* Block all signals in case we are executed from a signal handler. */
+  blocked_sigs = 0;
+#ifdef SHELL
+  if (interrupt_immediately || running_trap || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD))
+#endif
+    {
+      _malloc_block_signals (&set, &oset);
+      blocked_sigs = 1;
+    }
 
   tentry = find_entry (mem, FIND_ALLOC);
 
@@ -174,6 +191,8 @@ mregister_alloc (tag, mem, size, file, l
     {
       /* oops.  table is full.  punt. */
       fprintf (stderr, _("register_alloc: alloc table is full with FIND_ALLOC?\n"));
+      if (blocked_sigs)
+	_malloc_unblock_signals (&set, &oset);
       return;
     }
   
@@ -193,6 +212,9 @@ mregister_alloc (tag, mem, size, file, l
 
   if (tentry != &mem_overflow)
     table_allocated++;
+
+  if (blocked_sigs)
+    _malloc_unblock_signals (&set, &oset);
 }
 
 void
@@ -203,6 +225,18 @@ mregister_free (mem, size, file, line)
      int line;
 {
   mr_table_t *tentry;
+  sigset_t set, oset;
+  int blocked_sigs;
+
+  /* Block all signals in case we are executed from a signal handler. */
+  blocked_sigs = 0;
+#ifdef SHELL
+  if (interrupt_immediately || running_trap || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD))
+#endif
+    {
+      _malloc_block_signals (&set, &oset);
+      blocked_sigs = 1;
+    }
 
   tentry = find_entry (mem, FIND_EXIST);
   if (tentry == 0)
@@ -211,6 +245,8 @@ mregister_free (mem, size, file, line)
 #if 0
       fprintf (stderr, "register_free: %p not in allocation table?\n", mem);
 #endif
+      if (blocked_sigs)
+	_malloc_unblock_signals (&set, &oset);
       return;
     }
   if (tentry->flags & MT_FREE)
@@ -227,6 +263,9 @@ mregister_free (mem, size, file, line)
 
   if (tentry != &mem_overflow)
     table_allocated--;
+
+  if (blocked_sigs)
+    _malloc_unblock_signals (&set, &oset);
 }
 
 /* If we ever add more flags, this will require changes. */
