/* execute_cmd.c -- Execute a COMMAND structure. */
-/* Copyright (C) 1987-2012 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2013 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#include "filecntl.h"
#include "posixstat.h"
#include <signal.h>
-#ifndef _MINIX
+#if defined (HAVE_SYS_PARAM_H)
# include <sys/param.h>
#endif
extern char *glob_argv_flags;
#endif
+extern int job_control; /* XXX */
+
extern int close __P((int));
/* Static functions defined and used in this file. */
was terminated by a signal, and, if so, which one. */
int last_command_exit_signal;
+/* Are we currently ignoring the -e option for the duration of a builtin's
+ execution? */
+int builtin_ignoring_errexit = 0;
+
/* The list of redirections to perform which will undo the redirections
that I made in the shell. */
REDIRECT *redirection_undo_list = (REDIRECT *)NULL;
report the correct line number. Kind of a hack. */
static int showing_function_line;
-static int line_number_for_err_trap;
+/* $LINENO ($BASH_LINENO) for use by an ERR trap. Global so parse_and_execute
+ can save and restore it. */
+int line_number_for_err_trap;
/* A sort of function nesting level counter */
int funcnest = 0;
return currently_executing_command->value.Cond->line;
#endif
#if defined (DPAREN_ARITHMETIC)
- else if (currently_executing_command->type == cm_arith)
+ if (currently_executing_command->type == cm_arith)
return currently_executing_command->value.Arith->line;
#endif
#if defined (ARITH_FOR_COMMAND)
- else if (currently_executing_command->type == cm_arith_for)
+ if (currently_executing_command->type == cm_arith_for)
return currently_executing_command->value.ArithFor->line;
#endif
{
int exec_result, user_subshell, invert, ignore_return, was_error_trap;
REDIRECT *my_undo_list, *exec_undo_list;
+ char *tcmd;
volatile int last_pid;
volatile int save_line_number;
#if defined (PROCESS_SUBSTITUTION)
volatile char *ofifo_list;
#endif
-#if 0
- if (command == 0 || breaking || continuing || read_but_dont_execute)
- return (EXECUTION_SUCCESS);
-#else
if (breaking || continuing)
return (last_command_exit_value);
if (command == 0 || read_but_dont_execute)
return (EXECUTION_SUCCESS);
-#endif
QUIT;
run_pending_traps ();
(pipe_out != NO_PIPE || pipe_in != NO_PIPE || asynchronous)))
{
pid_t paren_pid;
+ int s;
/* Fork a subshell, turn off the subshell bit, turn off job
control and call execute_command () on the command again. */
line_number_for_err_trap = line_number;
- paren_pid = make_child (savestring (make_command_string (command)),
- asynchronous);
+ tcmd = make_command_string (command);
+ paren_pid = make_child (savestring (tcmd), asynchronous);
+
+ if (user_subshell && signal_is_trapped (ERROR_TRAP) &&
+ signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
+ {
+ FREE (the_printed_command_except_trap);
+ the_printed_command_except_trap = savestring (the_printed_command);
+ }
+
if (paren_pid == 0)
- exit (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
- /* NOTREACHED */
+ {
+ /* We want to run the exit trap for forced {} subshells, and we
+ want to note this before execute_in_subshell modifies the
+ COMMAND struct. Need to keep in mind that execute_in_subshell
+ runs the exit trap for () subshells itself. */
+ /* This handles { command; } & */
+ s = user_subshell == 0 && command->type == cm_group && pipe_in == NO_PIPE && pipe_out == NO_PIPE && asynchronous;
+ /* run exit trap for : | { ...; } and { ...; } | : */
+ /* run exit trap for : | ( ...; ) and ( ...; ) | : */
+ s += user_subshell == 0 && command->type == cm_group && (pipe_in != NO_PIPE || pipe_out != NO_PIPE) && asynchronous == 0;
+
+ last_command_exit_value = execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close);
+ if (s)
+ subshell_exit (last_command_exit_value);
+ else
+ exit (last_command_exit_value);
+ /* NOTREACHED */
+ }
else
{
close_pipes (pipe_in, pipe_out);
if (variable_context != 0)
{
ofifo = num_fifos ();
- ofifo_list = copy_fifo_list (&osize);
+ ofifo_list = copy_fifo_list ((int *)&osize);
saved_fifo = 1;
}
else
dispose_exec_redirects ();
#if defined (PROCESS_SUBSTITUTION)
if (saved_fifo)
- free (ofifo_list);
+ free ((void *)ofifo_list);
#endif
return (last_command_exit_value = EXECUTION_FAILURE);
}
the child. */
/* XXX - this is something to watch out for if there are problems
- when the shell is compiled without job control. */
- if (already_making_children && pipe_out == NO_PIPE &&
- last_made_pid != last_pid)
+ when the shell is compiled without job control. Don't worry about
+ whether or not last_made_pid == last_pid; already_making_children
+ tells us whether or not there are unwaited-for children to wait
+ for and reap. */
+ if (already_making_children && pipe_out == NO_PIPE)
{
stop_pipeline (asynchronous, (COMMAND *)NULL);
{
nfifo = num_fifos ();
if (nfifo > ofifo)
- close_new_fifos (ofifo_list, osize);
- free (ofifo_list);
+ close_new_fifos ((char *)ofifo_list, osize);
+ free ((void *)ofifo_list);
}
#endif
if (user_subshell)
{
stdin_redir = stdin_redirects (command->redirects);
- restore_default_signal (0);
+ restore_default_signal (EXIT_TRAP);
}
/* If this is an asynchronous command (command &), we want to
invert = (tcom->flags & CMD_INVERT_RETURN) != 0;
tcom->flags &= ~CMD_INVERT_RETURN;
- result = setjmp (top_level);
+ result = setjmp_nosigs (top_level);
/* If we're inside a function while executing this subshell, we
need to handle a possible `return'. */
function_value = 0;
if (return_catch_flag)
- function_value = setjmp (return_catch);
+ function_value = setjmp_nosigs (return_catch);
/* If we're going to exit the shell, we don't want to invert the return
status. */
struct cpelement *head;
struct cpelement *tail;
int ncoproc;
+ int lock;
}
cplist_t;
static void coproc_free __P((struct coproc *));
/* Will go away when there is fully-implemented support for multiple coprocs. */
-Coproc sh_coproc = { 0, NO_PID, -1, -1, 0, 0, 0, 0 };
+Coproc sh_coproc = { 0, NO_PID, -1, -1, 0, 0, 0, 0, 0 };
cplist_t coproc_list = {0, 0, 0};
cp->c_pid = NO_PID;
cp->c_rfd = cp->c_wfd = -1;
cp->c_rsave = cp->c_wsave = -1;
- cp->c_flags = cp->c_status = 0;
+ cp->c_flags = cp->c_status = cp->c_lock = 0;
}
struct coproc *
cp = &sh_coproc;
#endif
coproc_init (cp);
+ cp->c_lock = 2;
- cp->c_name = savestring (name);
cp->c_pid = pid;
-
+ cp->c_name = savestring (name);
#if MULTIPLE_COPROCS
cpl_add (cp);
#endif
-
+ cp->c_lock = 0;
return (cp);
}
coproc_dispose (cp)
struct coproc *cp;
{
+ sigset_t set, oset;
+
if (cp == 0)
return;
+ BLOCK_SIGNAL (SIGCHLD, set, oset);
+ cp->c_lock = 3;
coproc_unsetvars (cp);
FREE (cp->c_name);
coproc_close (cp);
coproc_free (cp);
#else
coproc_init (cp);
+ cp->c_lock = 0;
#endif
+ UNBLOCK_SIGNAL (oset);
}
/* Placeholder for now. Will require changes for multiple coprocs */
#endif
if (cp)
{
-#if 0
- itrace("coproc_pidchk: pid %d has died", pid);
-#endif
+ cp->c_lock = 4;
cp->c_status = status;
cp->c_flags |= COPROC_DEAD;
cp->c_flags &= ~COPROC_RUNNING;
-#if MULTIPLE_COPROCS
- coproc_dispose (cp);
-#else
- coproc_unsetvars (cp);
-#endif
+ /* Don't dispose the coproc or unset the COPROC_XXX variables because
+ this is executed in a signal handler context. Wait until coproc_reap
+ takes care of it. */
+ cp->c_lock = 0;
}
}
int pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
- int rpipe[2], wpipe[2], estat;
+ int rpipe[2], wpipe[2], estat, invert;
pid_t coproc_pid;
Coproc *cp;
char *tcmd;
+ sigset_t set, oset;
/* XXX -- can be removed after changes to handle multiple coprocs */
#if !MULTIPLE_COPROCS
coproc_init (&sh_coproc);
#endif
+ invert = (command->flags & CMD_INVERT_RETURN) != 0;
command_string_index = 0;
tcmd = make_command_string (command);
sh_openpipe ((int *)&rpipe); /* 0 = parent read, 1 = child write */
sh_openpipe ((int *)&wpipe); /* 0 = child read, 1 = parent write */
+ BLOCK_SIGNAL (SIGCHLD, set, oset);
+
coproc_pid = make_child (savestring (tcmd), 1);
+
if (coproc_pid == 0)
{
close (rpipe[0]);
close (wpipe[1]);
+ UNBLOCK_SIGNAL (oset);
estat = execute_in_subshell (command, 1, wpipe[0], rpipe[1], fds_to_close);
fflush (stdout);
close (rpipe[1]);
close (wpipe[0]);
+ /* XXX - possibly run Coproc->name through word expansion? */
cp = coproc_alloc (command->value.Coproc->name, coproc_pid);
cp->c_rfd = rpipe[0];
cp->c_wfd = wpipe[1];
coproc_setvars (cp);
+ UNBLOCK_SIGNAL (oset);
+
#if 0
itrace ("execute_coproc: [%d] %s", coproc_pid, the_printed_command);
#endif
DESCRIBE_PID (coproc_pid);
run_pending_traps ();
- return (EXECUTION_SUCCESS);
+ return (invert ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}
#endif
if (ignore_return && cmd)
cmd->flags |= CMD_IGNORE_RETURN;
-#if defined (JOB_CONTROL)
lastpipe_flag = 0;
+
begin_unwind_frame ("lastpipe-exec");
lstdin = -1;
/* If the `lastpipe' option is set with shopt, and job control is not
}
if (prev >= 0)
add_unwind_protect (close, prev);
-#endif
exec_result = execute_command_internal (cmd, asynchronous, prev, pipe_out, fds_to_close);
-#if defined (JOB_CONTROL)
if (lstdin > 0)
restore_stdin (lstdin);
-#endif
if (prev >= 0)
close (prev);
unfreeze_jobs_list ();
}
-#if defined (JOB_CONTROL)
discard_unwind_frame ("lastpipe-exec");
-#endif
return (exec_result);
}
/* Save this command unless it's a trap command and we're not running
a debug trap. */
-#if 0
- if (signal_in_progress (DEBUG_TRAP) == 0 && (this_command_name == 0 || (STREQ (this_command_name, "trap") == 0)))
-#else
if (signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
-#endif
{
FREE (the_printed_command_except_trap);
the_printed_command_except_trap = savestring (the_printed_command);
#endif
this_command_name = (char *)NULL;
- v = bind_variable (identifier, list->word->word, 0);
+ /* XXX - special ksh93 for command index variable handling */
+ v = find_variable_last_nameref (identifier);
+ if (v && nameref_p (v))
+ {
+ v = bind_variable_value (v, list->word->word, 0);
+ }
+ else
+ v = bind_variable (identifier, list->word->word, 0);
if (readonly_p (v) || noassign_p (v))
{
line_number = save_line_number;
{
#if defined (HANDLE_MULTIBYTE)
wchar_t *wcstr;
- size_t wclen, slen;
+ size_t slen;
+ int wclen;
wcstr = 0;
slen = mbstowcs (wcstr, s, 0);
mbstowcs (wcstr, s, slen + 1);
wclen = wcswidth (wcstr, slen);
free (wcstr);
- return ((int)wclen);
+ return (wclen < 0 ? STRLEN(s) : wclen);
#else
return (STRLEN (s));
#endif
{
close_pipes (pipe_in, pipe_out);
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
- unlink_fifo_list ();
+ if (pipe_out == NO_PIPE)
+ unlink_fifo_list ();
#endif
return (EXECUTION_SUCCESS);
}
{
WORD_LIST *w, *wcmd;
struct builtin *b;
- int assoc, global;
+ int assoc, global, array;
if (words == 0)
return;
b = 0;
- assoc = global = 0;
+ assoc = global = array = 0;
+ /* Skip over assignment statements preceding a command name */
wcmd = words;
- for (w = words; w; w = w->next)
+ for (wcmd = words; wcmd; wcmd = wcmd->next)
+ if ((wcmd->word->flags & W_ASSIGNMENT) == 0)
+ break;
+
+ for (w = wcmd; w; w = w->next)
if (w->word->flags & W_ASSIGNMENT)
{
if (b == 0)
#if defined (ARRAY_VARS)
if (assoc)
w->word->flags |= W_ASSIGNASSOC;
+ if (array)
+ w->word->flags |= W_ASSIGNARRAY;
+#endif
if (global)
w->word->flags |= W_ASSNGLOBAL;
-#endif
}
#if defined (ARRAY_VARS)
/* Note that we saw an associative array option to a builtin that takes
assignment statements. This is a bit of a kludge. */
- else if (w->word->word[0] == '-' && (strchr (w->word->word+1, 'A') || strchr (w->word->word+1, 'g')))
+ else if (w->word->word[0] == '-' && (strchr (w->word->word+1, 'A') || strchr (w->word->word+1, 'a') || strchr (w->word->word+1, 'g')))
#else
else if (w->word->word[0] == '-' && strchr (w->word->word+1, 'g'))
#endif
}
if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'A'))
assoc = 1;
+ else if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'a'))
+ array = 1;
if ((wcmd->word->flags & W_ASSNBLTIN) && strchr (w->word->word+1, 'g'))
global = 1;
}
#endif
last_asynchronous_pid = old_last_async_pid;
+
+ CHECK_SIGTERM;
}
else
{
result = last_command_exit_value;
close_pipes (pipe_in, pipe_out);
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
- unlink_fifo_list ();
+ /* Close /dev/fd file descriptors in the parent after forking the
+ last child in a (possibly one-element) pipeline. */
+ if (pipe_out == NO_PIPE) /* XXX */
+ unlink_fifo_list (); /* XXX */
#endif
command_line = (char *)NULL; /* don't free this. */
bind_lastarg ((char *)NULL);
last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level (ERREXIT);
}
+ tempenv_assign_error = 0; /* don't care about this any more */
add_unwind_protect (dispose_words, words);
QUIT;
}
if (command_line == 0)
- command_line = savestring (the_printed_command_except_trap);
+ command_line = savestring (the_printed_command_except_trap ? the_printed_command_except_trap : "");
#if defined (PROCESS_SUBSTITUTION)
if ((subshell_environment & SUBSHELL_COMSUB) && (simple_command->flags & CMD_NO_FORK) && fifos_pending() > 0)
error_trap = 0;
old_e_flag = exit_immediately_on_error;
+
/* The eval builtin calls parse_and_execute, which does not know about
the setting of flags, and always calls the execution functions with
flags that will exit the shell on an error if -e is set. If the
{
begin_unwind_frame ("eval_builtin");
unwind_protect_int (exit_immediately_on_error);
+ unwind_protect_int (builtin_ignoring_errexit);
error_trap = TRAP_STRING (ERROR_TRAP);
if (error_trap)
{
restore_default_signal (ERROR_TRAP);
}
exit_immediately_on_error = 0;
+ builtin_ignoring_errexit = 1;
eval_unwind = 1;
}
else
/* The temporary environment for a builtin is supposed to apply to
all commands executed by that builtin. Currently, this is a
- problem only with the `unset', `source' and `eval' builtins. */
-
- isbltinenv = (builtin == source_builtin || builtin == eval_builtin || builtin == unset_builtin);
+ problem only with the `unset', `source' and `eval' builtins.
+ `mapfile' is a special case because it uses evalstring (same as
+ eval or source) to run its callbacks. */
+ isbltinenv = (builtin == source_builtin || builtin == eval_builtin || builtin == unset_builtin || builtin == mapfile_builtin);
if (isbltinenv)
{
if (eval_unwind)
{
- exit_immediately_on_error += old_e_flag;
+ exit_immediately_on_error = errexit_flag;
+ builtin_ignoring_errexit = 0;
if (error_trap)
{
set_error_trap (error_trap);
fc = tc;
return_catch_flag++;
- return_val = setjmp (return_catch);
+ return_val = setjmp_nosigs (return_catch);
if (return_val)
{
{
/* Give builtins a place to jump back to on failure,
so we don't go back up to main(). */
- result = setjmp (top_level);
+ result = setjmp_nosigs (top_level);
/* Give the return builtin a place to jump to when executed in a subshell
or pipeline */
funcvalue = 0;
if (return_catch_flag && builtin == return_builtin)
- funcvalue = setjmp (return_catch);
+ funcvalue = setjmp_nosigs (return_catch);
if (result == EXITPROG)
exit (last_command_exit_value);
{
int old_interactive;
-#if 0
- /* This has been disabled for the time being. */
-#if !defined (ARG_MAX) || ARG_MAX >= 10240
- if (posixly_correct == 0)
- put_gnu_argv_flags_into_env ((long)getpid (), glob_argv_flags);
-#endif
-#endif
-
reset_terminating_signals (); /* XXX */
/* Cancel traps, in trap.c. */
restore_original_signals ();
+ CHECK_SIGTERM;
+
/* restore_original_signals may have undone the work done
by make_child to ensure that SIGINT and SIGQUIT are ignored
in asynchronous children. */
exit (EX_NOTFOUND); /* Posix.2 says the exit status is 127 */
}
+#if defined (JOB_CONTROL)
+ /* May need to reinitialize more of the job control state here. */
+ kill_current_pipeline ();
+#endif
+
wl = make_word_list (make_word (NOTFOUND_HOOK), words);
exit (execute_shell_function (hookf, wl));
}
+ CHECK_SIGTERM;
+
/* Execve expects the command name to be in args[0]. So we
leave it there, in the same format that the user used to
type it in. */