summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChet Ramey <chet@caleb.ins.cwru.edu>2013-03-04 08:11:02 -0500
committerChet Ramey <chet@caleb.ins.cwru.edu>2013-03-04 08:11:02 -0500
commit83509ab7ce6a469f1a9397fae60409aee5c11574 (patch)
tree9085413d20db28f5d2b1bba73fa13aa7548499d5
parent73a146bec7f75da9f78f6d54329c980b75a2318d (diff)
downloadbash-83509ab7ce6a469f1a9397fae60409aee5c11574.tar.gz
commit bash-20130222 snapshot
-rw-r--r--CHANGES-4.325
-rw-r--r--CWRU/CWRU.chlog37
-rw-r--r--CWRU/CWRU.chlog~46
-rw-r--r--NEWS-4.36
-rw-r--r--bashline.c4
-rw-r--r--braces.c13
-rw-r--r--eval.c~292
-rw-r--r--jobs.c10
-rw-r--r--lib/readline/isearch.c1
-rw-r--r--lib/readline/readline.c3
-rw-r--r--lib/readline/search.c1
-rw-r--r--nojobs.c9
-rw-r--r--pcomplete.c2
-rw-r--r--quit.h2
-rw-r--r--quit.h~2
-rw-r--r--redir.c10
-rw-r--r--sig.c18
-rw-r--r--sig.c~722
-rw-r--r--sig.h7
-rw-r--r--sig.h~139
-rw-r--r--unwind_prot.c2
-rw-r--r--unwind_prot.c~357
22 files changed, 1685 insertions, 23 deletions
diff --git a/CHANGES-4.3 b/CHANGES-4.3
index 33f721be..3ef84605 100644
--- a/CHANGES-4.3
+++ b/CHANGES-4.3
@@ -315,6 +315,22 @@ vvvv. Worked around a kernel problem that caused SIGCHLD to interrupt open(2)
wwww. Fixed a problem that resulted in inconsistent expansion of $* and ${a[*]}.
+xxxx. Fixed a problem that caused `read -t' to crash when interrupted by
+ SIGINT.
+
+yyyy. Fixed a problem that caused pattern removal to fail randomly because the
+ pattern matcher read beyond the end of a string.
+
+zzzz. Fixed a bug that caused core dumps when shell functions tried to create
+ local shadow copies of special variables like GROUPS.
+
+aaaaa. Fixed a bug that caused SIGTERM to be occasionally lost by children of
+ interactive shells when it arrived before the child process reset the
+ handler from SIG_DFL.
+
+bbbbb. Fixed a bug that caused redirections like <&n- to leave file descriptor
+ n closed if executed with a builtin command.
+
2. Changes to Readline
a. Fixed a bug that did not allow the `dd', `cc', or `yy' vi editing mode
@@ -372,6 +388,9 @@ q. Fixed a bug that caused binding a macro to a multi-character key sequence
r. Fixed several redisplay errors with multibyte characters and prompts
containing invisible characters when using horizontal scrolling.
+s. Fixed a bug that caused redisplay errors when trying to overwrite
+ existing characters using multibyte characters.
+
3. New Features in Bash
a. The `helptopic' completion action now maps to all the help topics, not just
@@ -472,6 +491,12 @@ dd. The `printf' %(...)T format specifier now uses the current time if no
ee. There is a new variable, BASH_COMPAT, that controls the current shell
compatibility level.
+ff. The `popd' builtin now treats additional arguments as errors.
+
+gg. The brace expansion code now treats a failed sequence expansion as a
+ simple string and will continue to expand brace terms in the remainder
+ of the word.
+
4. New Features in Readline
a. Readline is now more responsive to SIGHUP and other fatal signals when
diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog
index 47a0914c..67d406f4 100644
--- a/CWRU/CWRU.chlog
+++ b/CWRU/CWRU.chlog
@@ -4528,7 +4528,7 @@ trap.c
lib/glob/xmbsrtowcs.c
- xdupmbstowcs2: fixed but where end of string was not handled
correctly, causing loop to go past end of string in a bunch of cases.
- Fixes bug reported by
+ Fixes bug reported by "Dashing" <dashing@hushmail.com>
2/13
@@ -4615,3 +4615,38 @@ lib/readline/display.c
- use new variable `bytes_to_insert' instead of overloading temp in
some code blocks (nls - nfd, bytes that comprise the characters
different in the new line from the old)
+
+ 2/18
+ ----
+redir.c
+ - do_redirection_internal: add undoable redirection for the implicit
+ close performed by the <&n- and >&n- redirections. Fixes bug
+ reported by Stephane Chazelas <stephane.chazelas@gmail.com>
+
+ 2/19
+ ----
+sig.c
+ - termsig_handler: an interactive shell killed by SIGHUP and keeping
+ command history will try to save the shell history before exiting.
+ This is an attempt to preserve the save-history-when-the-terminal-
+ window-is-closed behavior
+
+ 2/21
+ ----
+braces.c
+ - brace_expand: if a sequence expansion fails (e.g. because the
+ integers overflow), treat that expansion as a simple string, including
+ the braces, and try to process any remainder of the string. The
+ remainder may include brace expansions. Derived from SuSE bug
+ 804551 example (https://bugzilla.novell.com/show_bug.cgi?id=804551)
+
+ 2/23
+ ----
+{quit,sig}.h,sig.c
+ - sigterm_received declaration now in sig.h; type is sig_atomic_t
+ - sigwinch_received type now sig_atomic_t
+ - sig.h includes bashtypes.h and <signal.h> if SIG_DFL not defined
+ (same logic as trap.h) to pick up sig_atomic_t
+
+unwind_prot.c
+ - include sig.h before quit.h (reverse order)
diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~
index ad8ff0d1..53f67c34 100644
--- a/CWRU/CWRU.chlog~
+++ b/CWRU/CWRU.chlog~
@@ -4528,7 +4528,7 @@ trap.c
lib/glob/xmbsrtowcs.c
- xdupmbstowcs2: fixed but where end of string was not handled
correctly, causing loop to go past end of string in a bunch of cases.
- Fixes bug reported by
+ Fixes bug reported by "Dashing" <dashing@hushmail.com>
2/13
@@ -4603,3 +4603,47 @@ execute_cmd.c
lib/readline/display.c
- open_some_spaces: new function, subset of insert_some_chars that just
opens up a specified number of spaces to be overwritten
+ - insert_some_spaces: now just calls to open_some_spaces followed by
+ _rl_output_some_chars
+ - update_line: use col_temp instead of recalculating it using
+ _rl_col_width in the case where we use more columns with fewer bytes
+ - update_line: use open_some_spaces and then output the right number
+ of chars instead of trying to print new characters then overwrite
+ existing characters in two separate calls. This includes removing
+ some dodgy code and making things simpler. Fix from Egmont
+ Koblinger <egmont@gmail.com>
+ - use new variable `bytes_to_insert' instead of overloading temp in
+ some code blocks (nls - nfd, bytes that comprise the characters
+ different in the new line from the old)
+
+ 2/18
+ ----
+redir.c
+ - do_redirection_internal: add undoable redirection for the implicit
+ close performed by the <&n- and >&n- redirections. Fixes bug
+ reported by Stephane Chazelas <stephane.chazelas@gmail.com>
+
+ 2/19
+ ----
+sig.c
+ - termsig_handler: an interactive shell killed by SIGHUP and keeping
+ command history will try to save the shell history before exiting.
+ This is an attempt to preserve the save-history-when-the-terminal-
+ window-is-closed behavior
+
+ 2/21
+ ----
+braces.c
+ - brace_expand: if a sequence expansion fails (e.g. because the
+ integers overflow), treat that expansion as a simple string, including
+ the braces, and try to process any remainder of the string. The
+ remainder may include brace expansions. Derived from SuSE bug
+ 804551 example (https://bugzilla.novell.com/show_bug.cgi?id=804551)
+
+ 2/23
+ ----
+{quit,sig}.h,sig.c
+ - sigterm_received declaration now in sig.h; type is sig_atomic_t
+ - sigwinch_received type now sig_atomic_t
+ - sig.h includes bashtypes.h and <signal.h> if SIG_DFL not defined
+ (same logic as trap.h) to pick up sig_atomic_t
diff --git a/NEWS-4.3 b/NEWS-4.3
index a02033ae..94e30931 100644
--- a/NEWS-4.3
+++ b/NEWS-4.3
@@ -102,6 +102,12 @@ dd. The `printf' %(...)T format specifier now uses the current time if no
ee. There is a new variable, BASH_COMPAT, that controls the current shell
compatibility level.
+ff. The `popd' builtin now treats additional arguments as errors.
+
+gg. The brace expansion code now treats a failed sequence expansion as a
+ simple string and will continue to expand brace terms in the remainder
+ of the word.
+
2. New Features in Readline
a. Readline is now more responsive to SIGHUP and other fatal signals when
diff --git a/bashline.c b/bashline.c
index 3a72f042..eafe3d92 100644
--- a/bashline.c
+++ b/bashline.c
@@ -4140,10 +4140,6 @@ bash_dequote_text (text)
static int
bash_event_hook ()
{
-#if defined (DEBUG)
-itrace("bash_event_hook");
-#endif
-
/* If we're going to longjmp to top_level, make sure we clean up readline */
if (interrupt_state && signal_is_trapped (SIGINT) == 0)
rl_cleanup_after_signal ();
diff --git a/braces.c b/braces.c
index 155b5a08..3c373e11 100644
--- a/braces.c
+++ b/braces.c
@@ -228,6 +228,19 @@ brace_expand (text)
tack = expand_seqterm (amble, alen);
if (tack)
goto add_tack;
+ else if (text[i + 1])
+ {
+ /* If the sequence expansion fails (e.g., because the integers
+ overflow), but there is more in the string, try and process
+ the rest of the string, which may contain additional brace
+ expansions. Treat the unexpanded sequence term as a simple
+ string (including the braces). */
+ tack = strvec_create (2);
+ tack[0] = savestring (text+start-1);
+ tack[0][i-start+2] = '\0';
+ tack[1] = (char *)0;
+ goto add_tack;
+ }
else
{
free (amble);
diff --git a/eval.c~ b/eval.c~
new file mode 100644
index 00000000..2f96a068
--- /dev/null
+++ b/eval.c~
@@ -0,0 +1,292 @@
+/* eval.c -- reading and evaluating commands. */
+
+/* Copyright (C) 1996-2011 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 Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include "bashansi.h"
+#include <stdio.h>
+
+#include "bashintl.h"
+
+
+#include "shell.h"
+#include "flags.h"
+#include "trap.h"
+
+#include "builtins/common.h"
+
+#include "input.h"
+#include "execute_cmd.h"
+
+#if defined (HISTORY)
+# include "bashhist.h"
+#endif
+
+extern int EOF_reached;
+extern int indirection_level;
+extern int posixly_correct;
+extern int subshell_environment, running_under_emacs;
+extern int last_command_exit_value, stdin_redir;
+extern int need_here_doc;
+extern int current_command_number, current_command_line_count, line_number;
+extern int expand_aliases;
+
+#if defined (HAVE_POSIX_SIGNALS)
+extern sigset_t top_level_mask;
+#endif
+
+static void send_pwd_to_eterm __P((void));
+static sighandler alrm_catcher __P((int));
+
+/* Read and execute commands until EOF is reached. This assumes that
+ the input source has already been initialized. */
+int
+reader_loop ()
+{
+ int our_indirection_level;
+ COMMAND * volatile current_command;
+
+ USE_VAR(current_command);
+
+ current_command = (COMMAND *)NULL;
+
+ our_indirection_level = ++indirection_level;
+
+ while (EOF_Reached == 0)
+ {
+ int code;
+
+ code = setjmp_nosigs (top_level);
+
+#if defined (PROCESS_SUBSTITUTION)
+ unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+ /* XXX - why do we set this every time through the loop? */
+ if (interactive_shell && signal_is_ignored (SIGINT) == 0)
+ set_signal_handler (SIGINT, sigint_sighandler);
+
+ if (code != NOT_JUMPED)
+ {
+ indirection_level = our_indirection_level;
+
+ switch (code)
+ {
+ /* Some kind of throw to top_level has occured. */
+ case FORCE_EOF:
+ case ERREXIT:
+ case EXITPROG:
+ current_command = (COMMAND *)NULL;
+ if (exit_immediately_on_error)
+ variable_context = 0; /* not in a function */
+ EOF_Reached = EOF;
+ goto exec_done;
+
+ case DISCARD:
+ /* Make sure the exit status is reset to a non-zero value, but
+ leave existing non-zero values (e.g., > 128 on signal)
+ alone. */
+ if (last_command_exit_value == 0)
+ last_command_exit_value = EXECUTION_FAILURE;
+ if (subshell_environment)
+ {
+ current_command = (COMMAND *)NULL;
+ EOF_Reached = EOF;
+ goto exec_done;
+ }
+ /* Obstack free command elements, etc. */
+ if (current_command)
+ {
+ dispose_command (current_command);
+ current_command = (COMMAND *)NULL;
+ }
+#if defined (HAVE_POSIX_SIGNALS)
+ sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
+#endif
+ break;
+
+ default:
+ command_error ("reader_loop", CMDERR_BADJUMP, code, 0);
+ }
+ }
+
+ executing = 0;
+ if (temporary_env)
+ dispose_used_env_vars ();
+
+#if (defined (ultrix) && defined (mips)) || defined (C_ALLOCA)
+ /* Attempt to reclaim memory allocated with alloca (). */
+ (void) alloca (0);
+#endif
+
+ if (read_command () == 0)
+ {
+ if (interactive_shell == 0 && read_but_dont_execute)
+ {
+ last_command_exit_value = EXECUTION_SUCCESS;
+ dispose_command (global_command);
+ global_command = (COMMAND *)NULL;
+ }
+ else if (current_command = global_command)
+ {
+ global_command = (COMMAND *)NULL;
+ current_command_number++;
+
+ executing = 1;
+ stdin_redir = 0;
+ execute_command (current_command);
+
+ exec_done:
+ QUIT;
+
+ if (current_command)
+ {
+ dispose_command (current_command);
+ current_command = (COMMAND *)NULL;
+ }
+ }
+ }
+ else
+ {
+ /* Parse error, maybe discard rest of stream if not interactive. */
+ if (interactive == 0)
+ EOF_Reached = EOF;
+ }
+ if (just_one_command)
+ EOF_Reached = EOF;
+ }
+ indirection_level--;
+ return (last_command_exit_value);
+}
+
+static sighandler
+alrm_catcher(i)
+ int i;
+{
+ printf (_("\007timed out waiting for input: auto-logout\n"));
+ fflush (stdout);
+ bash_logout (); /* run ~/.bash_logout if this is a login shell */
+ jump_to_top_level (EXITPROG);
+ SIGRETURN (0);
+}
+
+/* Send an escape sequence to emacs term mode to tell it the
+ current working directory. */
+static void
+send_pwd_to_eterm ()
+{
+ char *pwd, *f;
+
+ f = 0;
+ pwd = get_string_value ("PWD");
+ if (pwd == 0)
+ f = pwd = get_working_directory ("eterm");
+ fprintf (stderr, "\032/%s\n", pwd);
+ free (f);
+}
+
+/* Call the YACC-generated parser and return the status of the parse.
+ Input is read from the current input stream (bash_input). yyparse
+ leaves the parsed command in the global variable GLOBAL_COMMAND.
+ This is where PROMPT_COMMAND is executed. */
+int
+parse_command ()
+{
+ int r;
+ char *command_to_execute;
+
+ need_here_doc = 0;
+ run_pending_traps ();
+
+ /* Allow the execution of a random command just before the printing
+ of each primary prompt. If the shell variable PROMPT_COMMAND
+ is set then the value of it is the command to execute. */
+ if (interactive && bash_input.type != st_string)
+ {
+ command_to_execute = get_string_value ("PROMPT_COMMAND");
+ if (command_to_execute)
+ execute_variable_command (command_to_execute, "PROMPT_COMMAND");
+
+ if (running_under_emacs == 2)
+ send_pwd_to_eterm (); /* Yuck */
+ }
+
+ current_command_line_count = 0;
+ r = yyparse ();
+
+ if (need_here_doc)
+ gather_here_documents ();
+
+ return (r);
+}
+
+/* Read and parse a command, returning the status of the parse. The command
+ is left in the globval variable GLOBAL_COMMAND for use by reader_loop.
+ This is where the shell timeout code is executed. */
+int
+read_command ()
+{
+ SHELL_VAR *tmout_var;
+ int tmout_len, result;
+ SigHandler *old_alrm;
+
+ set_current_prompt_level (1);
+ global_command = (COMMAND *)NULL;
+
+ /* Only do timeouts if interactive. */
+ tmout_var = (SHELL_VAR *)NULL;
+ tmout_len = 0;
+ old_alrm = (SigHandler *)NULL;
+
+ if (interactive)
+ {
+ tmout_var = find_variable ("TMOUT");
+
+ if (tmout_var && var_isset (tmout_var))
+ {
+ tmout_len = atoi (value_cell (tmout_var));
+ if (tmout_len > 0)
+ {
+ old_alrm = set_signal_handler (SIGALRM, alrm_catcher);
+ alarm (tmout_len);
+ }
+ }
+ }
+
+ QUIT;
+
+ current_command_line_count = 0;
+ result = parse_command ();
+
+ if (interactive && tmout_var && (tmout_len > 0))
+ {
+ alarm(0);
+ set_signal_handler (SIGALRM, old_alrm);
+ }
+
+ return (result);
+}
diff --git a/jobs.c b/jobs.c
index 1338661c..257cb6c8 100644
--- a/jobs.c
+++ b/jobs.c
@@ -1717,6 +1717,8 @@ make_child (command, async_p)
sigset_t set, oset;
pid_t pid;
+ /* XXX - block SIGTERM here and unblock in child after fork resets the
+ set of pending signals? */
sigemptyset (&set);
sigaddset (&set, SIGCHLD);
sigaddset (&set, SIGINT);
@@ -1747,11 +1749,16 @@ make_child (command, async_p)
waitchld (-1, 0);
sys_error ("fork: retry");
+ RESET_SIGTERM;
+
if (sleep (forksleep) != 0)
break;
forksleep <<= 1;
}
+ if (pid != 0)
+ RESET_SIGTERM;
+
if (pid < 0)
{
sys_error ("fork");
@@ -1767,9 +1774,6 @@ make_child (command, async_p)
throw_to_top_level (); /* Reset signals, etc. */
}
- if (pid != 0)
- RESET_SIGTERM;
-
if (pid == 0)
{
/* In the child. Give this child the right process group, set the
diff --git a/lib/readline/isearch.c b/lib/readline/isearch.c
index fcc01d9b..dfd8ff8c 100644
--- a/lib/readline/isearch.c
+++ b/lib/readline/isearch.c
@@ -318,6 +318,7 @@ _rl_search_getchar (cxt)
c = cxt->lastc = _rl_read_mbstring (cxt->lastc, cxt->mb, MB_LEN_MAX);
#endif
+ RL_CHECK_SIGNALS ();
return c;
}
diff --git a/lib/readline/readline.c b/lib/readline/readline.c
index 1946ff14..947dfdbe 100644
--- a/lib/readline/readline.c
+++ b/lib/readline/readline.c
@@ -559,7 +559,8 @@ readline_internal_charloop ()
/* look at input.c:rl_getc() for the circumstances under which this will
be returned; punt immediately on read error without converting it to
- a newline. */
+ a newline; assume that rl_read_key has already called the signal
+ handler. */
if (c == READERR)
{
#if defined (READLINE_CALLBACKS)
diff --git a/lib/readline/search.c b/lib/readline/search.c
index 45307465..aff6654e 100644
--- a/lib/readline/search.c
+++ b/lib/readline/search.c
@@ -468,6 +468,7 @@ rl_history_search_internal (count, dir)
copy into the line buffer. */
while (count)
{
+ RL_CHECK_SIGNALS ();
ret = noninc_search_from_pos (history_search_string, rl_history_search_pos + dir, dir);
if (ret == -1)
break;
diff --git a/nojobs.c b/nojobs.c
index 3046cd91..16126be5 100644
--- a/nojobs.c
+++ b/nojobs.c
@@ -501,11 +501,17 @@ make_child (command, async_p)
sync_buffered_stream (default_buffered_input);
#endif /* BUFFERED_INPUT */
+ /* XXX - block SIGTERM here and unblock in child after fork resets the
+ set of pending signals? */
+ RESET_SIGTERM;
+
/* Create the child, handle severe errors. Retry on EAGAIN. */
forksleep = 1;
while ((pid = fork ()) < 0 && errno == EAGAIN && forksleep < FORKSLEEP_MAX)
{
sys_error ("fork: retry");
+ RESET_SIGTERM;
+
#if defined (HAVE_WAITPID)
/* Posix systems with a non-blocking waitpid () system call available
get another chance after zombies are reaped. */
@@ -519,6 +525,9 @@ make_child (command, async_p)
forksleep <<= 1;
}
+ if (pid != 0)
+ RESET_SIGTERM;
+
if (pid < 0)
{
sys_error ("fork");
diff --git a/pcomplete.c b/pcomplete.c
index 38faab1a..6b4e0332 100644
--- a/pcomplete.c
+++ b/pcomplete.c
@@ -729,7 +729,7 @@ pcomp_filename_completion_function (text, state)
{
FREE (dfn);
/* remove backslashes quoting special characters in filenames. */
- /* There are roughtly three paths we can follow to get here:
+ /* There are roughly three paths we can follow to get here:
1. complete -f
2. compgen -f "$word" from a completion function
3. compgen -f "$word" from the command line
diff --git a/quit.h b/quit.h
index adfb3ebb..87d9b8c2 100644
--- a/quit.h
+++ b/quit.h
@@ -25,8 +25,6 @@
extern volatile int interrupt_state;
extern volatile int terminating_signal;
-extern sig_atomic_t sigterm_received;
-
/* Macro to call a great deal. SIGINT just sets the interrupt_state variable.
When it is safe, put QUIT in the code, and the "interrupt" will take
place. The same scheme is used for terminating signals (e.g., SIGHUP)
diff --git a/quit.h~ b/quit.h~
index c72d310b..adfb3ebb 100644
--- a/quit.h~
+++ b/quit.h~
@@ -1,6 +1,6 @@
/* quit.h -- How to handle SIGINT gracefully. */
-/* Copyright (C) 1993-2012 Free Software Foundation, Inc.
+/* Copyright (C) 1993-2013 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
diff --git a/redir.c b/redir.c
index aa3d16df..9e5ca0f0 100644
--- a/redir.c
+++ b/redir.c
@@ -1044,6 +1044,16 @@ do_redirection_internal (redirect, flags)
r = add_undo_close_redirect (redirector);
REDIRECTION_ERROR (r, errno, -1);
}
+ if ((flags & RX_UNDOABLE) && (ri == r_move_input || ri == r_move_output))
+ {
+ /* r_move_input and r_move_output add an additional close()
+ that needs to be undone */
+ if (fcntl (redirector, F_GETFD, 0) != -1)
+ {
+ r = add_undo_redirect (redir_fd, r_close_this, -1);
+ REDIRECTION_ERROR (r, errno, -1);
+ }
+ }
#if defined (BUFFERED_INPUT)
check_bash_input (redirector);
#endif
diff --git a/sig.c b/sig.c
index 893446aa..fc6e41e2 100644
--- a/sig.c
+++ b/sig.c
@@ -71,13 +71,14 @@ extern void initialize_siglist ();
volatile int interrupt_state = 0;
/* Non-zero after SIGWINCH */
-volatile int sigwinch_received = 0;
+sig_atomic_t sigwinch_received = 0;
+
+/* Non-zero after SIGTERM */
+sig_atomic_t sigterm_received = 0;
/* Set to the value of any terminating signal received. */
volatile int terminating_signal = 0;
-sig_atomic_t sigterm_received = 0;
-
/* The environment at the top-level R-E loop. We use this in
the case of error return. */
procenv_t top_level;
@@ -420,7 +421,8 @@ throw_to_top_level ()
#endif /* JOB_CONTROL */
#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
- /* This should not be necessary on systems using sigsetjmp/siglongjmp. */
+ /* This needs to stay because jobs.c:make_child() uses it without resetting
+ the signal mask. */
sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
#endif
@@ -553,12 +555,14 @@ termsig_handler (sig)
if (sig == SIGINT && signal_is_trapped (SIGINT))
run_interrupt_trap ();
-#if 0
#if defined (HISTORY)
- if (interactive_shell && (sig != SIGABRT && sig != SIGINT && sig != SIGHUP && sig != SIGTERM))
+ /* If we don't do something like this, the history will not be saved when
+ an interactive shell is running in a terminal window that gets closed
+ with the `close' button. We can't test for RL_STATE_READCMD because
+ readline no longer handles SIGTERM synchronously. */
+ if (interactive_shell && interactive && sig == SIGHUP && remember_on_history)
maybe_save_shell_history ();
#endif /* HISTORY */
-#endif
#if defined (JOB_CONTROL)
if (sig == SIGHUP && (interactive || (subshell_environment & (SUBSHELL_COMSUB|SUBSHELL_PROCSUB))))
diff --git a/sig.c~ b/sig.c~
new file mode 100644
index 00000000..539a1ff7
--- /dev/null
+++ b/sig.c~
@@ -0,0 +1,722 @@
+/* sig.c - interface for shell signal handlers and signal initialization. */
+
+/* Copyright (C) 1994-2013 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 Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include "bashtypes.h"
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+
+#include "bashintl.h"
+
+#include "shell.h"
+#if defined (JOB_CONTROL)
+#include "jobs.h"
+#endif /* JOB_CONTROL */
+#include "siglist.h"
+#include "sig.h"
+#include "trap.h"
+
+#include "builtins/common.h"
+
+#if defined (READLINE)
+# include "bashline.h"
+# include <readline/readline.h>
+#endif
+
+#if defined (HISTORY)
+# include "bashhist.h"
+#endif
+
+extern int last_command_exit_value;
+extern int last_command_exit_signal;
+extern int return_catch_flag;
+extern int loop_level, continuing, breaking, funcnest;
+extern int executing_list;
+extern int comsub_ignore_return;
+extern int parse_and_execute_level, shell_initialized;
+#if defined (HISTORY)
+extern int history_lines_this_session;
+#endif
+extern int no_line_editing;
+
+extern void initialize_siglist ();
+
+/* Non-zero after SIGINT. */
+volatile int interrupt_state = 0;
+
+/* Non-zero after SIGWINCH */
+volatile int sigwinch_received = 0;
+
+/* Set to the value of any terminating signal received. */
+volatile int terminating_signal = 0;
+
+sig_atomic_t sigterm_received = 0;
+
+/* The environment at the top-level R-E loop. We use this in
+ the case of error return. */
+procenv_t top_level;
+
+#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
+/* The signal masks that this shell runs with. */
+sigset_t top_level_mask;
+#endif /* JOB_CONTROL */
+
+/* When non-zero, we throw_to_top_level (). */
+int interrupt_immediately = 0;
+
+/* When non-zero, we call the terminating signal handler immediately. */
+int terminate_immediately = 0;
+
+#if defined (SIGWINCH)
+static SigHandler *old_winch = (SigHandler *)SIG_DFL;
+#endif
+
+static void initialize_shell_signals __P((void));
+
+void
+initialize_signals (reinit)
+ int reinit;
+{
+ initialize_shell_signals ();
+ initialize_job_signals ();
+#if !defined (HAVE_SYS_SIGLIST) && !defined (HAVE_UNDER_SYS_SIGLIST) && !defined (HAVE_STRSIGNAL)
+ if (reinit == 0)
+ initialize_siglist ();
+#endif /* !HAVE_SYS_SIGLIST && !HAVE_UNDER_SYS_SIGLIST && !HAVE_STRSIGNAL */
+}
+
+/* A structure describing a signal that terminates the shell if not
+ caught. The orig_handler member is present so children can reset
+ these signals back to their original handlers. */
+struct termsig {
+ int signum;
+ SigHandler *orig_handler;
+ int orig_flags;
+};
+
+#define NULL_HANDLER (SigHandler *)SIG_DFL
+
+/* The list of signals that would terminate the shell if not caught.
+ We catch them, but just so that we can write the history file,
+ and so forth. */
+static struct termsig terminating_signals[] = {
+#ifdef SIGHUP
+{ SIGHUP, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGINT
+{ SIGINT, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGILL
+{ SIGILL, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGTRAP
+{ SIGTRAP, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGIOT
+{ SIGIOT, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGDANGER
+{ SIGDANGER, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGEMT
+{ SIGEMT, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGFPE
+{ SIGFPE, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGBUS
+{ SIGBUS, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGSEGV
+{ SIGSEGV, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGSYS
+{ SIGSYS, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGPIPE
+{ SIGPIPE, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGALRM
+{ SIGALRM, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGTERM
+{ SIGTERM, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGXCPU
+{ SIGXCPU, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGXFSZ
+{ SIGXFSZ, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGVTALRM
+{ SIGVTALRM, NULL_HANDLER, 0 },
+#endif
+
+#if 0
+#ifdef SIGPROF
+{ SIGPROF, NULL_HANDLER, 0 },
+#endif
+#endif
+
+#ifdef SIGLOST
+{ SIGLOST, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGUSR1
+{ SIGUSR1, NULL_HANDLER, 0 },
+#endif
+
+#ifdef SIGUSR2
+{ SIGUSR2, NULL_HANDLER, 0 },
+#endif
+};
+
+#define TERMSIGS_LENGTH (sizeof (terminating_signals) / sizeof (struct termsig))
+
+#define XSIG(x) (terminating_signals[x].signum)
+#define XHANDLER(x) (terminating_signals[x].orig_handler)
+#define XSAFLAGS(x) (terminating_signals[x].orig_flags)
+
+static int termsigs_initialized = 0;
+
+/* Initialize signals that will terminate the shell to do some
+ unwind protection. For non-interactive shells, we only call
+ this when a trap is defined for EXIT (0) or when trap is run
+ to display signal dispositions. */
+void
+initialize_terminating_signals ()
+{
+ register int i;
+#if defined (HAVE_POSIX_SIGNALS)
+ struct sigaction act, oact;
+#endif
+
+ if (termsigs_initialized)
+ return;
+
+ /* The following code is to avoid an expensive call to
+ set_signal_handler () for each terminating_signals. Fortunately,
+ this is possible in Posix. Unfortunately, we have to call signal ()
+ on non-Posix systems for each signal in terminating_signals. */
+#if defined (HAVE_POSIX_SIGNALS)
+ act.sa_handler = termsig_sighandler;
+ act.sa_flags = 0;
+ sigemptyset (&act.sa_mask);
+ sigemptyset (&oact.sa_mask);
+ for (i = 0; i < TERMSIGS_LENGTH; i++)
+ sigaddset (&act.sa_mask, XSIG (i));
+ for (i = 0; i < TERMSIGS_LENGTH; i++)
+ {
+ /* If we've already trapped it, don't do anything. */
+ if (signal_is_trapped (XSIG (i)))
+ continue;
+
+ sigaction (XSIG (i), &act, &oact);
+ XHANDLER(i) = oact.sa_handler;
+ XSAFLAGS(i) = oact.sa_flags;
+ /* Don't do anything with signals that are ignored at shell entry
+ if the shell is not interactive. */
+ /* XXX - should we do this for interactive shells, too? */
+ if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN)
+ {
+ sigaction (XSIG (i), &oact, &act);
+ set_signal_ignored (XSIG (i));
+ }
+#if defined (SIGPROF) && !defined (_MINIX)
+ if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
+ sigaction (XSIG (i), &oact, (struct sigaction *)NULL);
+#endif /* SIGPROF && !_MINIX */
+ }
+
+#else /* !HAVE_POSIX_SIGNALS */
+
+ for (i = 0; i < TERMSIGS_LENGTH; i++)
+ {
+ /* If we've already trapped it, don't do anything. */
+ if (signal_is_trapped (XSIG (i)))
+ continue;
+
+ XHANDLER(i) = signal (XSIG (i), termsig_sighandler);
+ XSAFLAGS(i) = 0;
+ /* Don't do anything with signals that are ignored at shell entry
+ if the shell is not interactive. */
+ /* XXX - should we do this for interactive shells, too? */
+ if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN)
+ {
+ signal (XSIG (i), SIG_IGN);
+ set_signal_ignored (XSIG (i));
+ }
+#ifdef SIGPROF
+ if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
+ signal (XSIG (i), XHANDLER (i));
+#endif
+ }
+
+#endif /* !HAVE_POSIX_SIGNALS */
+
+ termsigs_initialized = 1;
+}
+
+static void
+initialize_shell_signals ()
+{
+ if (interactive)
+ initialize_terminating_signals ();
+
+#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
+ /* All shells use the signal mask they inherit, and pass it along
+ to child processes. Children will never block SIGCHLD, though. */
+ sigemptyset (&top_level_mask);
+ sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &top_level_mask);
+# if defined (SIGCHLD)
+ sigdelset (&top_level_mask, SIGCHLD);
+# endif
+#endif /* JOB_CONTROL || HAVE_POSIX_SIGNALS */
+
+ /* And, some signals that are specifically ignored by the shell. */
+ set_signal_handler (SIGQUIT, SIG_IGN);
+
+ if (interactive)
+ {
+ set_signal_handler (SIGINT, sigint_sighandler);
+ get_original_signal (SIGTERM);
+ if (signal_is_hard_ignored (SIGTERM) == 0)
+ set_signal_handler (SIGTERM, sigterm_sighandler);
+ set_sigwinch_handler ();
+ }
+}
+
+void
+reset_terminating_signals ()
+{
+ register int i;
+#if defined (HAVE_POSIX_SIGNALS)
+ struct sigaction act;
+#endif
+
+ if (termsigs_initialized == 0)
+ return;
+
+#if defined (HAVE_POSIX_SIGNALS)
+ act.sa_flags = 0;
+ sigemptyset (&act.sa_mask);
+ for (i = 0; i < TERMSIGS_LENGTH; i++)
+ {
+ /* Skip a signal if it's trapped or handled specially, because the
+ trap code will restore the correct value. */
+ if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
+ continue;
+
+ act.sa_handler = XHANDLER (i);
+ act.sa_flags = XSAFLAGS (i);
+ sigaction (XSIG (i), &act, (struct sigaction *) NULL);
+ }
+#else /* !HAVE_POSIX_SIGNALS */
+ for (i = 0; i < TERMSIGS_LENGTH; i++)
+ {
+ if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
+ continue;
+
+ signal (XSIG (i), XHANDLER (i));
+ }
+#endif /* !HAVE_POSIX_SIGNALS */
+
+ termsigs_initialized = 0;
+}
+#undef XSIG
+#undef XHANDLER
+
+/* Run some of the cleanups that should be performed when we run
+ jump_to_top_level from a builtin command context. XXX - might want to
+ also call reset_parser here. */
+void
+top_level_cleanup ()
+{
+ /* Clean up string parser environment. */
+ while (parse_and_execute_level)
+ parse_and_execute_cleanup ();
+
+#if defined (PROCESS_SUBSTITUTION)
+ unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+ run_unwind_protects ();
+ loop_level = continuing = breaking = funcnest = 0;
+ executing_list = comsub_ignore_return = return_catch_flag = 0;
+}
+
+/* What to do when we've been interrupted, and it is safe to handle it. */
+void
+throw_to_top_level ()
+{
+ int print_newline = 0;
+
+ if (interrupt_state)
+ {
+ if (last_command_exit_value < 128)
+ last_command_exit_value = 128 + SIGINT;
+ print_newline = 1;
+ DELINTERRUPT;
+ }
+
+ if (interrupt_state)
+ return;
+
+ last_command_exit_signal = (last_command_exit_value > 128) ?
+ (last_command_exit_value - 128) : 0;
+ last_command_exit_value |= 128;
+
+ /* Run any traps set on SIGINT. */
+ run_interrupt_trap ();
+
+ /* Clean up string parser environment. */
+ while (parse_and_execute_level)
+ parse_and_execute_cleanup ();
+
+#if defined (JOB_CONTROL)
+ give_terminal_to (shell_pgrp, 0);
+#endif /* JOB_CONTROL */
+
+#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
+ /* This needs to stay because jobs.c:make_child() uses it without resetting
+ the signal mask. */
+ sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
+#endif
+
+ reset_parser ();
+
+#if defined (READLINE)
+ if (interactive)
+ bashline_reset ();
+#endif /* READLINE */
+
+#if defined (PROCESS_SUBSTITUTION)
+ unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+ run_unwind_protects ();
+ loop_level = continuing = breaking = funcnest = 0;
+ executing_list = comsub_ignore_return = return_catch_flag = 0;
+
+ if (interactive && print_newline)
+ {
+ fflush (stdout);
+ fprintf (stderr, "\n");
+ fflush (stderr);
+ }
+
+ /* An interrupted `wait' command in a script does not exit the script. */
+ if (interactive || (interactive_shell && !shell_initialized) ||
+ (print_newline && signal_is_trapped (SIGINT)))
+ jump_to_top_level (DISCARD);
+ else
+ jump_to_top_level (EXITPROG);
+}
+
+/* This is just here to isolate the longjmp calls. */
+void
+jump_to_top_level (value)
+ int value;
+{
+ longjmp (top_level, value);
+}
+
+sighandler
+termsig_sighandler (sig)
+ int sig;
+{
+ /* If we get called twice with the same signal before handling it,
+ terminate right away. */
+ if (
+#ifdef SIGHUP
+ sig != SIGHUP &&
+#endif
+#ifdef SIGINT
+ sig != SIGINT &&
+#endif
+#ifdef SIGDANGER
+ sig != SIGDANGER &&
+#endif
+#ifdef SIGPIPE
+ sig != SIGPIPE &&
+#endif
+#ifdef SIGALRM
+ sig != SIGALRM &&
+#endif
+#ifdef SIGTERM
+ sig != SIGTERM &&
+#endif
+#ifdef SIGXCPU
+ sig != SIGXCPU &&
+#endif
+#ifdef SIGXFSZ
+ sig != SIGXFSZ &&
+#endif
+#ifdef SIGVTALRM
+ sig != SIGVTALRM &&
+#endif
+#ifdef SIGLOST
+ sig != SIGLOST &&
+#endif
+#ifdef SIGUSR1
+ sig != SIGUSR1 &&
+#endif
+#ifdef SIGUSR2
+ sig != SIGUSR2 &&
+#endif
+ sig == terminating_signal)
+ terminate_immediately = 1;
+
+ terminating_signal = sig;
+
+ /* XXX - should this also trigger when interrupt_immediately is set? */
+ if (terminate_immediately)
+ {
+#if defined (HISTORY)
+ /* XXX - will inhibit history file being written */
+# if defined (READLINE)
+ if (interactive_shell == 0 || interactive == 0 || (sig != SIGHUP && sig != SIGTERM) || no_line_editing || (RL_ISSTATE (RL_STATE_READCMD) == 0))
+# endif
+ history_lines_this_session = 0;
+#endif
+ terminate_immediately = 0;
+ termsig_handler (sig);
+ }
+
+#if defined (READLINE)
+ /* Set the event hook so readline will call it after the signal handlers
+ finish executing, so if this interrupted character input we can get
+ quick response. */
+ if (interactive_shell && interactive && no_line_editing == 0)
+ bashline_set_event_hook ();
+#endif
+
+ SIGRETURN (0);
+}
+
+void
+termsig_handler (sig)
+ int sig;
+{
+ static int handling_termsig = 0;
+
+ /* Simple semaphore to keep this function from being executed multiple
+ times. Since we no longer are running as a signal handler, we don't
+ block multiple occurrences of the terminating signals while running. */
+ if (handling_termsig)
+ return;
+ handling_termsig = 1;
+ terminating_signal = 0; /* keep macro from re-testing true. */
+
+ /* I don't believe this condition ever tests true. */
+ if (sig == SIGINT && signal_is_trapped (SIGINT))
+ run_interrupt_trap ();
+
+#if defined (HISTORY)
+ /* If we don't do something like this, the history will not be saved when
+ an interactive shell is running in a terminal window that gets closed
+ with the `close' button. We can't test for RL_STATE_READCMD because
+ readline no longer handles SIGTERM synchronously. */
+ if (interactive_shell && interactive && sig == SIGHUP && remember_on_history)
+ maybe_save_shell_history ();
+#endif /* HISTORY */
+
+#if defined (JOB_CONTROL)
+ if (sig == SIGHUP && (interactive || (subshell_environment & (SUBSHELL_COMSUB|SUBSHELL_PROCSUB))))
+ hangup_all_jobs ();
+ end_job_control ();
+#endif /* JOB_CONTROL */
+
+#if defined (PROCESS_SUBSTITUTION)
+ unlink_fifo_list ();
+#endif /* PROCESS_SUBSTITUTION */
+
+ /* Reset execution context */
+ loop_level = continuing = breaking = funcnest = 0;
+ executing_list = comsub_ignore_return = return_catch_flag = 0;
+
+ run_exit_trap ();
+ set_signal_handler (sig, SIG_DFL);
+ kill (getpid (), sig);
+}
+
+/* What we really do when SIGINT occurs. */
+sighandler
+sigint_sighandler (sig)
+ int sig;
+{
+#if defined (MUST_REINSTALL_SIGHANDLERS)
+ signal (sig, sigint_sighandler);
+#endif
+
+ /* interrupt_state needs to be set for the stack of interrupts to work
+ right. Should it be set unconditionally? */
+ if (interrupt_state == 0)
+ ADDINTERRUPT;
+
+ if (interrupt_immediately)
+ {
+ interrupt_immediately = 0;
+ last_command_exit_value = 128 + sig;
+ throw_to_top_level ();
+ }
+#if defined (READLINE)
+ /* Set the event hook so readline will call it after the signal handlers
+ finish executing, so if this interrupted character input we can get
+ quick response. */
+ else if (RL_ISSTATE (RL_STATE_SIGHANDLER))
+ bashline_set_event_hook ();
+#endif
+
+ SIGRETURN (0);
+}
+
+#if defined (SIGWINCH)
+sighandler
+sigwinch_sighandler (sig)
+ int sig;
+{
+#if defined (MUST_REINSTALL_SIGHANDLERS)
+ set_signal_handler (SIGWINCH, sigwinch_sighandler);
+#endif /* MUST_REINSTALL_SIGHANDLERS */
+ sigwinch_received = 1;
+ SIGRETURN (0);
+}
+#endif /* SIGWINCH */
+
+void
+set_sigwinch_handler ()
+{
+#if defined (SIGWINCH)
+ old_winch = set_signal_handler (SIGWINCH, sigwinch_sighandler);
+#endif
+}
+
+void
+unset_sigwinch_handler ()
+{
+#if defined (SIGWINCH)
+ set_signal_handler (SIGWINCH, old_winch);
+#endif
+}
+
+sighandler
+sigterm_sighandler (sig)
+ int sig;
+{
+ sigterm_received = 1; /* XXX - counter? */
+ SIGRETURN (0);
+}
+
+/* Signal functions used by the rest of the code. */
+#if !defined (HAVE_POSIX_SIGNALS)
+
+/* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */
+sigprocmask (operation, newset, oldset)
+ int operation, *newset, *oldset;
+{
+ int old, new;
+
+ if (newset)
+ new = *newset;
+ else
+ new = 0;
+
+ switch (operation)
+ {
+ case SIG_BLOCK:
+ old = sigblock (new);
+ break;
+
+ case SIG_SETMASK:
+ old = sigsetmask (new);
+ break;
+
+ default:
+ internal_error (_("sigprocmask: %d: invalid operation"), operation);
+ }
+
+ if (oldset)
+ *oldset = old;
+}
+
+#else
+
+#if !defined (SA_INTERRUPT)
+# define SA_INTERRUPT 0
+#endif
+
+#if !defined (SA_RESTART)
+# define SA_RESTART 0
+#endif
+
+SigHandler *
+set_signal_handler (sig, handler)
+ int sig;
+ SigHandler *handler;
+{
+ struct sigaction act, oact;
+
+ act.sa_handler = handler;
+ act.sa_flags = 0;
+
+ /* XXX - bash-4.2 */
+ /* We don't want a child death to interrupt interruptible system calls, even
+ if we take the time to reap children */
+#if defined (SIGCHLD)
+ if (sig == SIGCHLD)
+ act.sa_flags |= SA_RESTART; /* XXX */
+#endif
+ /* If we're installing a SIGTERM handler for interactive shells, we want
+ it to be as close to SIG_IGN as possible. */
+ if (sig == SIGTERM && handler == sigterm_sighandler)
+ act.sa_flags |= SA_RESTART; /* XXX */
+
+ sigemptyset (&act.sa_mask);
+ sigemptyset (&oact.sa_mask);
+ sigaction (sig, &act, &oact);
+ return (oact.sa_handler);
+}
+#endif /* HAVE_POSIX_SIGNALS */
diff --git a/sig.h b/sig.h
index 392072f2..eba65ec7 100644
--- a/sig.h
+++ b/sig.h
@@ -25,6 +25,10 @@
#include "stdc.h"
+#if !defined (SIG_DFL)
+# include <signal.h> /* for sig_atomic_t */
+#endif
+
#if !defined (SIGABRT) && defined (SIGIOT)
# define SIGABRT SIGIOT
#endif
@@ -104,7 +108,8 @@ do { \
#endif /* !HAVE_POSIX_SIGNALS */
/* Extern variables */
-extern volatile int sigwinch_received;
+extern sig_atomic_t sigwinch_received;
+extern sig_atomic_t sigterm_received;
extern int interrupt_immediately;
extern int terminate_immediately;
diff --git a/sig.h~ b/sig.h~
new file mode 100644
index 00000000..fa28c729
--- /dev/null
+++ b/sig.h~
@@ -0,0 +1,139 @@
+/* sig.h -- header file for signal handler definitions. */
+
+/* Copyright (C) 1994-2013 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 Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Make sure that this is included *after* config.h! */
+
+#if !defined (_SIG_H_)
+# define _SIG_H_
+
+#include "stdc.h"
+
+#if !defined (SIG_DFL)
+# include <signal.h>
+#endif
+
+#if !defined (SIGABRT) && defined (SIGIOT)
+# define SIGABRT SIGIOT
+#endif
+
+#define sighandler RETSIGTYPE
+typedef RETSIGTYPE SigHandler __P((int));
+
+#if defined (VOID_SIGHANDLER)
+# define SIGRETURN(n) return
+#else
+# define SIGRETURN(n) return(n)
+#endif /* !VOID_SIGHANDLER */
+
+/* Here is a definition for set_signal_handler () which simply expands to
+ a call to signal () for non-Posix systems. The code for set_signal_handler
+ in the Posix case resides in general.c. */
+#if !defined (HAVE_POSIX_SIGNALS)
+# define set_signal_handler(sig, handler) (SigHandler *)signal (sig, handler)
+#else
+extern SigHandler *set_signal_handler __P((int, SigHandler *)); /* in sig.c */
+#endif /* _POSIX_VERSION */
+
+#if !defined (SIGCHLD) && defined (SIGCLD)
+# define SIGCHLD SIGCLD
+#endif
+
+#if !defined (HAVE_POSIX_SIGNALS) && !defined (sigmask)
+# define sigmask(x) (1 << ((x)-1))
+#endif /* !HAVE_POSIX_SIGNALS && !sigmask */
+
+#if !defined (HAVE_POSIX_SIGNALS)
+# if !defined (SIG_BLOCK)
+# define SIG_BLOCK 2
+# define SIG_SETMASK 3
+# endif /* SIG_BLOCK */
+
+/* sigset_t defined in config.h */
+
+/* Make sure there is nothing inside the signal set. */
+# define sigemptyset(set) (*(set) = 0)
+
+/* Initialize the signal set to hold all signals. */
+# define sigfillset(set) (*set) = sigmask (NSIG) - 1
+
+/* Add SIG to the contents of SET. */
+# define sigaddset(set, sig) *(set) |= sigmask (sig)
+
+/* Delete SIG from signal set SET. */
+# define sigdelset(set, sig) *(set) &= ~sigmask (sig)
+
+/* Is SIG a member of the signal set SET? */
+# define sigismember(set, sig) ((*(set) & sigmask (sig)) != 0)
+
+/* Suspend the process until the reception of one of the signals
+ not present in SET. */
+# define sigsuspend(set) sigpause (*(set))
+#endif /* !HAVE_POSIX_SIGNALS */
+
+/* These definitions are used both in POSIX and non-POSIX implementations. */
+
+#define BLOCK_SIGNAL(sig, nvar, ovar) \
+do { \
+ sigemptyset (&nvar); \
+ sigaddset (&nvar, sig); \
+ sigemptyset (&ovar); \
+ sigprocmask (SIG_BLOCK, &nvar, &ovar); \
+} while (0)
+
+#define UNBLOCK_SIGNAL(ovar) sigprocmask (SIG_SETMASK, &ovar, (sigset_t *) NULL)
+
+#if defined (HAVE_POSIX_SIGNALS)
+# define BLOCK_CHILD(nvar, ovar) BLOCK_SIGNAL (SIGCHLD, nvar, ovar)
+# define UNBLOCK_CHILD(ovar) UNBLOCK_SIGNAL(ovar)
+#else /* !HAVE_POSIX_SIGNALS */
+# define BLOCK_CHILD(nvar, ovar) ovar = sigblock (sigmask (SIGCHLD))
+# define UNBLOCK_CHILD(ovar) sigsetmask (ovar)
+#endif /* !HAVE_POSIX_SIGNALS */
+
+/* Extern variables */
+extern sig_atomic_t sigwinch_received;
+extern sig_atomic_t sigterm_received;
+
+extern int interrupt_immediately;
+extern int terminate_immediately;
+
+/* Functions from sig.c. */
+extern sighandler termsig_sighandler __P((int));
+extern void termsig_handler __P((int));
+extern sighandler sigint_sighandler __P((int));
+extern void initialize_signals __P((int));
+extern void initialize_terminating_signals __P((void));
+extern void reset_terminating_signals __P((void));
+extern void top_level_cleanup __P((void));
+extern void throw_to_top_level __P((void));
+extern void jump_to_top_level __P((int)) __attribute__((__noreturn__));
+
+extern sighandler sigwinch_sighandler __P((int));
+extern void set_sigwinch_handler __P((void));
+extern void unset_sigwinch_handler __P((void));
+
+extern sighandler sigterm_sighandler __P((int));
+
+/* Functions defined in trap.c. */
+extern SigHandler *set_sigint_handler __P((void));
+extern SigHandler *trap_to_sighandler __P((int));
+extern sighandler trap_handler __P((int));
+
+#endif /* _SIG_H_ */
diff --git a/unwind_prot.c b/unwind_prot.c
index a477c03a..85a10b57 100644
--- a/unwind_prot.c
+++ b/unwind_prot.c
@@ -46,8 +46,8 @@
#include "command.h"
#include "general.h"
#include "unwind_prot.h"
-#include "quit.h"
#include "sig.h"
+#include "quit.h"
#include "error.h" /* for internal_warning */
/* Structure describing a saved variable and the value to restore it to. */
diff --git a/unwind_prot.c~ b/unwind_prot.c~
new file mode 100644
index 00000000..a477c03a
--- /dev/null
+++ b/unwind_prot.c~
@@ -0,0 +1,357 @@
+/* unwind_prot.c - a simple unwind-protect system for internal variables */
+
+/* I can't stand it anymore! Please can't we just write the
+ whole Unix system in lisp or something? */
+
+/* Copyright (C) 1987-2009 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 Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* **************************************************************** */
+/* */
+/* Unwind Protection Scheme for Bash */
+/* */
+/* **************************************************************** */
+#include "config.h"
+
+#include "bashtypes.h"
+#include "bashansi.h"
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#if STDC_HEADERS
+# include <stddef.h>
+#endif
+
+#ifndef offsetof
+# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#include "command.h"
+#include "general.h"
+#include "unwind_prot.h"
+#include "quit.h"
+#include "sig.h"
+#include "error.h" /* for internal_warning */
+
+/* Structure describing a saved variable and the value to restore it to. */
+typedef struct {
+ char *variable;
+ int size;
+ char desired_setting[1]; /* actual size is `size' */
+} SAVED_VAR;
+
+/* If HEAD.CLEANUP is null, then ARG.V contains a tag to throw back to.
+ If HEAD.CLEANUP is restore_variable, then SV.V contains the saved
+ variable. Otherwise, call HEAD.CLEANUP (ARG.V) to clean up. */
+typedef union uwp {
+ struct uwp_head {
+ union uwp *next;
+ Function *cleanup;
+ } head;
+ struct {
+ struct uwp_head uwp_head;
+ char *v;
+ } arg;
+ struct {
+ struct uwp_head uwp_head;
+ SAVED_VAR v;
+ } sv;
+} UNWIND_ELT;
+
+
+static void without_interrupts __P((VFunction *, char *, char *));
+static void unwind_frame_discard_internal __P((char *, char *));
+static void unwind_frame_run_internal __P((char *, char *));
+static void add_unwind_protect_internal __P((Function *, char *));
+static void remove_unwind_protect_internal __P((char *, char *));
+static void run_unwind_protects_internal __P((char *, char *));
+static void clear_unwind_protects_internal __P((char *, char *));
+static inline void restore_variable __P((SAVED_VAR *));
+static void unwind_protect_mem_internal __P((char *, char *));
+
+static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL;
+
+#define uwpalloc(elt) (elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT))
+#define uwpfree(elt) free(elt)
+
+/* Run a function without interrupts. This relies on the fact that the
+ FUNCTION cannot change the value of interrupt_immediately. (I.e., does
+ not call QUIT (). */
+static void
+without_interrupts (function, arg1, arg2)
+ VFunction *function;
+ char *arg1, *arg2;
+{
+ int old_interrupt_immediately;
+
+ old_interrupt_immediately = interrupt_immediately;
+ interrupt_immediately = 0;
+
+ (*function)(arg1, arg2);
+
+ interrupt_immediately = old_interrupt_immediately;
+}
+
+/* Start the beginning of a region. */
+void
+begin_unwind_frame (tag)
+ char *tag;
+{
+ add_unwind_protect ((Function *)NULL, tag);
+}
+
+/* Discard the unwind protects back to TAG. */
+void
+discard_unwind_frame (tag)
+ char *tag;
+{
+ if (unwind_protect_list)
+ without_interrupts (unwind_frame_discard_internal, tag, (char *)NULL);
+}
+
+/* Run the unwind protects back to TAG. */
+void
+run_unwind_frame (tag)
+ char *tag;
+{
+ if (unwind_protect_list)
+ without_interrupts (unwind_frame_run_internal, tag, (char *)NULL);
+}
+
+/* Add the function CLEANUP with ARG to the list of unwindable things. */
+void
+add_unwind_protect (cleanup, arg)
+ Function *cleanup;
+ char *arg;
+{
+ without_interrupts (add_unwind_protect_internal, (char *)cleanup, arg);
+}
+
+/* Remove the top unwind protect from the list. */
+void
+remove_unwind_protect ()
+{
+ if (unwind_protect_list)
+ without_interrupts
+ (remove_unwind_protect_internal, (char *)NULL, (char *)NULL);
+}
+
+/* Run the list of cleanup functions in unwind_protect_list. */
+void
+run_unwind_protects ()
+{
+ if (unwind_protect_list)
+ without_interrupts
+ (run_unwind_protects_internal, (char *)NULL, (char *)NULL);
+}
+
+/* Erase the unwind-protect list. If flags is 1, free the elements. */
+void
+clear_unwind_protect_list (flags)
+ int flags;
+{
+ char *flag;
+
+ if (unwind_protect_list)
+ {
+ flag = flags ? "" : (char *)NULL;
+ without_interrupts
+ (clear_unwind_protects_internal, flag, (char *)NULL);
+ }
+}
+
+int
+have_unwind_protects ()
+{
+ return (unwind_protect_list != 0);
+}
+
+/* **************************************************************** */
+/* */
+/* The Actual Functions */
+/* */
+/* **************************************************************** */
+
+static void
+add_unwind_protect_internal (cleanup, arg)
+ Function *cleanup;
+ char *arg;
+{
+ UNWIND_ELT *elt;
+
+ uwpalloc (elt);
+ elt->head.next = unwind_protect_list;
+ elt->head.cleanup = cleanup;
+ elt->arg.v = arg;
+ unwind_protect_list = elt;
+}
+
+static void
+remove_unwind_protect_internal (ignore1, ignore2)
+ char *ignore1, *ignore2;
+{
+ UNWIND_ELT *elt;
+
+ elt = unwind_protect_list;
+ if (elt)
+ {
+ unwind_protect_list = unwind_protect_list->head.next;
+ uwpfree (elt);
+ }
+}
+
+static void
+run_unwind_protects_internal (ignore1, ignore2)
+ char *ignore1, *ignore2;
+{
+ unwind_frame_run_internal ((char *) NULL, (char *) NULL);
+}
+
+static void
+clear_unwind_protects_internal (flag, ignore)
+ char *flag, *ignore;
+{
+ if (flag)
+ {
+ while (unwind_protect_list)
+ remove_unwind_protect_internal ((char *)NULL, (char *)NULL);
+ }
+ unwind_protect_list = (UNWIND_ELT *)NULL;
+}
+
+static void
+unwind_frame_discard_internal (tag, ignore)
+ char *tag, *ignore;
+{
+ UNWIND_ELT *elt;
+ int found;
+
+ found = 0;
+ while (elt = unwind_protect_list)
+ {
+ unwind_protect_list = unwind_protect_list->head.next;
+ if (elt->head.cleanup == 0 && (STREQ (elt->arg.v, tag)))
+ {
+ uwpfree (elt);
+ found = 1;
+ break;
+ }
+ else
+ uwpfree (elt);
+ }
+
+ if (found == 0)
+ internal_warning ("unwind_frame_discard: %s: frame not found", tag);
+}
+
+/* Restore the value of a variable, based on the contents of SV.
+ sv->desired_setting is a block of memory SIZE bytes long holding the
+ value itself. This block of memory is copied back into the variable. */
+static inline void
+restore_variable (sv)
+ SAVED_VAR *sv;
+{
+ FASTCOPY (sv->desired_setting, sv->variable, sv->size);
+}
+
+static void
+unwind_frame_run_internal (tag, ignore)
+ char *tag, *ignore;
+{
+ UNWIND_ELT *elt;
+ int found;
+
+ found = 0;
+ while (elt = unwind_protect_list)
+ {
+ unwind_protect_list = elt->head.next;
+
+ /* If tag, then compare. */
+ if (elt->head.cleanup == 0)
+ {
+ if (tag && STREQ (elt->arg.v, tag))
+ {
+ uwpfree (elt);
+ found = 1;
+ break;
+ }
+ }
+ else
+ {
+ if (elt->head.cleanup == (Function *) restore_variable)
+ restore_variable (&elt->sv.v);
+ else
+ (*(elt->head.cleanup)) (elt->arg.v);
+ }
+
+ uwpfree (elt);
+ }
+ if (tag && found == 0)
+ internal_warning ("unwind_frame_run: %s: frame not found", tag);
+}
+
+static void
+unwind_protect_mem_internal (var, psize)
+ char *var;
+ char *psize;
+{
+ int size, allocated;
+ UNWIND_ELT *elt;
+
+ size = *(int *) psize;
+ allocated = size + offsetof (UNWIND_ELT, sv.v.desired_setting[0]);
+ elt = (UNWIND_ELT *)xmalloc (allocated);
+ elt->head.next = unwind_protect_list;
+ elt->head.cleanup = (Function *) restore_variable;
+ elt->sv.v.variable = var;
+ elt->sv.v.size = size;
+ FASTCOPY (var, elt->sv.v.desired_setting, size);
+ unwind_protect_list = elt;
+}
+
+/* Save the value of a variable so it will be restored when unwind-protects
+ are run. VAR is a pointer to the variable. SIZE is the size in
+ bytes of VAR. */
+void
+unwind_protect_mem (var, size)
+ char *var;
+ int size;
+{
+ without_interrupts (unwind_protect_mem_internal, var, (char *) &size);
+}
+
+#if defined (DEBUG)
+#include <stdio.h>
+
+void
+print_unwind_protect_tags ()
+{
+ UNWIND_ELT *elt;
+
+ elt = unwind_protect_list;
+ while (elt)
+ {
+ unwind_protect_list = unwind_protect_list->head.next;
+ if (elt->head.cleanup == 0)
+ fprintf(stderr, "tag: %s\n", elt->arg.v);
+ elt = unwind_protect_list;
+ }
+}
+#endif