pointer when encountering an invalid wide character by using our
own mbstate_t and reinitializing it on EILSEQ.
Report and patch from Grisha Levit <grishalevit@gmail.com>
+
+ 9/17
+ ----
+builtins/read.def
+ - read_builtin: make sure i is >= 0 after a timeout longjmp before
+ trying to terminate input_string
+ From a report from Duncan Roe <duncan_roe@optusnet.com.au>
+
+jobs.c,jobs.h
+ - wait_for_background_pids: now takes a new first argument, WFLAGS.
+ Used by wait builtin to pass through JWAIT_FORCE
+
+builtins/wait.def
+ - wait_builtin: if wait doesn't have any pid or job arguments, make
+ sure to pass JWAIT_FORCE through to wait_for_background_pids so
+ we make sure to wait for all background jobs to terminate
+ Inspired by report from Robert Elz <kre@munnari.oz.au>
+
+doc/bash.1,doc/bashref.texi
+ - wait: update description of -f to include wait with no arguments
+
+ 9/18
+ ----
+execute_cmd.c
+ - execute_function: save and restore the_printed_command_except_trap
+ (reflected in $BASH_COMMAND) around shell function calls
+ From a report by Mike Jonkmans <bashbug@jonkmans.nl>
tests/trap7.sub f
tests/trap8.sub f
tests/trap9.sub f
+tests/trap10.sub f
tests/type.tests f
tests/type.right f
tests/type1.sub f
so we have to save input_string temporarily, run the unwind-
protects, then restore input_string so we can use it later */
orig_input_string = 0;
- input_string[i] = '\0'; /* make sure it's terminated */
+ if (i >= 0)
+ input_string[i] = '\0'; /* make sure it's terminated */
if (i == 0)
{
t = (char *)xmalloc (1);
currently active background processes. */
if (list == 0)
{
- opt = wait_for_background_pids (&pstat);
+ opt = wait_for_background_pids (wflags, &pstat);
#if 0
/* Compatibility with NetBSD sh: don't set VNAME since it doesn't
correspond to the return status. */
#define CMD_LASTPIPE 0x2000
#define CMD_STDPATH 0x4000 /* use standard path for command lookup */
#define CMD_TRY_OPTIMIZING 0x8000 /* try to optimize this simple command */
+#define CMD_WANT_ERR_TRAP 0x10000
/* What a command looks like. */
typedef struct command {
Supplying the \fB\-f\fP option, when job control is enabled,
forces \fBwait\fP to wait for each \fIid\fP to terminate before
returning its status, instead of returning when it changes status.
+If there are no \fIid\fP arguments,
+\fBwait\fP waits until all background processes have terminated.
.IP
If none of the \fIid\fPs specify one of the shell's active child
processes, the return status is 127.
Supplying the @option{-f} option, when job control is enabled,
forces @code{wait} to wait for each @var{id} to terminate before
returning its status, instead of returning when it changes status.
+If there are no @var{id} arguments,
+@code{wait} waits until all background processes have terminated.
If none of the @var{id}s specify one of the shell's an active child
processes, the return status is 127.
} \
do { } while (0)
+#define ERROR_TRAP_SET() (signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0)
+
/* A sort of function nesting level counter */
int funcnest = 0;
int funcnest_max = 0;
int
execute_command_internal (COMMAND *command, int asynchronous, int pipe_in, int pipe_out, struct fd_bitmap *fds_to_close)
{
- int exec_result, user_subshell, invert, ignore_return, was_error_trap, fork_flags;
+ int exec_result, user_subshell, invert, ignore_return, fork_flags;
+ int was_error_trap, want_to_run_error_trap;
REDIRECT *my_undo_list, *exec_undo_list;
char *tcmd;
volatile int save_line_number;
if (asynchronous == 0)
{
- was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
+ was_error_trap = ERROR_TRAP_SET ();
invert = (command->flags & CMD_INVERT_RETURN) != 0;
ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
/* Handle WHILE FOR CASE etc. with redirections. (Also '&' input
redirection.) */
- was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
+ was_error_trap = ERROR_TRAP_SET ();
ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
if (do_redirections (command->redirects, RX_ACTIVE|RX_UNDOABLE) != 0)
#if defined (RECYCLES_PIDS)
last_made_pid = NO_PID;
#endif
- was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
+ was_error_trap = ERROR_TRAP_SET ();
+ want_to_run_error_trap = ignore_return == 0 && invert == 0 &&
+ pipe_in == NO_PIPE && pipe_out == NO_PIPE &&
+ (command->value.Simple->flags & CMD_COMMAND_BUILTIN) == 0;
if ((ignore_return || invert) && command->value.Simple)
command->value.Simple->flags |= CMD_IGNORE_RETURN;
if (command->flags & CMD_STDIN_REDIR)
command->value.Simple->flags |= CMD_STDIN_REDIR;
+ if (want_to_run_error_trap)
+ command->value.Simple->flags |= CMD_WANT_ERR_TRAP;
+
begin_unwind_frame ("simple_lineno");
add_unwind_protect (uw_restore_lineno, (void *) (intptr_t) save_line_number);
trap if the command run by the `command' builtin fails; we want to
defer that until the command builtin itself returns failure. */
/* 2020/07/14 -- this changes with how the command builtin is handled */
- if (was_error_trap && ignore_return == 0 && invert == 0 &&
- pipe_in == NO_PIPE && pipe_out == NO_PIPE &&
- (command->value.Simple->flags & CMD_COMMAND_BUILTIN) == 0 &&
- exec_result != EXECUTION_SUCCESS)
+ /* XXX - what happens if a function is called that sets the ERR trap
+ then returns a non-zero exit status? Have to check here using
+ ERROR_TRAP_SET() instead of relying on was_error_trap */
+ if (was_error_trap && want_to_run_error_trap && exec_result != EXECUTION_SUCCESS)
{
last_command_exit_value = exec_result;
line_number = line_number_for_err_trap;
case cm_cond:
#endif
case cm_function_def:
- was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
+ was_error_trap = ERROR_TRAP_SET ();
#if defined (DPAREN_ARITHMETIC)
if (ignore_return && command->type == cm_arith)
command->value.Arith->flags |= CMD_IGNORE_RETURN;
break;
case '|':
- was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
+ was_error_trap = ERROR_TRAP_SET ();
SET_LINE_NUMBER (line_number); /* XXX - save value? */
exec_result = execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close);
}
#endif
+static void
+restore_bash_command (void *oldcmd)
+{
+ if (the_printed_command_except_trap != (char *)oldcmd)
+ {
+ FREE (the_printed_command_except_trap);
+ the_printed_command_except_trap = oldcmd;
+ }
+}
+
static void
function_misc_cleanup (void)
{
int return_val, result, lineno;
COMMAND *tc, *fc, *save_current;
char *debug_trap, *error_trap, *return_trap;
+#if 0
+ int have_error_trap, want_to_run_error_trap;
+#endif
#if defined (ARRAY_VARS)
SHELL_VAR *funcname_v, *bash_source_v, *bash_lineno_v;
ARRAY *funcname_a;
if (tc && (flags & CMD_IGNORE_RETURN))
tc->flags |= CMD_IGNORE_RETURN;
+#if 0
+ have_error_trap = ERROR_TRAP_SET () && error_trace_mode;
+ want_to_run_error_trap = flags & CMD_WANT_ERR_TRAP;
+#endif
+
/* A limited attempt at optimization: shell functions at the end of command
substitutions that are already marked NO_FORK. */
if (tc && (flags & CMD_NO_FORK) && (subshell_environment & SUBSHELL_COMSUB))
unwind_protect_pointer (this_shell_function);
unwind_protect_int (funcnest);
unwind_protect_int (loop_level);
+ add_unwind_protect (restore_bash_command, savestring (the_printed_command_except_trap));
}
else
push_context (var->name, subshell, temporary_env); /* don't unwind-protect for subshells */
save_current = currently_executing_command;
if (from_return_trap == 0)
run_return_trap ();
+#if 0
+ /* An ERR trap on a return <non-zero> won't be run anywhere else */
+ if (result != EXECUTION_SUCCESS && have_error_trap && want_to_run_error_trap)
+ run_error_trap ();
+#endif
currently_executing_command = save_current;
}
else
if (es == 0)
es = 2; /* strlen ("| ") */
name_padding = LONGEST_SIGNAL_DESC - es;
- if (name_padding <= 0)
- name_padding = 1;
fprintf (stream, "%*s", name_padding, "");
/* Wait for all of the background processes started by this shell to finish. */
int
-wait_for_background_pids (struct procstat *ps)
+wait_for_background_pids (int wflags, struct procstat *ps)
{
register int i, r;
int any_stopped, check_async, njobs;
UNBLOCK_CHILD (oset);
QUIT;
errno = 0; /* XXX */
- r = wait_for_single_pid (pid, JWAIT_PERROR);
+ r = wait_for_single_pid (pid, JWAIT_PERROR|wflags);
if (ps)
{
ps->pid = pid;
extern int process_exit_status (WAIT);
extern int wait_for_single_pid (pid_t, int);
-extern int wait_for_background_pids (struct procstat *);
+extern int wait_for_background_pids (int, struct procstat *);
extern int wait_for (pid_t, int);
extern int wait_for_job (int, int, struct procstat *);
extern int wait_for_any_job (int, struct procstat *);
bash: -c: line 1: syntax error near `<'
bash: -c: line 1: `[[ -n < ]]'
ERR: 22: -[[ -n $unset ]]- failed
-ERR: 28: -[[ -z nonempty ]]- failed
+ERR: 28: -func- failed
+ [[ -t X ]]
./cond-xtrace1.sub: line 6: [[: X: integer expected
+ [[ '' > 7 ]]
A
In trap-argument: last command preceding the trap action
In a function call: last command in the trap action
+ERR: 23: -func- failed
+ERR: 20: -[[ -z nonempty ]]- failed
+ERR: 27: -func- failed
+ERR: 35: -func2- failed
+ERR: 39: -func2- failed
caught a child death
caught a child death
caught a child death
trap -- 'echo caught a child death' SIGCHLD
echo caught a child death
-./trap.tests: line 118: trap: cannot specify both -p and -P
-./trap.tests: line 119: trap: -P requires at least one signal name
+./trap.tests: line 121: trap: cannot specify both -p and -P
+./trap.tests: line 122: trap: -P requires at least one signal name
trap: usage: trap [-Plp] [[action] signal_spec ...]
-./trap.tests: line 121: trap: -x: invalid option
+./trap.tests: line 124: trap: -x: invalid option
trap: usage: trap [-Plp] [[action] signal_spec ...]
trap -- 'echo exiting' EXIT
trap -- 'echo aborting' SIGABRT
# return without argument in trap string
${THIS_SH} ./trap9.sub
+# ERR trap and functions
+${THIS_SH} ./trap10.sub
+
#
# show that setting a trap on SIGCHLD is not disastrous.
#
--- /dev/null
+# This program 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.
+#
+# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+trap 'echo ERR: $LINENO: -$BASH_COMMAND- failed' ERR
+
+# the conditional command will fail, then func will fail
+func()
+{
+ [[ -z nonempty ]]
+}
+# func will fail
+func
+
+set -E
+# the conditional command will fail, then func will fail
+func
+set +E
+
+func2()
+{
+ return 1
+}
+# func2 will fail
+func2
+
+# if we change so that `return 1' triffers the ERR trap, this will change
+set -E
+func2
+set +E
command command command false
unset FALSE
-if [ -x /bin/false ]; then
- FALSE=/bin/false
-elif [ -x /usr/bin/false ]; then
- FALSE=/usr/bin/false
-elif [ -x /usr/local/bin/false ]; then
- FALSE=/usr/local/bin/false
-else
+FALSE=$(type -P false)
+if [ -z "$FALSE" ]; then
FALSE='command false'
fi