mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-07-04 02:40:49 +02:00
commit bash-20130222 snapshot
This commit is contained in:
+25
@@ -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
|
||||
|
||||
+36
-1
@@ -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)
|
||||
|
||||
+45
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))))
|
||||
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
|
||||
@@ -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_ */
|
||||
+1
-1
@@ -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. */
|
||||
|
||||
+357
@@ -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
|
||||
Reference in New Issue
Block a user