-/* execute_command.c -- Execute a COMMAND structure. */
+/* execute_cmd.c -- Execute a COMMAND structure. */
-/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2018 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 2, or (at your option)
- any later version.
+ 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.
+ 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; see the file COPYING. If not, write to the Free
- Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
#include "config.h"
#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
#include "filecntl.h"
#include "posixstat.h"
#include <signal.h>
-#ifndef _MINIX
+#if defined (HAVE_SYS_PARAM_H)
# include <sys/param.h>
#endif
extern int errno;
#endif
+#define NEED_FPURGE_DECL
+#define NEED_SH_SETLINEBUF_DECL
+
#include "bashansi.h"
#include "bashintl.h"
#include "memalloc.h"
#include "shell.h"
#include <y.tab.h> /* use <...> so we pick it up from the build directory */
+#include "parser.h"
#include "flags.h"
#include "builtins.h"
#include "hashlib.h"
#include "builtins/common.h"
#include "builtins/builtext.h" /* list of builtins */
+#include "builtins/getopt.h"
+
#include <glob/strmatch.h>
#include <tilde/tilde.h>
# include "bashhist.h"
#endif
-extern int posixly_correct;
-extern int breaking, continuing, loop_level;
-extern int expand_aliases;
-extern int parse_and_execute_level, running_trap;
-extern int command_string_index, line_number;
-extern int dot_found_in_search;
-extern int already_making_children;
-extern int tempenv_assign_error;
-extern char *the_printed_command, *shell_name;
-extern pid_t last_command_subst_pid;
-extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
-extern char **subshell_argv, **subshell_envp;
-extern int subshell_argc;
+#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
+# include <mbstr.h> /* mbschr */
+#endif
+
+extern int command_string_index;
+extern char *the_printed_command;
+extern time_t shell_start_time;
#if 0
extern char *glob_argv_flags;
#endif
static int execute_for_command __P((FOR_COM *));
#if defined (SELECT_COMMAND)
+static int displen __P((const char *));
static int print_index_and_element __P((int, int, WORD_LIST *));
static void indent __P((int, int));
static void print_select_list __P((WORD_LIST *, int, int, int));
int, int, int,
struct fd_bitmap *,
int));
-static void execute_disk_command __P((WORD_LIST *, REDIRECT *, char *,
+static int execute_disk_command __P((WORD_LIST *, REDIRECT *, char *,
int, int, int, struct fd_bitmap *, int));
static char *getinterp __P((char *, int, int *));
static void initialize_subshell __P((void));
static int execute_in_subshell __P((COMMAND *, int, int, int, struct fd_bitmap *));
+#if defined (COPROCESS_SUPPORT)
+static void coproc_setstatus __P((struct coproc *, int));
+static int execute_coproc __P((COMMAND *, int, int, struct fd_bitmap *));
+#endif
static int execute_pipeline __P((COMMAND *, int, int, int, struct fd_bitmap *));
static int execute_connection __P((COMMAND *, int, int, int, struct fd_bitmap *));
-static int execute_intern_function __P((WORD_DESC *, COMMAND *));
-
-/* The line number that the currently executing function starts on. */
-static int function_line_number;
+static int execute_intern_function __P((WORD_DESC *, FUNCTION_DEF *));
/* Set to 1 if fd 0 was the subject of redirection to a subshell. Global
so that reader_loop can set it to zero before executing a command. */
a debugger to know where exactly the program is currently executing. */
char *the_printed_command_except_trap;
-static COMMAND *currently_executing_command;
-
-struct stat SB; /* used for debugging */
-
-static int special_builtin_failed;
-
-/* XXX - set to 1 if we're running the DEBUG trap and we want to show the line
- number containing the function name. Used by executing_line_number to
- report the correct line number. Kind of a hack. */
-static int showing_function_line;
-
/* For catching RETURN in a function. */
int return_catch_flag;
int return_catch_value;
procenv_t return_catch;
/* The value returned by the last synchronous command. */
-int last_command_exit_value;
+volatile int last_command_exit_value;
/* Whether or not the last command (corresponding to last_command_exit_value)
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;
that must be undone even when exec discards redirection_undo_list. */
REDIRECT *exec_redirection_undo_list = (REDIRECT *)NULL;
+/* When greater than zero, value is the `level' of builtins we are
+ currently executing (e.g. `eval echo a' would have it set to 2). */
+int executing_builtin = 0;
+
+/* Non-zero if we are executing a command list (a;b;c, etc.) */
+int executing_list = 0;
+
+/* Non-zero if failing commands in a command substitution should not exit the
+ shell even if -e is set. Used to pass the CMD_IGNORE_RETURN flag down to
+ commands run in command substitutions by parse_and_execute. */
+int comsub_ignore_return = 0;
+
/* Non-zero if we have just forked and are currently running in a subshell
environment. */
int subshell_environment;
/* If non-zero, matches in case and [[ ... ]] are case-insensitive */
int match_ignore_case = 0;
+int executing_command_builtin = 0;
+
+struct stat SB; /* used for debugging */
+
+static int special_builtin_failed;
+
+static COMMAND *currently_executing_command;
+
+/* The line number that the currently executing function starts on. */
+static int function_line_number;
+
+/* XXX - set to 1 if we're running the DEBUG trap and we want to show the line
+ number containing the function name. Used by executing_line_number to
+ report the correct line number. Kind of a hack. */
+static int showing_function_line;
+
+static int connection_count;
+
+/* $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;
+int funcnest_max = 0;
+
+int evalnest = 0;
+int evalnest_max = EVALNEST_MAX;
+
+int sourcenest = 0;
+int sourcenest_max = SOURCENEST_MAX;
+
+volatile int from_return_trap = 0;
+
+int lastpipe_opt = 0;
+
struct fd_bitmap *current_fds_to_close = (struct fd_bitmap *)NULL;
#define FD_BITMAP_DEFAULT_SIZE 32
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
#if defined (PROCESS_SUBSTITUTION)
/* don't unlink fifos if we're in a shell function; wait until the function
returns. */
- if (variable_context == 0)
+ if (variable_context == 0 && executing_list == 0)
unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */
+ QUIT;
return (result);
}
dispose_redirects (list);
}
+void
+undo_partial_redirects ()
+{
+ if (redirection_undo_list)
+ {
+ cleanup_redirects (redirection_undo_list);
+ redirection_undo_list = (REDIRECT *)NULL;
+ }
+}
+
#if 0
/* Function to unwind_protect the redirections for functions and builtins. */
static void
}
}
+void
+dispose_partial_redirects ()
+{
+ if (redirection_undo_list)
+ {
+ dispose_redirects (redirection_undo_list);
+ redirection_undo_list = (REDIRECT *)NULL;
+ }
+}
+
#if defined (JOB_CONTROL)
/* A function to restore the signal mask to its proper value when the shell
is interrupted or errors occur while creating a pipeline. */
#define DESCRIBE_PID(pid) do { if (interactive) describe_pid (pid); } while (0)
-/* Execute the command passed in COMMAND, perhaps doing it asynchrounously.
+/* Execute the command passed in COMMAND, perhaps doing it asynchronously.
COMMAND is exactly what read_command () places into GLOBAL_COMMAND.
ASYNCHROUNOUS, if non-zero, says to do this command in the background.
PIPE_IN and PIPE_OUT are file descriptors saying where input comes
int pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
- int exec_result, invert, ignore_return, was_error_trap;
+ int exec_result, user_subshell, invert, ignore_return, was_error_trap;
REDIRECT *my_undo_list, *exec_undo_list;
- volatile int last_pid;
+ char *tcmd;
volatile int save_line_number;
+#if defined (PROCESS_SUBSTITUTION)
+ volatile int ofifo, nfifo, osize, saved_fifo;
+ volatile char *ofifo_list;
+#endif
- if (command == 0 || breaking || continuing || read_but_dont_execute)
+ if (breaking || continuing)
+ return (last_command_exit_value);
+ if (command == 0 || read_but_dont_execute)
return (EXECUTION_SUCCESS);
+ QUIT;
run_pending_traps ();
#if 0
if (command->type == cm_subshell && (command->flags & CMD_NO_FORK))
return (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
+#if defined (COPROCESS_SUPPORT)
+ if (command->type == cm_coproc)
+ return (last_command_exit_value = execute_coproc (command, pipe_in, pipe_out, fds_to_close));
+#endif
+
+ user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0);
+
+#if defined (TIME_BEFORE_SUBSHELL)
+ if ((command->flags & CMD_TIME_PIPELINE) && user_subshell && asynchronous == 0)
+ {
+ command->flags |= CMD_FORCE_SUBSHELL;
+ exec_result = time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close);
+ currently_executing_command = (COMMAND *)NULL;
+ return (exec_result);
+ }
+#endif
+
if (command->type == cm_subshell ||
(command->flags & (CMD_WANT_SUBSHELL|CMD_FORCE_SUBSHELL)) ||
(shell_control_structure (command->type) &&
(pipe_out != NO_PIPE || pipe_in != NO_PIPE || asynchronous)))
{
pid_t paren_pid;
+ int s;
+ char *p;
/* Fork a subshell, turn off the subshell bit, turn off job
control and call execute_command () on the command again. */
- paren_pid = make_child (savestring (make_command_string (command)),
- asynchronous);
+ if (command->type == cm_subshell)
+ line_number_for_err_trap = line_number = command->value.Subshell->line; /* XXX - save value? */
+ /* Otherwise we defer setting line_number */
+ tcmd = make_command_string (command);
+ paren_pid = make_child (p = 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 */
+ {
+#if defined (JOB_CONTROL)
+ FREE (p); /* child doesn't use pointer */
+#endif
+ /* 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
+ sh_exit (last_command_exit_value);
+ /* NOTREACHED */
+ }
else
{
close_pipes (pipe_in, pipe_out);
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
- unlink_fifo_list ();
+ if (variable_context == 0) /* wait until shell function completes */
+ unlink_fifo_list ();
#endif
/* If we are part of a pipeline, and not the end of the pipeline,
then we should simply return and let the last command in the
if (asynchronous == 0)
{
- last_command_exit_value = wait_for (paren_pid);
+ was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
+ invert = (command->flags & CMD_INVERT_RETURN) != 0;
+ ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
+
+ exec_result = wait_for (paren_pid);
/* If we have to, invert the return value. */
if (invert)
- exec_result = ((last_command_exit_value == EXECUTION_SUCCESS)
+ exec_result = ((exec_result == EXECUTION_SUCCESS)
? EXECUTION_FAILURE
: EXECUTION_SUCCESS);
- else
- exec_result = last_command_exit_value;
- return (last_command_exit_value = exec_result);
+ last_command_exit_value = exec_result;
+ if (user_subshell && was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
+ {
+ save_line_number = line_number;
+ line_number = line_number_for_err_trap;
+ run_error_trap ();
+ line_number = save_line_number;
+ }
+
+ if (user_subshell && ignore_return == 0 && invert == 0 && exit_immediately_on_error && exec_result != EXECUTION_SUCCESS)
+ {
+ run_pending_traps ();
+ jump_to_top_level (ERREXIT);
+ }
+
+ return (last_command_exit_value);
}
else
{
run_pending_traps ();
+ /* Posix 2013 2.9.3.1: "the exit status of an asynchronous list
+ shall be zero." */
+ last_command_exit_value = 0;
return (EXECUTION_SUCCESS);
}
}
if (shell_control_structure (command->type) && command->redirects)
stdin_redir = stdin_redirects (command->redirects);
+#if defined (PROCESS_SUBSTITUTION)
+# if !defined (HAVE_DEV_FD)
+ reap_procsubs ();
+# endif
+
+ if (variable_context != 0) /* XXX - also if sourcelevel != 0? */
+ {
+ ofifo = num_fifos ();
+ ofifo_list = copy_fifo_list ((int *)&osize);
+ begin_unwind_frame ("internal_fifos");
+ add_unwind_protect (xfree, ofifo_list);
+ saved_fifo = 1;
+ }
+ else
+ saved_fifo = 0;
+#endif
+
/* Handle WHILE FOR CASE etc. with redirections. (Also '&' input
redirection.) */
if (do_redirections (command->redirects, RX_ACTIVE|RX_UNDOABLE) != 0)
{
- cleanup_redirects (redirection_undo_list);
- redirection_undo_list = (REDIRECT *)NULL;
+ undo_partial_redirects ();
dispose_exec_redirects ();
- return (EXECUTION_FAILURE);
+#if defined (PROCESS_SUBSTITUTION)
+ if (saved_fifo)
+ {
+ free ((void *)ofifo_list);
+ discard_unwind_frame ("internal_fifos");
+ }
+#endif
+ return (last_command_exit_value = EXECUTION_FAILURE);
}
+#if 0
if (redirection_undo_list)
{
+ /* XXX - why copy here? */
my_undo_list = (REDIRECT *)copy_redirects (redirection_undo_list);
- dispose_redirects (redirection_undo_list);
- redirection_undo_list = (REDIRECT *)NULL;
+ dispose_partial_redirects ();
}
else
my_undo_list = (REDIRECT *)NULL;
+#else
+ my_undo_list = redirection_undo_list;
+ redirection_undo_list = (REDIRECT *)NULL;
+#endif
+#if 0
if (exec_redirection_undo_list)
{
+ /* XXX - why copy here? */
exec_undo_list = (REDIRECT *)copy_redirects (exec_redirection_undo_list);
- dispose_redirects (exec_redirection_undo_list);
- exec_redirection_undo_list = (REDIRECT *)NULL;
+ dispose_exec_redirects ();
}
else
exec_undo_list = (REDIRECT *)NULL;
+#else
+ exec_undo_list = exec_redirection_undo_list;
+ exec_redirection_undo_list = (REDIRECT *)NULL;
+#endif
if (my_undo_list || exec_undo_list)
begin_unwind_frame ("loop_redirections");
#if defined (RECYCLES_PIDS)
last_made_pid = NO_PID;
#endif
- last_pid = last_made_pid;
was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
if (ignore_return && command->value.Simple)
if (command->flags & CMD_STDIN_REDIR)
command->value.Simple->flags |= CMD_STDIN_REDIR;
- line_number = command->value.Simple->line;
+ line_number_for_err_trap = line_number = command->value.Simple->line;
exec_result =
execute_simple_command (command->value.Simple, pipe_in, pipe_out,
asynchronous, fds_to_close);
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);
if (asynchronous)
{
DESCRIBE_PID (last_made_pid);
+ exec_result = EXECUTION_SUCCESS;
+ invert = 0; /* async commands always succeed */
}
else
#if !defined (JOB_CONTROL)
/* Do not wait for asynchronous processes started from
startup files. */
- if (last_made_pid != last_asynchronous_pid)
+ if (last_made_pid != NO_PID && last_made_pid != last_asynchronous_pid)
+#else
+ if (last_made_pid != NO_PID)
#endif
/* When executing a shell function that executes other
commands, this causes the last simple command in
}
}
- if (was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
+ /* 2009/02/13 -- pipeline failure is processed elsewhere. This handles
+ only the failure of a simple command. We don't want to run the error
+ trap if the command run by the `command' builtin fails; we want to
+ defer that until the command builtin itself returns failure. */
+ 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)
{
last_command_exit_value = exec_result;
+ line_number = line_number_for_err_trap;
run_error_trap ();
+ line_number = save_line_number;
}
if (ignore_return == 0 && invert == 0 &&
((posixly_correct && interactive == 0 && special_builtin_failed) ||
- (exit_immediately_on_error && (exec_result != EXECUTION_SUCCESS))))
+ (exit_immediately_on_error && pipe_in == NO_PIPE && pipe_out == NO_PIPE && exec_result != EXECUTION_SUCCESS)))
{
last_command_exit_value = exec_result;
run_pending_traps ();
+
+ /* Undo redirections before running exit trap on the way out of
+ set -e. Report by Mark Farrell 5/19/2014 */
+ if (exit_immediately_on_error && signal_is_trapped (0) &&
+ unwind_protect_tag_on_stack ("saved-redirects"))
+ run_unwind_frame ("saved-redirects");
+
jump_to_top_level (ERREXIT);
}
case cm_connection:
exec_result = execute_connection (command, asynchronous,
pipe_in, pipe_out, fds_to_close);
+ if (asynchronous)
+ invert = 0; /* XXX */
+
break;
#if defined (DPAREN_ARITHMETIC)
case cm_arith:
+ was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
if (ignore_return)
command->value.Arith->flags |= CMD_IGNORE_RETURN;
+ line_number_for_err_trap = save_line_number = line_number;
exec_result = execute_arith_command (command->value.Arith);
+ line_number = save_line_number;
+
+ if (was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
+ {
+ last_command_exit_value = exec_result;
+ save_line_number = line_number;
+ line_number = line_number_for_err_trap;
+ run_error_trap ();
+ line_number = save_line_number;
+ }
+
+ if (ignore_return == 0 && invert == 0 && exit_immediately_on_error && exec_result != EXECUTION_SUCCESS)
+ {
+ last_command_exit_value = exec_result;
+ run_pending_traps ();
+ jump_to_top_level (ERREXIT);
+ }
+
break;
#endif
#if defined (COND_COMMAND)
case cm_cond:
+ was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
if (ignore_return)
command->value.Cond->flags |= CMD_IGNORE_RETURN;
- save_line_number = line_number;
+
+ line_number_for_err_trap = save_line_number = line_number;
exec_result = execute_cond_command (command->value.Cond);
line_number = save_line_number;
+
+ if (was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
+ {
+ last_command_exit_value = exec_result;
+ save_line_number = line_number;
+ line_number = line_number_for_err_trap;
+ run_error_trap ();
+ line_number = save_line_number;
+ }
+
+ if (ignore_return == 0 && invert == 0 && exit_immediately_on_error && exec_result != EXECUTION_SUCCESS)
+ {
+ last_command_exit_value = exec_result;
+ run_pending_traps ();
+ jump_to_top_level (ERREXIT);
+ }
+
break;
#endif
case cm_function_def:
exec_result = execute_intern_function (command->value.Function_def->name,
- command->value.Function_def->command);
+ command->value.Function_def);
break;
default:
}
if (my_undo_list)
- {
- do_redirections (my_undo_list, RX_ACTIVE);
- dispose_redirects (my_undo_list);
- }
+ cleanup_redirects (my_undo_list);
if (exec_undo_list)
dispose_redirects (exec_undo_list);
if (my_undo_list || exec_undo_list)
discard_unwind_frame ("loop_redirections");
+#if defined (PROCESS_SUBSTITUTION)
+ if (saved_fifo)
+ {
+ nfifo = num_fifos ();
+ if (nfifo > ofifo)
+ close_new_fifos ((char *)ofifo_list, osize);
+ free ((void *)ofifo_list);
+ discard_unwind_frame ("internal_fifos");
+ }
+#endif
+
/* Invert the return value if we have to */
if (invert)
exec_result = (exec_result == EXECUTION_SUCCESS)
? EXECUTION_FAILURE
: EXECUTION_SUCCESS;
+#if defined (DPAREN_ARITHMETIC) || defined (COND_COMMAND)
+ /* This is where we set PIPESTATUS from the exit status of the appropriate
+ compound commands (the ones that look enough like simple commands to
+ cause confusion). We might be able to optimize by not doing this if
+ subshell_environment != 0. */
+ switch (command->type)
+ {
+# if defined (DPAREN_ARITHMETIC)
+ case cm_arith:
+# endif
+# if defined (COND_COMMAND)
+ case cm_cond:
+# endif
+ set_pipestatus_from_exit (exec_result);
+ break;
+ default:
+ break;
+ }
+#endif
+
last_command_exit_value = exec_result;
run_pending_traps ();
#if 0
if (running_trap == 0)
#endif
currently_executing_command = (COMMAND *)NULL;
+
return (last_command_exit_value);
}
#define POSIX_TIMEFORMAT "real %2R\nuser %2U\nsys %2S"
#define BASH_TIMEFORMAT "\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS"
-static int precs[] = { 0, 100, 10, 1 };
+static const int precs[] = { 0, 100, 10, 1 };
/* Expand one `%'-prefixed escape sequence from a time format string. */
static int
and 999. */
if (prec != 0)
{
- buf[ind++] = '.';
+ buf[ind++] = locale_decpoint ();
for (aind = 1; aind <= prec; aind++)
{
buf[ind++] = (sec_fraction / precs[aind]) + '0';
else if (s[1] == 'P')
{
s++;
+#if 0
+ /* clamp CPU usage at 100% */
if (cpu > 10000)
cpu = 10000;
+#endif
sum = cpu / 100;
sum_frac = (cpu % 100) * 10;
len = mkfmt (ts, 2, 0, sum, sum_frac);
int asynchronous, pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
- int rv, posix_time, old_flags;
+ int rv, posix_time, old_flags, nullcmd, code;
time_t rs, us, ss;
int rsf, usf, ssf;
int cpu;
char *time_format;
+ volatile procenv_t save_top_level;
#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
struct timeval real, user, sys;
# endif
#endif
- posix_time = (command->flags & CMD_TIME_POSIX);
+ posix_time = command && (command->flags & CMD_TIME_POSIX);
+
+ nullcmd = (command == 0) || (command->type == cm_simple && command->value.Simple->words == 0 && command->value.Simple->redirects == 0);
+ if (posixly_correct && nullcmd)
+ {
+#if defined (HAVE_GETRUSAGE)
+ selfb.ru_utime.tv_sec = kidsb.ru_utime.tv_sec = selfb.ru_stime.tv_sec = kidsb.ru_stime.tv_sec = 0;
+ selfb.ru_utime.tv_usec = kidsb.ru_utime.tv_usec = selfb.ru_stime.tv_usec = kidsb.ru_stime.tv_usec = 0;
+ before.tv_sec = shell_start_time;
+ before.tv_usec = 0;
+#else
+ before.tms_utime = before.tms_stime = before.tms_cutime = before.tms_cstime = 0;
+ tbefore = shell_start_time;
+#endif
+ }
old_flags = command->flags;
+ COPY_PROCENV (top_level, save_top_level);
command->flags &= ~(CMD_TIME_PIPELINE|CMD_TIME_POSIX);
- rv = execute_command_internal (command, asynchronous, pipe_in, pipe_out, fds_to_close);
- command->flags = old_flags;
+ code = setjmp_nosigs (top_level);
+ if (code == NOT_JUMPED)
+ {
+ rv = execute_command_internal (command, asynchronous, pipe_in, pipe_out, fds_to_close);
+ command->flags = old_flags;
+ }
+ COPY_PROCENV (save_top_level, top_level);
rs = us = ss = 0;
rsf = usf = ssf = cpu = 0;
if (posix_time)
time_format = POSIX_TIMEFORMAT;
else if ((time_format = get_string_value ("TIMEFORMAT")) == 0)
- time_format = BASH_TIMEFORMAT;
+ {
+ if (posixly_correct && nullcmd)
+ time_format = "user\t%2lU\nsys\t%2lS";
+ else
+ time_format = BASH_TIMEFORMAT;
+ }
if (time_format && *time_format)
print_formatted_time (stderr, time_format, rs, rsf, us, usf, ss, ssf, cpu);
+ if (code)
+ sh_longjmp (top_level, code);
+
return rv;
}
#endif /* COMMAND_TIMING */
int pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
- int user_subshell, return_code, function_value, should_redir_stdin, invert;
- int ois;
- COMMAND *tcom;
+ volatile int user_subshell, user_coproc, invert;
+ int return_code, function_value, should_redir_stdin, ois, result;
+ volatile COMMAND *tcom;
USE_VAR(user_subshell);
+ USE_VAR(user_coproc);
USE_VAR(invert);
USE_VAR(tcom);
USE_VAR(asynchronous);
invert = (command->flags & CMD_INVERT_RETURN) != 0;
user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0);
+ user_coproc = command->type == cm_coproc;
command->flags &= ~(CMD_FORCE_SUBSHELL | CMD_WANT_SUBSHELL | CMD_INVERT_RETURN);
the special case of an asynchronous GROUP command where the
the subshell bit is turned on down in case cm_group: below),
turn off `asynchronous', so that two subshells aren't spawned.
+ XXX - asynchronous used to be set to 0 in this block, but that
+ means that setup_async_signals was never run. Now it's set to
+ 0 after subshell_environment is set appropriately and setup_async_signals
+ is run.
This seems semantically correct to me. For example,
( foo ) & seems to say ``do the command `foo' in a subshell
aliases. */
if (ois != interactive_shell)
expand_aliases = 0;
- asynchronous = 0;
}
/* Subshells are neither login nor interactive. */
login_shell = interactive = 0;
- subshell_environment = user_subshell ? SUBSHELL_PAREN : SUBSHELL_ASYNC;
+ /* And we're no longer in a loop. See Posix interp 842 (we are not in the
+ "same execution environment"). */
+ if (shell_compatibility_level > 44)
+ loop_level = 0;
+
+ if (user_subshell)
+ {
+ subshell_environment = SUBSHELL_PAREN; /* XXX */
+ if (asynchronous)
+ subshell_environment |= SUBSHELL_ASYNC;
+ }
+ else
+ {
+ subshell_environment = 0; /* XXX */
+ if (asynchronous)
+ subshell_environment |= SUBSHELL_ASYNC;
+ if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
+ subshell_environment |= SUBSHELL_PIPE;
+ if (user_coproc)
+ subshell_environment |= SUBSHELL_COPROC;
+ }
reset_terminating_signals (); /* in sig.c */
/* Cancel traps, in trap.c. */
- restore_original_signals ();
+ /* Reset the signal handlers in the child, but don't free the
+ trap strings. Set a flag noting that we have to free the
+ trap strings if we run trap to change a signal disposition. */
+ reset_signal_handlers ();
+ subshell_environment |= SUBSHELL_RESETTRAP;
+#if 0 /* TAG:bash-5.1 */
+ /* We are in a subshell, so forget that we are running a trap handler or
+ that the signal handler has changed (we haven't changed it!) */
+ if (running_trap > 0)
+ {
+ run_trap_cleanup (running_trap - 1);
+ running_trap = 0;
+ }
+#endif
+
+ /* Make sure restore_original_signals doesn't undo the work done by
+ make_child to ensure that asynchronous children are immune to SIGINT
+ and SIGQUIT. Turn off asynchronous to make sure more subshells are
+ not spawned. */
if (asynchronous)
- setup_async_signals ();
+ {
+ setup_async_signals ();
+ asynchronous = 0;
+ }
+ else
+ set_sigint_handler ();
#if defined (JOB_CONTROL)
set_sigchld_handler ();
#endif /* JOB_CONTROL */
- set_sigint_handler ();
-
-#if defined (JOB_CONTROL)
/* Delete all traces that there were any jobs running. This is
only for subshells. */
without_job_control ();
-#endif /* JOB_CONTROL */
if (fds_to_close)
close_fd_bitmap (fds_to_close);
do_piping (pipe_in, pipe_out);
+#if defined (COPROCESS_SUPPORT)
+ coproc_closeall ();
+#endif
+
/* If this is a user subshell, set a flag if stdin was redirected.
This is used later to decide whether to redirect fd 0 to
/dev/null for async commands in the subshell. This adds more
- sh compatibility, but I'm not sure it's the right thing to do. */
+ sh compatibility, but I'm not sure it's the right thing to do.
+ Note that an input pipe to a compound command suffices to inhibit
+ the implicit /dev/null redirection for asynchronous commands
+ executed as part of that compound command. */
if (user_subshell)
{
- stdin_redir = stdin_redirects (command->redirects);
- restore_default_signal (0);
+ stdin_redir = stdin_redirects (command->redirects) || pipe_in != NO_PIPE;
+#if 0
+ restore_default_signal (EXIT_TRAP); /* XXX - reset_signal_handlers above */
+#endif
}
+ else if (shell_control_structure (command->type) && pipe_in != NO_PIPE)
+ stdin_redir = 1;
/* If this is an asynchronous command (command &), we want to
redirect the standard input from /dev/null in the absence of
if (should_redir_stdin && stdin_redir == 0)
async_redirect_stdin ();
+#if 0
+ /* XXX - TAG:bash-5.1 */
+ if (user_subshell && command->type == cm_subshell)
+ optimize_subshell_command (command->value.Subshell->command);
+#endif
+
/* Do redirections, then dispose of them before recursive call. */
if (command->redirects)
{
command->redirects = (REDIRECT *)NULL;
}
- tcom = (command->type == cm_subshell) ? command->value.Subshell->command : command;
+ if (command->type == cm_subshell)
+ tcom = command->value.Subshell->command;
+ else if (user_coproc)
+ tcom = command->value.Coproc->command;
+ else
+ tcom = command;
if (command->flags & CMD_TIME_PIPELINE)
tcom->flags |= CMD_TIME_PIPELINE;
This means things like ( sleep 10 ) will only cause one fork.
If we're timing the command or inverting its return value, however,
we cannot do this optimization. */
- if (user_subshell && (tcom->type == cm_simple || tcom->type == cm_subshell) &&
+ if ((user_subshell || user_coproc) && (tcom->type == cm_simple || tcom->type == cm_subshell) &&
((tcom->flags & CMD_TIME_PIPELINE) == 0) &&
((tcom->flags & CMD_INVERT_RETURN) == 0))
{
invert = (tcom->flags & CMD_INVERT_RETURN) != 0;
tcom->flags &= ~CMD_INVERT_RETURN;
+ 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);
-
- if (function_value)
+ function_value = setjmp_nosigs (return_catch);
+
+ /* If we're going to exit the shell, we don't want to invert the return
+ status. */
+ if (result == EXITPROG)
+ invert = 0, return_code = last_command_exit_value;
+ else if (result)
+ return_code = (last_command_exit_value == EXECUTION_SUCCESS) ? EXECUTION_FAILURE : last_command_exit_value;
+ else if (function_value)
return_code = return_catch_value;
else
- return_code = execute_command_internal
- (tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
+ return_code = execute_command_internal ((COMMAND *)tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
/* If we are asked to, invert the return value. */
if (invert)
return_code = (return_code == EXECUTION_SUCCESS) ? EXECUTION_FAILURE
: EXECUTION_SUCCESS;
+
/* If we were explicitly placed in a subshell with (), we need
to do the `shell cleanup' things, such as running traps[0]. */
if (user_subshell && signal_is_trapped (0))
return_code = run_exit_trap ();
}
- subshell_level--;
+#if 0
+ subshell_level--; /* don't bother, caller will just exit */
+#endif
return (return_code);
/* NOTREACHED */
}
-static int
-execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
- COMMAND *command;
- int asynchronous, pipe_in, pipe_out;
- struct fd_bitmap *fds_to_close;
+#if defined (COPROCESS_SUPPORT)
+#define COPROC_MAX 16
+
+typedef struct cpelement
+ {
+ struct cpelement *next;
+ struct coproc *coproc;
+ }
+cpelement_t;
+
+typedef struct cplist
+ {
+ struct cpelement *head;
+ struct cpelement *tail;
+ int ncoproc;
+ int lock;
+ }
+cplist_t;
+
+static struct cpelement *cpe_alloc __P((struct coproc *));
+static void cpe_dispose __P((struct cpelement *));
+static struct cpelement *cpl_add __P((struct coproc *));
+static struct cpelement *cpl_delete __P((pid_t));
+static void cpl_reap __P((void));
+static void cpl_flush __P((void));
+static void cpl_closeall __P((void));
+static struct cpelement *cpl_search __P((pid_t));
+static struct cpelement *cpl_searchbyname __P((const char *));
+static void cpl_prune __P((void));
+
+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, 0 };
+
+cplist_t coproc_list = {0, 0, 0};
+
+/* Functions to manage the list of coprocs */
+
+static struct cpelement *
+cpe_alloc (cp)
+ Coproc *cp;
{
- int prev, fildes[2], new_bitmap_size, dummyfd, ignore_return, exec_result;
- COMMAND *cmd;
- struct fd_bitmap *fd_bitmap;
+ struct cpelement *cpe;
-#if defined (JOB_CONTROL)
- sigset_t set, oset;
- BLOCK_CHILD (set, oset);
-#endif /* JOB_CONTROL */
+ cpe = (struct cpelement *)xmalloc (sizeof (struct cpelement));
+ cpe->coproc = cp;
+ cpe->next = (struct cpelement *)0;
+ return cpe;
+}
- ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
+static void
+cpe_dispose (cpe)
+ struct cpelement *cpe;
+{
+ free (cpe);
+}
- prev = pipe_in;
- cmd = command;
+static struct cpelement *
+cpl_add (cp)
+ Coproc *cp;
+{
+ struct cpelement *cpe;
- while (cmd && cmd->type == cm_connection &&
- cmd->value.Connection && cmd->value.Connection->connector == '|')
+ cpe = cpe_alloc (cp);
+
+ if (coproc_list.head == 0)
{
- /* Make a pipeline between the two commands. */
- if (pipe (fildes) < 0)
- {
- sys_error ("pipe error");
-#if defined (JOB_CONTROL)
+ coproc_list.head = coproc_list.tail = cpe;
+ coproc_list.ncoproc = 0; /* just to make sure */
+ }
+ else
+ {
+ coproc_list.tail->next = cpe;
+ coproc_list.tail = cpe;
+ }
+ coproc_list.ncoproc++;
+
+ return cpe;
+}
+
+static struct cpelement *
+cpl_delete (pid)
+ pid_t pid;
+{
+ struct cpelement *prev, *p;
+
+ for (prev = p = coproc_list.head; p; prev = p, p = p->next)
+ if (p->coproc->c_pid == pid)
+ {
+ prev->next = p->next; /* remove from list */
+ break;
+ }
+
+ if (p == 0)
+ return 0; /* not found */
+
+#if defined (DEBUG)
+ itrace("cpl_delete: deleting %d", pid);
+#endif
+
+ /* Housekeeping in the border cases. */
+ if (p == coproc_list.head)
+ coproc_list.head = coproc_list.head->next;
+ else if (p == coproc_list.tail)
+ coproc_list.tail = prev;
+
+ coproc_list.ncoproc--;
+ if (coproc_list.ncoproc == 0)
+ coproc_list.head = coproc_list.tail = 0;
+ else if (coproc_list.ncoproc == 1)
+ coproc_list.tail = coproc_list.head; /* just to make sure */
+
+ return (p);
+}
+
+static void
+cpl_reap ()
+{
+ struct cpelement *p, *next, *nh, *nt;
+
+ /* Build a new list by removing dead coprocs and fix up the coproc_list
+ pointers when done. */
+ nh = nt = next = (struct cpelement *)0;
+ for (p = coproc_list.head; p; p = next)
+ {
+ next = p->next;
+ if (p->coproc->c_flags & COPROC_DEAD)
+ {
+ coproc_list.ncoproc--; /* keep running count, fix up pointers later */
+
+#if defined (DEBUG)
+ itrace("cpl_reap: deleting %d", p->coproc->c_pid);
+#endif
+
+ coproc_dispose (p->coproc);
+ cpe_dispose (p);
+ }
+ else if (nh == 0)
+ nh = nt = p;
+ else
+ {
+ nt->next = p;
+ nt = nt->next;
+ }
+ }
+
+ if (coproc_list.ncoproc == 0)
+ coproc_list.head = coproc_list.tail = 0;
+ else
+ {
+ if (nt)
+ nt->next = 0;
+ coproc_list.head = nh;
+ coproc_list.tail = nt;
+ if (coproc_list.ncoproc == 1)
+ coproc_list.tail = coproc_list.head; /* just to make sure */
+ }
+}
+
+/* Clear out the list of saved statuses */
+static void
+cpl_flush ()
+{
+ struct cpelement *cpe, *p;
+
+ for (cpe = coproc_list.head; cpe; )
+ {
+ p = cpe;
+ cpe = cpe->next;
+
+ coproc_dispose (p->coproc);
+ cpe_dispose (p);
+ }
+
+ coproc_list.head = coproc_list.tail = 0;
+ coproc_list.ncoproc = 0;
+}
+
+static void
+cpl_closeall ()
+{
+ struct cpelement *cpe;
+
+ for (cpe = coproc_list.head; cpe; cpe = cpe->next)
+ coproc_close (cpe->coproc);
+}
+
+static void
+cpl_fdchk (fd)
+ int fd;
+{
+ struct cpelement *cpe;
+
+ for (cpe = coproc_list.head; cpe; cpe = cpe->next)
+ coproc_checkfd (cpe->coproc, fd);
+}
+
+/* Search for PID in the list of coprocs; return the cpelement struct if
+ found. If not found, return NULL. */
+static struct cpelement *
+cpl_search (pid)
+ pid_t pid;
+{
+ struct cpelement *cpe;
+
+ for (cpe = coproc_list.head ; cpe; cpe = cpe->next)
+ if (cpe->coproc->c_pid == pid)
+ return cpe;
+ return (struct cpelement *)NULL;
+}
+
+/* Search for the coproc named NAME in the list of coprocs; return the
+ cpelement struct if found. If not found, return NULL. */
+static struct cpelement *
+cpl_searchbyname (name)
+ const char *name;
+{
+ struct cpelement *cp;
+
+ for (cp = coproc_list.head ; cp; cp = cp->next)
+ if (STREQ (cp->coproc->c_name, name))
+ return cp;
+ return (struct cpelement *)NULL;
+}
+
+static pid_t
+cpl_firstactive ()
+{
+ struct cpelement *cpe;
+
+ for (cpe = coproc_list.head ; cpe; cpe = cpe->next)
+ if ((cpe->coproc->c_flags & COPROC_DEAD) == 0)
+ return cpe->coproc->c_pid;
+ return (pid_t)NO_PID;
+}
+
+#if 0
+static void
+cpl_prune ()
+{
+ struct cpelement *cp;
+
+ while (coproc_list.head && coproc_list.ncoproc > COPROC_MAX)
+ {
+ cp = coproc_list.head;
+ coproc_list.head = coproc_list.head->next;
+ coproc_dispose (cp->coproc);
+ cpe_dispose (cp);
+ coproc_list.ncoproc--;
+ }
+}
+#endif
+
+/* These currently use a single global "shell coproc" but are written in a
+ way to not preclude additional coprocs later (using the list management
+ package above). */
+
+struct coproc *
+getcoprocbypid (pid)
+ pid_t pid;
+{
+#if MULTIPLE_COPROCS
+ struct cpelement *p;
+
+ p = cpl_search (pid);
+ return (p ? p->coproc : 0);
+#else
+ return (pid == sh_coproc.c_pid ? &sh_coproc : 0);
+#endif
+}
+
+struct coproc *
+getcoprocbyname (name)
+ const char *name;
+{
+#if MULTIPLE_COPROCS
+ struct cpelement *p;
+
+ p = cpl_searchbyname (name);
+ return (p ? p->coproc : 0);
+#else
+ return ((sh_coproc.c_name && STREQ (sh_coproc.c_name, name)) ? &sh_coproc : 0);
+#endif
+}
+
+void
+coproc_init (cp)
+ struct coproc *cp;
+{
+ cp->c_name = 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 = cp->c_lock = 0;
+}
+
+struct coproc *
+coproc_alloc (name, pid)
+ char *name;
+ pid_t pid;
+{
+ struct coproc *cp;
+
+#if MULTIPLE_COPROCS
+ cp = (struct coproc *)xmalloc (sizeof (struct coproc));
+#else
+ cp = &sh_coproc;
+#endif
+ coproc_init (cp);
+ cp->c_lock = 2;
+
+ cp->c_pid = pid;
+ cp->c_name = savestring (name);
+#if MULTIPLE_COPROCS
+ cpl_add (cp);
+#endif
+ cp->c_lock = 0;
+ return (cp);
+}
+
+static void
+coproc_free (cp)
+ struct coproc *cp;
+{
+ free (cp);
+}
+
+void
+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);
+#if MULTIPLE_COPROCS
+ coproc_free (cp);
+#else
+ coproc_init (cp);
+ cp->c_lock = 0;
+#endif
+ UNBLOCK_SIGNAL (oset);
+}
+
+/* Placeholder for now. Will require changes for multiple coprocs */
+void
+coproc_flush ()
+{
+#if MULTIPLE_COPROCS
+ cpl_flush ();
+#else
+ coproc_dispose (&sh_coproc);
+#endif
+}
+
+void
+coproc_close (cp)
+ struct coproc *cp;
+{
+ if (cp->c_rfd >= 0)
+ {
+ close (cp->c_rfd);
+ cp->c_rfd = -1;
+ }
+ if (cp->c_wfd >= 0)
+ {
+ close (cp->c_wfd);
+ cp->c_wfd = -1;
+ }
+ cp->c_rsave = cp->c_wsave = -1;
+}
+
+void
+coproc_closeall ()
+{
+#if MULTIPLE_COPROCS
+ cpl_closeall ();
+#else
+ coproc_close (&sh_coproc); /* XXX - will require changes for multiple coprocs */
+#endif
+}
+
+void
+coproc_reap ()
+{
+#if MULTIPLE_COPROCS
+ cpl_reap ();
+#else
+ struct coproc *cp;
+
+ cp = &sh_coproc; /* XXX - will require changes for multiple coprocs */
+ if (cp && (cp->c_flags & COPROC_DEAD))
+ coproc_dispose (cp);
+#endif
+}
+
+void
+coproc_rclose (cp, fd)
+ struct coproc *cp;
+ int fd;
+{
+ if (cp->c_rfd >= 0 && cp->c_rfd == fd)
+ {
+ close (cp->c_rfd);
+ cp->c_rfd = -1;
+ }
+}
+
+void
+coproc_wclose (cp, fd)
+ struct coproc *cp;
+ int fd;
+{
+ if (cp->c_wfd >= 0 && cp->c_wfd == fd)
+ {
+ close (cp->c_wfd);
+ cp->c_wfd = -1;
+ }
+}
+
+void
+coproc_checkfd (cp, fd)
+ struct coproc *cp;
+ int fd;
+{
+ int update;
+
+ update = 0;
+ if (cp->c_rfd >= 0 && cp->c_rfd == fd)
+ update = cp->c_rfd = -1;
+ if (cp->c_wfd >= 0 && cp->c_wfd == fd)
+ update = cp->c_wfd = -1;
+ if (update)
+ coproc_setvars (cp);
+}
+
+void
+coproc_fdchk (fd)
+ int fd;
+{
+#if MULTIPLE_COPROCS
+ cpl_fdchk (fd);
+#else
+ coproc_checkfd (&sh_coproc, fd);
+#endif
+}
+
+void
+coproc_fdclose (cp, fd)
+ struct coproc *cp;
+ int fd;
+{
+ coproc_rclose (cp, fd);
+ coproc_wclose (cp, fd);
+ coproc_setvars (cp);
+}
+
+void
+coproc_fdsave (cp)
+ struct coproc *cp;
+{
+ cp->c_rsave = cp->c_rfd;
+ cp->c_wsave = cp->c_wfd;
+}
+
+void
+coproc_fdrestore (cp)
+ struct coproc *cp;
+{
+ cp->c_rfd = cp->c_rsave;
+ cp->c_wfd = cp->c_wsave;
+}
+
+static void
+coproc_setstatus (cp, status)
+ struct coproc *cp;
+ int status;
+{
+ cp->c_lock = 4;
+ cp->c_status = status;
+ cp->c_flags |= COPROC_DEAD;
+ cp->c_flags &= ~COPROC_RUNNING;
+ /* 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;
+}
+
+void
+coproc_pidchk (pid, status)
+ pid_t pid;
+ int status;
+{
+ struct coproc *cp;
+
+#if MULTIPLE_COPROCS
+ struct cpelement *cpe;
+
+ /* We're not disposing the coproc because this is executed in a signal
+ handler context */
+ cpe = cpl_search (pid);
+ cp = cpe ? cpe->coproc : 0;
+#else
+ cp = getcoprocbypid (pid);
+#endif
+ if (cp)
+ coproc_setstatus (cp, status);
+}
+
+pid_t
+coproc_active ()
+{
+#if MULTIPLE_COPROCS
+ return (cpl_firstactive ());
+#else
+ return ((sh_coproc.c_flags & COPROC_DEAD) ? NO_PID : sh_coproc.c_pid);
+#endif
+}
+void
+coproc_setvars (cp)
+ struct coproc *cp;
+{
+ SHELL_VAR *v;
+ char *namevar, *t;
+ int l;
+ WORD_DESC w;
+#if defined (ARRAY_VARS)
+ arrayind_t ind;
+#endif
+
+ if (cp->c_name == 0)
+ return;
+
+ /* We could do more here but right now we only check the name, warn if it's
+ not a valid identifier, and refuse to create variables with invalid names
+ if a coproc with such a name is supplied. */
+ w.word = cp->c_name;
+ w.flags = 0;
+ if (check_identifier (&w, 1) == 0)
+ return;
+
+ l = strlen (cp->c_name);
+ namevar = xmalloc (l + 16);
+
+#if defined (ARRAY_VARS)
+ v = find_variable (cp->c_name);
+
+ /* This is the same code as in find_or_make_array_variable */
+ if (v == 0)
+ {
+ v = find_variable_nameref_for_create (cp->c_name, 1);
+ if (v == INVALID_NAMEREF_VALUE)
+ {
+ free (namevar);
+ return;
+ }
+ if (v && nameref_p (v))
+ {
+ free (cp->c_name);
+ cp->c_name = savestring (nameref_cell (v));
+ v = make_new_array_variable (cp->c_name);
+ }
+ }
+
+ if (v && (readonly_p (v) || noassign_p (v)))
+ {
+ if (readonly_p (v))
+ err_readonly (cp->c_name);
+ free (namevar);
+ return;
+ }
+ if (v == 0)
+ v = make_new_array_variable (cp->c_name);
+ if (array_p (v) == 0)
+ v = convert_var_to_array (v);
+
+ t = itos (cp->c_rfd);
+ ind = 0;
+ v = bind_array_variable (cp->c_name, ind, t, 0);
+ free (t);
+
+ t = itos (cp->c_wfd);
+ ind = 1;
+ v = bind_array_variable (cp->c_name, ind, t, 0);
+ free (t);
+#else
+ sprintf (namevar, "%s_READ", cp->c_name);
+ t = itos (cp->c_rfd);
+ bind_variable (namevar, t, 0);
+ free (t);
+ sprintf (namevar, "%s_WRITE", cp->c_name);
+ t = itos (cp->c_wfd);
+ bind_variable (namevar, t, 0);
+ free (t);
+#endif
+
+ sprintf (namevar, "%s_PID", cp->c_name);
+ t = itos (cp->c_pid);
+ v = bind_variable (namevar, t, 0);
+ free (t);
+
+ free (namevar);
+}
+
+void
+coproc_unsetvars (cp)
+ struct coproc *cp;
+{
+ int l;
+ char *namevar;
+
+ if (cp->c_name == 0)
+ return;
+
+ l = strlen (cp->c_name);
+ namevar = xmalloc (l + 16);
+
+ sprintf (namevar, "%s_PID", cp->c_name);
+ unbind_variable_noref (namevar);
+
+#if defined (ARRAY_VARS)
+ check_unbind_variable (cp->c_name);
+#else
+ sprintf (namevar, "%s_READ", cp->c_name);
+ unbind_variable (namevar);
+ sprintf (namevar, "%s_WRITE", cp->c_name);
+ unbind_variable (namevar);
+#endif
+
+ free (namevar);
+}
+
+static int
+execute_coproc (command, pipe_in, pipe_out, fds_to_close)
+ COMMAND *command;
+ int pipe_in, pipe_out;
+ struct fd_bitmap *fds_to_close;
+{
+ int rpipe[2], wpipe[2], estat, invert;
+ pid_t coproc_pid;
+ Coproc *cp;
+ char *tcmd, *p, *name;
+ sigset_t set, oset;
+
+ /* XXX -- can be removed after changes to handle multiple coprocs */
+#if !MULTIPLE_COPROCS
+ if (sh_coproc.c_pid != NO_PID && (sh_coproc.c_rfd >= 0 || sh_coproc.c_wfd >= 0))
+ internal_warning (_("execute_coproc: coproc [%d:%s] still exists"), sh_coproc.c_pid, sh_coproc.c_name);
+ coproc_init (&sh_coproc);
+#endif
+
+ invert = (command->flags & CMD_INVERT_RETURN) != 0;
+
+ /* expand name without splitting - could make this dependent on a shopt option */
+ name = expand_string_unsplit_to_string (command->value.Coproc->name, 0);
+ /* Optional check -- could be relaxed */
+ if (legal_identifier (name) == 0)
+ {
+ internal_error (_("`%s': not a valid identifier"), name);
+ return (invert ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
+ }
+ else
+ {
+ free (command->value.Coproc->name);
+ command->value.Coproc->name = name;
+ }
+
+ 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 (p = savestring (tcmd), 1);
+
+ if (coproc_pid == 0)
+ {
+ close (rpipe[0]);
+ close (wpipe[1]);
+
+#if defined (JOB_CONTROL)
+ FREE (p);
+#endif
+
+ UNBLOCK_SIGNAL (oset);
+ estat = execute_in_subshell (command, 1, wpipe[0], rpipe[1], fds_to_close);
+
+ fflush (stdout);
+ fflush (stderr);
+
+ exit (estat);
+ }
+
+ close (rpipe[1]);
+ close (wpipe[0]);
+
+ cp = coproc_alloc (command->value.Coproc->name, coproc_pid);
+ cp->c_rfd = rpipe[0];
+ cp->c_wfd = wpipe[1];
+
+ cp->c_flags |= COPROC_RUNNING;
+
+ SET_CLOSE_ON_EXEC (cp->c_rfd);
+ SET_CLOSE_ON_EXEC (cp->c_wfd);
+
+ coproc_setvars (cp);
+
+ UNBLOCK_SIGNAL (oset);
+
+#if 0
+ itrace ("execute_coproc (%s): [%d] %s", command->value.Coproc->name, coproc_pid, the_printed_command);
+#endif
+
+ close_pipes (pipe_in, pipe_out);
+#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
+ unlink_fifo_list ();
+#endif
+ stop_pipeline (1, (COMMAND *)NULL);
+ DESCRIBE_PID (coproc_pid);
+ run_pending_traps ();
+
+ return (invert ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
+}
+#endif
+
+static void
+restore_stdin (s)
+ int s;
+{
+ dup2 (s, 0);
+ close (s);
+}
+
+/* Catch-all cleanup function for lastpipe code for unwind-protects */
+static void
+lastpipe_cleanup (s)
+ int s;
+{
+ set_jobs_list_frozen (s);
+}
+
+static int
+execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
+ COMMAND *command;
+ int asynchronous, pipe_in, pipe_out;
+ struct fd_bitmap *fds_to_close;
+{
+ int prev, fildes[2], new_bitmap_size, dummyfd, ignore_return, exec_result;
+ int lstdin, lastpipe_flag, lastpipe_jid, old_frozen;
+ COMMAND *cmd;
+ struct fd_bitmap *fd_bitmap;
+ pid_t lastpid;
+
+#if defined (JOB_CONTROL)
+ sigset_t set, oset;
+ BLOCK_CHILD (set, oset);
+#endif /* JOB_CONTROL */
+
+ ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
+
+ prev = pipe_in;
+ cmd = command;
+
+ while (cmd && cmd->type == cm_connection &&
+ cmd->value.Connection && cmd->value.Connection->connector == '|')
+ {
+ /* Make a pipeline between the two commands. */
+ if (pipe (fildes) < 0)
+ {
+ sys_error (_("pipe error"));
+#if defined (JOB_CONTROL)
terminate_current_pipeline ();
kill_current_pipeline ();
UNBLOCK_CHILD (oset);
cmd = cmd->value.Connection->second;
}
+ lastpid = last_made_pid;
+
/* Now execute the rightmost command in the pipeline. */
if (ignore_return && cmd)
cmd->flags |= CMD_IGNORE_RETURN;
+
+ lastpipe_flag = 0;
+
+ begin_unwind_frame ("lastpipe-exec");
+ lstdin = -1;
+ /* If the `lastpipe' option is set with shopt, and job control is not
+ enabled, execute the last element of non-async pipelines in the
+ current shell environment. */
+ if (lastpipe_opt && job_control == 0 && asynchronous == 0 && pipe_out == NO_PIPE && prev > 0)
+ {
+ lstdin = move_to_high_fd (0, 1, -1);
+ if (lstdin > 0)
+ {
+ do_piping (prev, pipe_out);
+ prev = NO_PIPE;
+ add_unwind_protect (restore_stdin, lstdin);
+ lastpipe_flag = 1;
+ old_frozen = freeze_jobs_list ();
+ lastpipe_jid = stop_pipeline (0, (COMMAND *)NULL); /* XXX */
+ add_unwind_protect (lastpipe_cleanup, old_frozen);
+ }
+ if (cmd)
+ cmd->flags |= CMD_LASTPIPE;
+ }
+ if (prev >= 0)
+ add_unwind_protect (close, prev);
+
exec_result = execute_command_internal (cmd, asynchronous, prev, pipe_out, fds_to_close);
+ if (lstdin > 0)
+ restore_stdin (lstdin);
+
if (prev >= 0)
close (prev);
UNBLOCK_CHILD (oset);
#endif
+ QUIT;
+
+ if (lastpipe_flag)
+ {
+#if defined (JOB_CONTROL)
+ if (INVALID_JOB (lastpipe_jid) == 0)
+ {
+ append_process (savestring (the_printed_command_except_trap), dollar_dollar_pid, exec_result, lastpipe_jid);
+ lstdin = wait_for (lastpid);
+ }
+ else
+ lstdin = wait_for_single_pid (lastpid, 0); /* checks bgpids list */
+#else
+ lstdin = wait_for (lastpid);
+#endif
+
+#if defined (JOB_CONTROL)
+ /* If wait_for removes the job from the jobs table, use result of last
+ command as pipeline's exit status as usual. The jobs list can get
+ frozen and unfrozen at inconvenient times if there are multiple pipelines
+ running simultaneously. */
+ if (INVALID_JOB (lastpipe_jid) == 0)
+ exec_result = job_exit_status (lastpipe_jid);
+ else if (pipefail_opt)
+ exec_result = exec_result | lstdin; /* XXX */
+ /* otherwise we use exec_result */
+#endif
+
+ set_jobs_list_frozen (old_frozen);
+ }
+
+ discard_unwind_frame ("lastpipe-exec");
+
return (exec_result);
}
int asynchronous, pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
- REDIRECT *rp;
COMMAND *tc, *second;
- int ignore_return, exec_result;
+ int ignore_return, exec_result, was_error_trap, invert;
+ volatile int save_line_number;
ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
if (tc == 0)
return (EXECUTION_SUCCESS);
- rp = tc->redirects;
-
if (ignore_return)
tc->flags |= CMD_IGNORE_RETURN;
tc->flags |= CMD_AMPERSAND;
tc->flags |= CMD_STDIN_REDIR;
exec_result = execute_command_internal (tc, 1, pipe_in, pipe_out, fds_to_close);
+ QUIT;
if (tc->flags & CMD_STDIN_REDIR)
tc->flags &= ~CMD_STDIN_REDIR;
if (command->value.Connection->second)
command->value.Connection->second->flags |= CMD_IGNORE_RETURN;
}
+ executing_list++;
QUIT;
execute_command (command->value.Connection->first);
QUIT;
exec_result = execute_command_internal (command->value.Connection->second,
asynchronous, pipe_in, pipe_out,
fds_to_close);
+ executing_list--;
break;
case '|':
+ was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;
+ invert = (command->flags & CMD_INVERT_RETURN) != 0;
+ ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
+
+ line_number_for_err_trap = line_number; /* XXX - save value? */
exec_result = execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close);
+
+ if (asynchronous)
+ {
+ exec_result = EXECUTION_SUCCESS;
+ invert = 0;
+ }
+
+ if (was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
+ {
+ last_command_exit_value = exec_result;
+ save_line_number = line_number;
+ line_number = line_number_for_err_trap;
+ run_error_trap ();
+ line_number = save_line_number;
+ }
+
+ if (ignore_return == 0 && invert == 0 && exit_immediately_on_error && exec_result != EXECUTION_SUCCESS)
+ {
+ last_command_exit_value = exec_result;
+ run_pending_traps ();
+ jump_to_top_level (ERREXIT);
+ }
+
break;
case AND_AND:
and the connector is OR_OR, then execute the second command,
otherwise return. */
+ executing_list++;
if (command->value.Connection->first)
command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
((command->value.Connection->connector == OR_OR) &&
(exec_result != EXECUTION_SUCCESS)))
{
- if (ignore_return && command->value.Connection->second)
- command->value.Connection->second->flags |= CMD_IGNORE_RETURN;
+ second = command->value.Connection->second;
+ if (ignore_return && second)
+ second->flags |= CMD_IGNORE_RETURN;
- exec_result = execute_command (command->value.Connection->second);
+ exec_result = execute_command (second);
}
+ executing_list--;
break;
default:
return exec_result;
}
+/* The test used to be only for interactive_shell, but we don't want to report
+ job status when the shell is not interactive or when job control isn't
+ enabled. */
#define REAP() \
do \
{ \
- if (!interactive_shell) \
+ if (job_control == 0 || interactive_shell == 0) \
reap_dead_jobs (); \
} \
while (0)
{
if (posixly_correct && interactive_shell == 0)
{
- last_command_exit_value = EX_USAGE;
+ last_command_exit_value = EX_BADUSAGE;
jump_to_top_level (ERREXIT);
}
return (EXECUTION_FAILURE);
loop_level++;
identifier = for_command->name->word;
+ line_number = for_command->line; /* for expansion error messages */
list = releaser = expand_words_no_vars (for_command->map_list);
begin_unwind_frame ("for");
/* Save this command unless it's a trap command and we're not running
a debug trap. */
- if (signal_in_progress (DEBUG_TRAP) == 0 && (this_command_name == 0 || (STREQ (this_command_name, "trap") == 0)))
+ if (signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
{
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);
- if (readonly_p (v) || noassign_p (v))
+ /* XXX - special ksh93 for command index variable handling */
+ v = find_variable_last_nameref (identifier, 1);
+ if (v && nameref_p (v))
+ {
+ if (valid_nameref_value (list->word->word, 1) == 0)
+ {
+ sh_invalidid (list->word->word);
+ v = 0;
+ }
+ else if (readonly_p (v))
+ err_readonly (name_cell (v));
+ else
+ v = bind_variable_value (v, list->word->word, ASS_NAMEREF);
+ }
+ else
+ v = bind_variable (identifier, list->word->word, 0);
+
+ if (v == 0 || readonly_p (v) || noassign_p (v))
{
line_number = save_line_number;
- if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
+ if (v && readonly_p (v) && interactive_shell == 0 && posixly_correct)
{
last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level (FORCE_EOF);
return (EXECUTION_FAILURE);
}
}
+
+ if (ifsname (identifier))
+ setifs (v);
+
retval = execute_command (for_command->action);
REAP ();
QUIT;
{
SHELL_VAR *new_value;
- new_value = bind_variable (identifier, value_cell(old_value), 0);
+ new_value = bind_variable (identifier, value_cell (old_value), 0);
new_value->attributes = old_value->attributes;
dispose_variable (old_value);
}
command_string_index = 0;
print_arith_command (new);
- if (signal_in_progress (DEBUG_TRAP) == 0)
+ if (signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
{
FREE (the_printed_command_except_trap);
the_printed_command_except_trap = savestring (the_printed_command);
skip the command. */
#if defined (DEBUGGER)
if (debugging_mode == 0 || r == EXECUTION_SUCCESS)
- expresult = evalexp (new->word->word, okp);
+ expresult = evalexp (new->word->word, EXP_EXPANDED, okp);
else
{
expresult = 0;
*okp = 1;
}
#else
- expresult = evalexp (new->word->word, okp);
+ expresult = evalexp (new->word->word, EXP_EXPANDED, okp);
#endif
dispose_words (new);
}
line_number before executing each expression -- for $LINENO
and the DEBUG trap. */
line_number = arith_lineno = arith_for_command->line;
- if (variable_context && interactive_shell)
- line_number -= function_line_number;
+ if (variable_context && interactive_shell && sourcelevel == 0)
+ {
+ /* line numbers in a function start at 1 */
+ line_number -= function_line_number - 1;
+ if (line_number <= 0)
+ line_number = 1;
+ }
/* Evaluate the initialization expression. */
expresult = eval_arith_for_expr (arith_for_command->init, &expok);
: ((s < 100000) ? 5 \
: 6)))))
+static int
+displen (s)
+ const char *s;
+{
+#if defined (HANDLE_MULTIBYTE)
+ wchar_t *wcstr;
+ size_t slen;
+ int wclen;
+
+ wcstr = 0;
+ slen = mbstowcs (wcstr, s, 0);
+ if (slen == -1)
+ slen = 0;
+ wcstr = (wchar_t *)xmalloc (sizeof (wchar_t) * (slen + 1));
+ mbstowcs (wcstr, s, slen + 1);
+ wclen = wcswidth (wcstr, slen);
+ free (wcstr);
+ return (wclen < 0 ? STRLEN(s) : wclen);
+#else
+ return (STRLEN (s));
+#endif
+}
+
static int
print_index_and_element (len, ind, list)
int len, ind;
return (0);
for (i = ind, l = list; l && --i; l = l->next)
;
+ if (l == 0) /* don't think this can happen */
+ return (0);
fprintf (stderr, "%*d%s%s", len, ind, RP_SPACE, l->word->word);
- return (STRLEN (l->word->word));
+ return (displen (l->word->word));
}
static void
WORD_LIST *l;
char *repl_string, *t;
- t = get_string_value ("LINES");
- LINES = (t && *t) ? atoi (t) : 24;
- t = get_string_value ("COLUMNS");
- COLS = (t && *t) ? atoi (t) : 80;
+ COLS = default_columns ();
#if 0
t = get_string_value ("TABSIZE");
max_elem_len = 0;
for (l = list; l; l = l->next)
{
- len = STRLEN (l->word->word);
+ len = displen (l->word->word);
if (len > max_elem_len)
max_elem_len = len;
}
fflush (stderr);
QUIT;
- if (read_builtin ((WORD_LIST *)NULL) == EXECUTION_FAILURE)
+ if (read_builtin ((WORD_LIST *)NULL) != EXECUTION_SUCCESS)
{
putchar ('\n');
return ((char *)NULL);
}
repl_string = get_string_value ("REPLY");
+ if (repl_string == 0)
+ return ((char *)NULL);
if (*repl_string == 0)
{
print_menu = 1;
for (l = list; l && --reply; l = l->next)
;
- return (l->word->word);
+ return (l->word->word); /* XXX - can't be null? */
}
}
if (echo_command_at_execute)
xtrace_print_select_command_head (select_command);
+#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);
}
v = bind_variable (identifier, selection, 0);
- if (readonly_p (v) || noassign_p (v))
+ if (v == 0 || readonly_p (v) || noassign_p (v))
{
- if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
+ if (v && readonly_p (v) && interactive_shell == 0 && posixly_correct)
{
last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level (FORCE_EOF);
WORD_LIST *wlist, *es;
PATTERN_LIST *clauses;
char *word, *pattern;
- int retval, match, ignore_return, save_line_number;
+ int retval, match, ignore_return, save_line_number, qflags;
save_line_number = line_number;
line_number = case_command->line;
if (echo_command_at_execute)
xtrace_print_case_command_head (case_command);
- if (signal_in_progress (DEBUG_TRAP == 0) && (this_command_name == 0 || (STREQ (this_command_name, "trap") == 0)))
+#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
- wlist = expand_word_unsplit (case_command->word, 0);
- word = wlist ? string_list (wlist) : savestring ("");
+ /* Use the same expansions (the ones POSIX specifies) as the patterns;
+ dequote the resulting string (as POSIX specifies) since the quotes in
+ patterns are handled specially below. We have to do it in this order
+ because we're not supposed to perform word splitting. */
+ wlist = expand_word_leave_quoted (case_command->word, 0);
+ if (wlist)
+ {
+ char *t;
+ t = string_list (wlist);
+ word = dequote_string (t);
+ free (t);
+ }
+ else
+ word = savestring ("");
dispose_words (wlist);
retval = EXECUTION_SUCCESS;
ignore_return = case_command->flags & CMD_IGNORE_RETURN;
begin_unwind_frame ("case");
- add_unwind_protect ((Function *)xfree, word);
+ add_unwind_protect (xfree, word);
#define EXIT_CASE() goto exit_case_command
es = expand_word_leave_quoted (list->word, 0);
if (es && es->word && es->word->word && *(es->word->word))
- pattern = quote_string_for_globbing (es->word->word, QGLOB_CVTNULL);
+ {
+ /* Convert quoted null strings into empty strings. */
+ qflags = QGLOB_CVTNULL;
+
+ /* We left CTLESC in place quoting CTLESC and CTLNUL after the
+ call to expand_word_leave_quoted; tell quote_string_for_globbing
+ to remove those here. This works for both unquoted portions of
+ the word (which call quote_escapes) and quoted portions
+ (which call quote_string). */
+ qflags |= QGLOB_CTLESC;
+ pattern = quote_string_for_globbing (es->word->word, qflags);
+ }
else
{
pattern = (char *)xmalloc (1);
if (match)
{
- if (clauses->action && ignore_return)
- clauses->action->flags |= CMD_IGNORE_RETURN;
- retval = execute_command (clauses->action);
- EXIT_CASE ();
+ do
+ {
+ if (clauses->action && ignore_return)
+ clauses->action->flags |= CMD_IGNORE_RETURN;
+ retval = execute_command (clauses->action);
+ }
+ while ((clauses->flags & CASEPAT_FALLTHROUGH) && (clauses = clauses->next));
+ if (clauses == 0 || (clauses->flags & CASEPAT_TESTNEXT) == 0)
+ EXIT_CASE ();
+ else
+ break;
}
QUIT;
loop. The job control code will set `breaking' to loop_level
when a job in a loop is stopped with SIGTSTP. If the stopped job
is in the loop test, `breaking' will not be reset unless we do
- this, and the shell will cease to execute commands. */
+ this, and the shell will cease to execute commands. The same holds
+ true for `continue'. */
if (type == CMD_WHILE && return_value != EXECUTION_SUCCESS)
{
if (breaking)
breaking--;
+ if (continuing)
+ continuing--;
break;
}
if (type == CMD_UNTIL && return_value == EXECUTION_SUCCESS)
{
if (breaking)
breaking--;
+ if (continuing)
+ continuing--;
break;
}
int expok, save_line_number, retval;
intmax_t expresult;
WORD_LIST *new;
+ char *exp;
expresult = 0;
save_line_number = line_number;
this_command_name = "(("; /* )) */
- line_number = arith_command->line;
+ line_number_for_err_trap = line_number = arith_command->line;
/* If we're in a function, update the line number information. */
- if (variable_context && interactive_shell)
- line_number -= function_line_number;
+ if (variable_context && interactive_shell && sourcelevel == 0)
+ {
+ /* line numbers in a function start at 1 */
+ line_number -= function_line_number - 1;
+ if (line_number <= 0)
+ line_number = 1;
+ }
command_string_index = 0;
print_arith_command (arith_command->exp);
- if (signal_in_progress (DEBUG_TRAP) == 0)
+ if (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 (new)
{
- expresult = evalexp (new->word->word, &expok);
+ exp = new->next ? string_list (new) : new->word->word;
+ expresult = evalexp (exp, EXP_EXPANDED, &expok);
line_number = save_line_number;
+ if (exp != new->word->word)
+ free (exp);
dispose_words (new);
}
else
#if defined (COND_COMMAND)
-static char *nullstr = "";
+static char * const nullstr = "";
+/* XXX - can COND ever be NULL when this is called? */
static int
execute_cond_node (cond)
COND_COM *cond;
{
- int result, invert, patmatch, rmatch, mflags;
+ int result, invert, patmatch, rmatch, mflags, ignore;
char *arg1, *arg2;
+#if 0
+ char *t1, *t2;
+#endif
invert = (cond->flags & CMD_INVERT_RETURN);
-
+ ignore = (cond->flags & CMD_IGNORE_RETURN);
+ if (ignore)
+ {
+ if (cond->left)
+ cond->left->flags |= CMD_IGNORE_RETURN;
+ if (cond->right)
+ cond->right->flags |= CMD_IGNORE_RETURN;
+ }
+
if (cond->type == COND_EXPR)
result = execute_cond_node (cond->left);
else if (cond->type == COND_OR)
}
else if (cond->type == COND_UNARY)
{
+ if (ignore)
+ comsub_ignore_return++;
arg1 = cond_expand_word (cond->left->op, 0);
+ if (ignore)
+ comsub_ignore_return--;
if (arg1 == 0)
arg1 = nullstr;
if (echo_command_at_execute)
}
else if (cond->type == COND_BINARY)
{
- patmatch = ((cond->op->word[1] == '=') && (cond->op->word[2] == '\0') &&
- (cond->op->word[0] == '!' || cond->op->word[0] == '=') ||
+ rmatch = 0;
+ patmatch = (((cond->op->word[1] == '=') && (cond->op->word[2] == '\0') &&
+ (cond->op->word[0] == '!' || cond->op->word[0] == '=')) ||
(cond->op->word[0] == '=' && cond->op->word[1] == '\0'));
#if defined (COND_REGEXP)
rmatch = (cond->op->word[0] == '=' && cond->op->word[1] == '~' &&
cond->op->word[2] == '\0');
#endif
+ if (ignore)
+ comsub_ignore_return++;
arg1 = cond_expand_word (cond->left->op, 0);
+ if (ignore)
+ comsub_ignore_return--;
if (arg1 == 0)
arg1 = nullstr;
- arg2 = cond_expand_word (cond->right->op, patmatch);
+ if (ignore)
+ comsub_ignore_return++;
+ arg2 = cond_expand_word (cond->right->op,
+ (rmatch && shell_compatibility_level > 31) ? 2 : (patmatch ? 1 : 0));
+ if (ignore)
+ comsub_ignore_return--;
if (arg2 == 0)
arg2 = nullstr;
mflags |= SHMAT_SUBEXP;
#endif
+#if 0
+ t1 = strescape(arg1);
+ t2 = strescape(arg2);
+ itrace("execute_cond_node: sh_regmatch on `%s' and `%s'", t1, t2);
+ free(t1);
+ free(t2);
+#endif
+
result = sh_regmatch (arg1, arg2, mflags);
}
else
int oe;
oe = extended_glob;
extended_glob = 1;
- result = binary_test (cond->op->word, arg1, arg2, TEST_PATMATCH|TEST_ARITHEXP)
+ result = binary_test (cond->op->word, arg1, arg2, TEST_PATMATCH|TEST_ARITHEXP|TEST_LOCALE)
? EXECUTION_SUCCESS
: EXECUTION_FAILURE;
extended_glob = oe;
{
int retval, save_line_number;
- retval = EXECUTION_SUCCESS;
save_line_number = line_number;
this_command_name = "[[";
- line_number = cond_command->line;
+ line_number_for_err_trap = line_number = cond_command->line;
/* If we're in a function, update the line number information. */
- if (variable_context && interactive_shell)
- line_number -= function_line_number;
-
+ if (variable_context && interactive_shell && sourcelevel == 0)
+ {
+ /* line numbers in a function start at 1 */
+ line_number -= function_line_number - 1;
+ if (line_number <= 0)
+ line_number = 1;
+ }
command_string_index = 0;
print_cond_command (cond_command);
- if (signal_in_progress (DEBUG_TRAP) == 0)
+ if (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 (arg == 0)
arg = "";
var = bind_variable ("_", arg, 0);
- VUNSETATTR (var, att_exported);
+ if (var)
+ VUNSETATTR (var, att_exported);
}
/* Execute a null command. Fork a subshell if the command uses pipes or is
int pipe_in, pipe_out, async;
{
int r;
+ int forcefork;
+ REDIRECT *rd;
+
+ for (forcefork = 0, rd = redirects; rd; rd = rd->next)
+ {
+ forcefork += rd->rflags & REDIR_VARASSIGN;
+ /* Safety */
+ forcefork += (rd->redirector.dest == 0 || fd_is_bash_input (rd->redirector.dest)) && (INPUT_REDIRECT (rd->instruction) || TRANSLATE_REDIRECT (rd->instruction) || rd->instruction == r_close_this);
+ }
- if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
+ if (forcefork || pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
{
/* We have a null command, but we really want a subshell to take
care of it. Just fork, do piping and redirections, and exit. */
do_piping (pipe_in, pipe_out);
- subshell_environment = SUBSHELL_ASYNC;
+#if defined (COPROCESS_SUPPORT)
+ coproc_closeall ();
+#endif
+
+ interactive = 0; /* XXX */
+
+ subshell_environment = 0;
+ if (async)
+ subshell_environment |= SUBSHELL_ASYNC;
+ if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
+ subshell_environment |= SUBSHELL_PIPE;
if (do_redirections (redirects, RX_ACTIVE) == 0)
exit (EXECUTION_SUCCESS);
{
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);
}
fix_assignment_words (words)
WORD_LIST *words;
{
- WORD_LIST *w;
+ WORD_LIST *w, *wcmd;
struct builtin *b;
+ int assoc, global, array, integer;
if (words == 0)
return;
b = 0;
+ assoc = global = array = integer = 0;
- for (w = words; w; w = w->next)
+ /* Skip over assignment statements preceding a command name */
+ wcmd = words;
+ for (wcmd = words; wcmd; wcmd = wcmd->next)
+ if ((wcmd->word->flags & W_ASSIGNMENT) == 0)
+ break;
+ /* Posix (post-2008) says that `command' doesn't change whether
+ or not the builtin it shadows is a `declaration command', even
+ though it removes other special builtin properties. In Posix
+ mode, we skip over one or more instances of `command' and
+ deal with the next word as the assignment builtin. */
+ while (posixly_correct && wcmd && wcmd->word && wcmd->word->word && STREQ (wcmd->word->word, "command"))
+ wcmd = wcmd->next;
+
+ for (w = wcmd; w; w = w->next)
if (w->word->flags & W_ASSIGNMENT)
{
+ /* Lazy builtin lookup, only do it if we find an assignment */
if (b == 0)
{
- b = builtin_address_internal (words->word->word, 0);
+ b = builtin_address_internal (wcmd->word->word, 0);
if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
return;
else if (b && (b->flags & ASSIGNMENT_BUILTIN))
- words->word->flags |= W_ASSNBLTIN;
+ wcmd->word->flags |= W_ASSNBLTIN;
}
w->word->flags |= (W_NOSPLIT|W_NOGLOB|W_TILDEEXP|W_ASSIGNARG);
+#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;
+
+ /* If we have an assignment builtin that does not create local variables,
+ make sure we create global variables even if we internally call
+ `declare'. The CHKLOCAL flag means to set attributes or values on
+ an existing local variable, if there is one. */
+ if (b && ((b->flags & (ASSIGNMENT_BUILTIN|LOCALVAR_BUILTIN)) == ASSIGNMENT_BUILTIN))
+ w->word->flags |= W_ASSNGLOBAL|W_CHKLOCAL;
+#if 0
+ else if (b && (b->flags & ASSIGNMENT_BUILTIN) && (b->flags & LOCALVAR_BUILTIN))
+ w->word->flags |= W_CHKLOCAL;
+#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] == '-' && (strpbrk (w->word->word+1, "Aag") != 0))
+#else
+ else if (w->word->word[0] == '-' && strchr (w->word->word+1, 'g'))
+#endif
+ {
+ if (b == 0)
+ {
+ b = builtin_address_internal (wcmd->word->word, 0);
+ if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
+ return;
+ else if (b && (b->flags & ASSIGNMENT_BUILTIN))
+ wcmd->word->flags |= W_ASSNBLTIN;
+ }
+ 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;
}
}
+/* Return 1 if the file found by searching $PATH for PATHNAME, defaulting
+ to PATHNAME, is a directory. Used by the autocd code below. */
+static int
+is_dirname (pathname)
+ char *pathname;
+{
+ char *temp;
+ int ret;
+
+ temp = search_for_command (pathname, 0);
+ ret = temp ? file_isdir (temp) : file_isdir (pathname);
+ free (temp);
+ return ret;
+}
+
/* The meaty part of all the executions. We have to start hacking the
real execution of commands here. Fork a process, set things up,
execute the command. */
pid_t old_last_async_pid;
sh_builtin_func_t *builtin;
SHELL_VAR *func;
+ volatile int old_builtin, old_command_builtin;
result = EXECUTION_SUCCESS;
special_builtin_failed = builtin_is_special = 0;
command_line = (char *)0;
+ QUIT;
+
/* If we're in a function, update the line number information. */
- if (variable_context && interactive_shell)
- line_number -= function_line_number;
+ if (variable_context && interactive_shell && sourcelevel == 0)
+ {
+ /* line numbers in a function start at 1 */
+ line_number -= function_line_number - 1;
+ if (line_number <= 0)
+ line_number = 1;
+ }
/* Remember what this command line looks like at invocation. */
command_string_index = 0;
print_simple_command (simple_command);
+#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 = the_printed_command ? savestring (the_printed_command) : (char *)0;
#endif
first_word_quoted =
- simple_command->words ? (simple_command->words->word->flags & W_QUOTED): 0;
+ simple_command->words ? (simple_command->words->word->flags & W_QUOTED) : 0;
last_command_subst_pid = NO_PID;
old_last_async_pid = last_asynchronous_pid;
- already_forked = dofork = 0;
+ already_forked = 0;
/* If we're in a pipeline or run in the background, set DOFORK so we
make the child early, before word expansion. This keeps assignment
if (dofork)
{
+ char *p;
+
/* Do this now, because execute_disk_command will do it anyway in the
vast majority of cases. */
maybe_make_export_env ();
/* Don't let a DEBUG trap overwrite the command string to be saved with
the process/job associated with this child. */
- if (make_child (savestring (the_printed_command_except_trap), async) == 0)
+ if (make_child (p = savestring (the_printed_command_except_trap), async) == 0)
{
already_forked = 1;
simple_command->flags |= CMD_NO_FORK;
- subshell_environment = SUBSHELL_FORK;
+ subshell_environment = SUBSHELL_FORK; /* XXX */
if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
subshell_environment |= SUBSHELL_PIPE;
if (async)
if (fds_to_close)
close_fd_bitmap (fds_to_close);
+ /* If we fork because of an input pipe, note input pipe for later to
+ inhibit async commands from redirecting stdin from /dev/null */
+ stdin_redir |= pipe_in != NO_PIPE;
+
do_piping (pipe_in, pipe_out);
pipe_in = pipe_out = NO_PIPE;
+#if defined (COPROCESS_SUPPORT)
+ coproc_closeall ();
+#endif
last_asynchronous_pid = old_last_async_pid;
+
+ CHECK_SIGTERM;
+
+ if (async)
+ subshell_level++; /* not for pipes yet */
+
+#if defined (JOB_CONTROL)
+ FREE (p); /* child doesn't use pointer */
+#endif
}
else
{
+ /* Don't let simple commands that aren't the last command in a
+ pipeline change $? for the rest of the pipeline (or at all). */
+ if (pipe_out != NO_PIPE)
+ result = last_command_exit_value;
close_pipes (pipe_in, pipe_out);
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
- unlink_fifo_list ();
+#if 0
+ /* Close /dev/fd file descriptors in the parent after forking the
+ last child in a (possibly one-element) pipeline. Defer this
+ until any running shell function completes. */
+ if (pipe_out == NO_PIPE && variable_context == 0) /* XXX */
+ unlink_fifo_list (); /* XXX */
+#endif
#endif
command_line = (char *)NULL; /* don't free this. */
+#if 0
bind_lastarg ((char *)NULL);
+#endif
return (result);
}
}
+ QUIT; /* XXX */
+
/* If we are re-running this as the result of executing the `command'
builtin, do not expand the command words a second time. */
if ((simple_command->flags & CMD_INHIBIT_EXPANSION) == 0)
{
current_fds_to_close = fds_to_close;
fix_assignment_words (simple_command->words);
+ /* Pass the ignore return flag down to command substitutions */
+ if (simple_command->flags & CMD_IGNORE_RETURN) /* XXX */
+ comsub_ignore_return++;
words = expand_words (simple_command->words);
+ if (simple_command->flags & CMD_IGNORE_RETURN)
+ comsub_ignore_return--;
current_fds_to_close = (struct fd_bitmap *)NULL;
}
else
pipe_in, pipe_out,
already_forked ? 0 : async);
if (already_forked)
- exit (result);
+ sh_exit (result);
else
{
bind_lastarg ((char *)NULL);
/* In POSIX mode, assignment errors in the temporary environment cause a
non-interactive shell to exit. */
- if (builtin_is_special && interactive_shell == 0 && tempenv_assign_error)
+ if (posixly_correct && builtin_is_special && interactive_shell == 0 && tempenv_assign_error)
{
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;
goto return_result;
}
- /* One other possiblilty. The user may want to resume an existing job.
+ /* One other possibililty. The user may want to resume an existing job.
If they do, find out whether this word is a candidate for a running
job. */
if (job_control && already_forked == 0 && async == 0 &&
}
#endif /* JOB_CONTROL */
+run_builtin:
/* Remember the name of this command globally. */
this_command_name = words->word->word;
if (builtin || func)
{
+ if (builtin)
+ {
+ old_builtin = executing_builtin;
+ old_command_builtin = executing_command_builtin;
+ unwind_protect_int (executing_builtin); /* modified in execute_builtin */
+ unwind_protect_int (executing_command_builtin); /* ditto */
+ }
if (already_forked)
{
/* reset_terminating_signals (); */ /* XXX */
- /* Cancel traps, in trap.c. */
- restore_original_signals ();
+ /* Reset the signal handlers in the child, but don't free the
+ trap strings. Set a flag noting that we have to free the
+ trap strings if we run trap to change a signal disposition. */
+ reset_signal_handlers ();
+ subshell_environment |= SUBSHELL_RESETTRAP;
if (async)
{
setup_async_signals ();
}
- subshell_level++;
+ if (async == 0)
+ subshell_level++;
execute_subshell_builtin_or_function
(words, simple_command->redirects, builtin, func,
pipe_in, pipe_out, async, fds_to_close,
{
if (result > EX_SHERRBASE)
{
+ switch (result)
+ {
+ case EX_REDIRFAIL:
+ case EX_BADASSIGN:
+ case EX_EXPFAIL:
+ /* These errors cause non-interactive posix mode shells to exit */
+ if (posixly_correct && builtin_is_special && interactive_shell == 0)
+ {
+ last_command_exit_value = EXECUTION_FAILURE;
+ jump_to_top_level (ERREXIT);
+ }
+ break;
+ case EX_DISKFALLBACK:
+ /* XXX - experimental */
+ executing_builtin = old_builtin;
+ executing_command_builtin = old_command_builtin;
+ builtin = 0;
+ /* XXX - redirections will have to be performed again */
+ goto execute_from_filesystem;
+ }
result = builtin_status (result);
if (builtin_is_special)
- special_builtin_failed = 1;
+ special_builtin_failed = 1; /* XXX - take command builtin into account? */
}
/* In POSIX mode, if there are assignment statements preceding
a special builtin, they persist after the builtin
}
}
+ if (autocd && interactive && words->word && is_dirname (words->word->word))
+ {
+ words = make_word_list (make_word ("--"), words);
+ words = make_word_list (make_word ("cd"), words);
+ xtrace_print_word_list (words, 0);
+ func = find_function ("cd");
+ goto run_builtin;
+ }
+
+execute_from_filesystem:
if (command_line == 0)
- command_line = savestring (the_printed_command);
+ command_line = savestring (the_printed_command_except_trap ? the_printed_command_except_trap : "");
- execute_disk_command (words, simple_command->redirects, command_line,
+#if defined (PROCESS_SUBSTITUTION)
+ /* The old code did not test already_forked and only did this if
+ subshell_environment&SUBSHELL_COMSUB != 0 (comsubs and procsubs). Other
+ uses of the no-fork optimization left FIFOs in $TMPDIR */
+ if (already_forked == 0 && (simple_command->flags & CMD_NO_FORK) && fifos_pending() > 0)
+ simple_command->flags &= ~CMD_NO_FORK;
+#endif
+ result = execute_disk_command (words, simple_command->redirects, command_line,
pipe_in, pipe_out, async, fds_to_close,
simple_command->flags);
bind_lastarg (lastarg);
FREE (command_line);
dispose_words (words);
+ if (builtin)
+ {
+ executing_builtin = old_builtin;
+ executing_command_builtin = old_command_builtin;
+ }
discard_unwind_frame ("simple-command");
this_command_name = (char *)NULL; /* points to freed memory now */
return (result);
WORD_LIST *words;
int flags, subshell;
{
- int old_e_flag, result, eval_unwind;
- int isbltinenv;
+ int result, eval_unwind, ignexit_flag;
+ int isbltinenv, should_keep;
+ char *error_trap;
+
+ error_trap = 0;
+ should_keep = 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
eval builtin is being called, and we're supposed to ignore the exit
- value of the command, we turn the -e flag off ourselves, then
- restore it when the command completes. */
- if (subshell == 0 && builtin == eval_builtin && (flags & CMD_IGNORE_RETURN))
+ value of the command, we turn the -e flag off ourselves and disable
+ the ERR trap, then restore them when the command completes. This is
+ also a problem (as below) for the command and source/. builtins. */
+ if (subshell == 0 && (flags & CMD_IGNORE_RETURN) &&
+ (builtin == eval_builtin || builtin == command_builtin || builtin == source_builtin))
{
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)
+ {
+ error_trap = savestring (error_trap);
+ add_unwind_protect (xfree, error_trap);
+ add_unwind_protect (set_error_trap, error_trap);
+ restore_default_signal (ERROR_TRAP);
+ }
exit_immediately_on_error = 0;
+ ignexit_flag = builtin_ignoring_errexit;
+ 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. */
+ /* SHOULD_KEEP is for the pop_scope call below; it only matters when
+ posixly_correct is set, but we should propagate the temporary environment
+ to the enclosing environment only for special builtins. */
+ isbltinenv = (builtin == source_builtin || builtin == eval_builtin || builtin == unset_builtin || builtin == mapfile_builtin);
+ should_keep = isbltinenv && builtin != mapfile_builtin;
+#if defined (HISTORY) && defined (READLINE)
+ if (builtin == fc_builtin || builtin == read_builtin)
+ {
+ isbltinenv = 1;
+ should_keep = 0;
+ }
+#endif
if (isbltinenv)
{
if (temporary_env)
{
push_scope (VC_BLTNENV, temporary_env);
+ if (flags & CMD_COMMAND_BUILTIN)
+ should_keep = 0;
if (subshell == 0)
- add_unwind_protect (pop_scope, (flags & CMD_COMMAND_BUILTIN) ? 0 : "1");
+ add_unwind_protect (pop_scope, should_keep ? "1" : 0);
temporary_env = (HASH_TABLE *)NULL;
}
}
+ if (subshell == 0 && builtin == eval_builtin)
+ {
+ if (evalnest_max > 0 && evalnest >= evalnest_max)
+ {
+ internal_error (_("eval: maximum eval nesting level exceeded (%d)"), evalnest);
+ evalnest = 0;
+ jump_to_top_level (DISCARD);
+ }
+ unwind_protect_int (evalnest);
+ /* The test for subshell == 0 above doesn't make a difference */
+ evalnest++; /* execute_subshell_builtin_or_function sets this to 0 */
+ }
+ else if (subshell == 0 && builtin == source_builtin)
+ {
+ if (sourcenest_max > 0 && sourcenest >= sourcenest_max)
+ {
+ internal_error (_("%s: maximum source nesting level exceeded (%d)"), this_command_name, sourcenest);
+ sourcenest = 0;
+ jump_to_top_level (DISCARD);
+ }
+ unwind_protect_int (sourcenest);
+ /* The test for subshell == 0 above doesn't make a difference */
+ sourcenest++; /* execute_subshell_builtin_or_function sets this to 0 */
+ }
+
/* `return' does a longjmp() back to a saved environment in execute_function.
If a variable assignment list preceded the command, and the shell is
running in POSIX mode, we need to merge that into the shell_variables
- table, since `return' is a POSIX special builtin. */
- if (posixly_correct && subshell == 0 && builtin == return_builtin && temporary_env)
+ table, since `return' is a POSIX special builtin. We don't do this if
+ it's being run by the `command' builtin, since that's supposed to inhibit
+ the special builtin properties. */
+ if (posixly_correct && subshell == 0 && builtin == return_builtin && (flags & CMD_COMMAND_BUILTIN) == 0 && temporary_env)
{
begin_unwind_frame ("return_temp_env");
add_unwind_protect (merge_temporary_env, (char *)NULL);
}
+ executing_builtin++;
+ executing_command_builtin |= builtin == command_builtin;
result = ((*builtin) (words->next));
/* This shouldn't happen, but in case `return' comes back instead of
if (eval_unwind)
{
- exit_immediately_on_error += old_e_flag;
+ builtin_ignoring_errexit = ignexit_flag;
+ exit_immediately_on_error = builtin_ignoring_errexit ? 0 : errexit_flag;
+ if (error_trap)
+ {
+ set_error_trap (error_trap);
+ free (error_trap);
+ }
discard_unwind_frame ("eval_builtin");
}
return (result);
}
+static void
+maybe_restore_getopt_state (gs)
+ sh_getopt_state_t *gs;
+{
+ /* If we have a local copy of OPTIND and it's at the right (current)
+ context, then we restore getopt's internal state. If not, we just
+ let it go. We know there is a local OPTIND if gs->gs_flags & 1.
+ This is set below in execute_function() before the context is run. */
+ if (gs->gs_flags & 1)
+ sh_getopt_restore_istate (gs);
+ else
+ free (gs);
+}
+
+#if defined (ARRAY_VARS)
+void
+restore_funcarray_state (fa)
+ struct func_array_state *fa;
+{
+ SHELL_VAR *nfv;
+ ARRAY *funcname_a;
+
+ array_pop (fa->source_a);
+ array_pop (fa->lineno_a);
+
+ GET_ARRAY_FROM_VAR ("FUNCNAME", nfv, funcname_a);
+ if (nfv == fa->funcname_v)
+ array_pop (funcname_a);
+
+ free (fa);
+}
+#endif
+
static int
execute_function (var, words, flags, fds_to_close, async, subshell)
SHELL_VAR *var;
COMMAND *tc, *fc, *save_current;
char *debug_trap, *error_trap, *return_trap;
#if defined (ARRAY_VARS)
- SHELL_VAR *funcname_v, *nfv, *bash_source_v, *bash_lineno_v;
- ARRAY *funcname_a, *bash_source_a, *bash_lineno_a;
+ SHELL_VAR *funcname_v, *bash_source_v, *bash_lineno_v;
+ ARRAY *funcname_a;
+ volatile ARRAY *bash_source_a;
+ volatile ARRAY *bash_lineno_a;
+ struct func_array_state *fa;
#endif
FUNCTION_DEF *shell_fn;
char *sfile, *t;
- static int funcnest = 0;
+ sh_getopt_state_t *gs;
+ SHELL_VAR *gv;
USE_VAR(fc);
+ if (funcnest_max > 0 && funcnest >= funcnest_max)
+ {
+ internal_error (_("%s: maximum function nesting level exceeded (%d)"), var->name, funcnest);
+ funcnest = 0; /* XXX - should we reset it somewhere else? */
+ jump_to_top_level (DISCARD);
+ }
+
#if defined (ARRAY_VARS)
GET_ARRAY_FROM_VAR ("FUNCNAME", funcname_v, funcname_a);
GET_ARRAY_FROM_VAR ("BASH_SOURCE", bash_source_v, bash_source_a);
if (tc && (flags & CMD_IGNORE_RETURN))
tc->flags |= CMD_IGNORE_RETURN;
+ gs = sh_getopt_save_istate ();
if (subshell == 0)
{
begin_unwind_frame ("function_calling");
+ /* If the shell is in posix mode, this will push the variables in
+ the temporary environment to the "current shell environment" (the
+ global scope), and dispose the temporary env before setting it to
+ NULL later. This behavior has disappeared from the latest edition
+ of the standard, so I will eventually remove it from variables.c:
+ push_var_context. */
push_context (var->name, subshell, temporary_env);
+ /* This has to be before the pop_context(), because the unwinding of
+ local variables may cause the restore of a local declaration of
+ OPTIND to force a getopts state reset. */
+ add_unwind_protect (maybe_restore_getopt_state, gs);
add_unwind_protect (pop_context, (char *)NULL);
unwind_protect_int (line_number);
+ unwind_protect_int (line_number_for_err_trap);
+ unwind_protect_int (function_line_number);
unwind_protect_int (return_catch_flag);
unwind_protect_jmp_buf (return_catch);
add_unwind_protect (dispose_command, (char *)tc);
unwind_protect_pointer (this_shell_function);
+ unwind_protect_int (funcnest);
unwind_protect_int (loop_level);
}
else
{
debug_trap = savestring (debug_trap);
add_unwind_protect (xfree, debug_trap);
- add_unwind_protect (set_debug_trap, debug_trap);
+ add_unwind_protect (maybe_set_debug_trap, debug_trap);
}
restore_default_signal (DEBUG_TRAP);
}
{
error_trap = savestring (error_trap);
add_unwind_protect (xfree, error_trap);
- add_unwind_protect (set_error_trap, error_trap);
+ add_unwind_protect (maybe_set_error_trap, error_trap);
}
restore_default_signal (ERROR_TRAP);
}
/* Shell functions inherit the RETURN trap if function tracing is on
globally or on individually for this function. */
-#if 0
- if (return_trap && ((trace_p (var) == 0) && function_trace_mode == 0))
-#else
if (return_trap && (signal_in_progress (DEBUG_TRAP) || ((trace_p (var) == 0) && function_trace_mode == 0)))
-#endif
{
if (subshell == 0)
{
return_trap = savestring (return_trap);
add_unwind_protect (xfree, return_trap);
- add_unwind_protect (set_return_trap, return_trap);
+ add_unwind_protect (maybe_set_return_trap, return_trap);
}
restore_default_signal (RETURN_TRAP);
}
/* This is quite similar to the code in shell.c and elsewhere. */
shell_fn = find_function_def (this_shell_function->name);
sfile = shell_fn ? shell_fn->source_file : "";
- array_push (funcname_a, this_shell_function->name);
+ array_push ((ARRAY *)funcname_a, this_shell_function->name);
- array_push (bash_source_a, sfile);
+ array_push ((ARRAY *)bash_source_a, sfile);
t = itos (executing_line_number ());
- array_push (bash_lineno_a, t);
+ array_push ((ARRAY *)bash_lineno_a, t);
free (t);
#endif
+#if defined (ARRAY_VARS)
+ fa = (struct func_array_state *)xmalloc (sizeof (struct func_array_state));
+ fa->source_a = (ARRAY *)bash_source_a;
+ fa->source_v = bash_source_v;
+ fa->lineno_a = (ARRAY *)bash_lineno_a;
+ fa->lineno_v = bash_lineno_v;
+ fa->funcname_a = (ARRAY *)funcname_a;
+ fa->funcname_v = funcname_v;
+ if (subshell == 0)
+ add_unwind_protect (restore_funcarray_state, fa);
+#endif
+
/* The temporary environment for a function is supposed to apply to
all commands executed within the function body. */
+ /* Initialize BASH_ARGC and BASH_ARGV before we blow away the positional
+ parameters */
+ if (debugging_mode || shell_compatibility_level <= 44)
+ init_bash_argv ();
+
remember_args (words->next, 1);
/* Update BASH_ARGV and BASH_ARGC */
if (debugging_mode)
- push_args (words->next);
+ {
+ push_args (words->next);
+ if (subshell == 0)
+ add_unwind_protect (pop_args, 0);
+ }
/* Number of the line on which the function body starts. */
line_number = function_line_number = tc->line;
stop_pipeline (async, (COMMAND *)NULL);
#endif
+ if (shell_compatibility_level > 43)
+ loop_level = 0;
+
fc = tc;
+ from_return_trap = 0;
+
return_catch_flag++;
- return_val = setjmp (return_catch);
+ return_val = setjmp_nosigs (return_catch);
if (return_val)
{
result = return_catch_value;
/* Run the RETURN trap in the function's context. */
save_current = currently_executing_command;
- run_return_trap ();
+ if (from_return_trap == 0)
+ run_return_trap ();
currently_executing_command = save_current;
}
else
showing_function_line = 0;
}
- /* Restore BASH_ARGC and BASH_ARGV */
- if (debugging_mode)
- pop_args ();
+ /* If we have a local copy of OPTIND, note it in the saved getopts state. */
+ gv = find_variable ("OPTIND");
+ if (gv && gv->context == variable_context)
+ gs->gs_flags |= 1;
if (subshell == 0)
run_unwind_frame ("function_calling");
-
- funcnest--;
#if defined (ARRAY_VARS)
- /* These two variables cannot be unset, and cannot be affected by the
- function. */
- array_pop (bash_source_a);
- array_pop (bash_lineno_a);
-
- /* FUNCNAME can be unset, and so can potentially be changed by the
- function. */
- GET_ARRAY_FROM_VAR ("FUNCNAME", nfv, funcname_a);
- if (nfv == funcname_v)
- array_pop (funcname_a);
+ else
+ {
+ restore_funcarray_state (fa);
+ /* Restore BASH_ARGC and BASH_ARGV */
+ if (debugging_mode)
+ pop_args ();
+ }
#endif
-
+
if (variable_context == 0 || this_shell_function == 0)
- make_funcname_visible (0);
+ {
+ make_funcname_visible (0);
+#if defined (PROCESS_SUBSTITUTION)
+ unlink_fifo_list ();
+#endif
+ }
return (result);
}
struct fd_bitmap *fds_to_close;
int flags;
{
- int result, r;
+ int result, r, funcvalue;
#if defined (JOB_CONTROL)
int jobs_hack;
/* A subshell is neither a login shell nor interactive. */
login_shell = interactive = 0;
+ if (builtin == eval_builtin)
+ evalnest = 0;
+ else if (builtin == source_builtin)
+ sourcenest = 0;
- subshell_environment = SUBSHELL_ASYNC;
+ if (async)
+ subshell_environment |= SUBSHELL_ASYNC;
+ if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
+ subshell_environment |= SUBSHELL_PIPE;
maybe_make_export_env (); /* XXX - is this needed? */
without_job_control ();
set_sigchld_handler ();
+#else
+ without_job_control ();
#endif /* JOB_CONTROL */
set_sigint_handler ();
{
/* 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_nosigs (return_catch);
if (result == EXITPROG)
exit (last_command_exit_value);
else if (result)
exit (EXECUTION_FAILURE);
+ else if (funcvalue)
+ exit (return_catch_value);
else
{
r = execute_builtin (builtin, words, flags, 1);
+ fflush (stdout);
if (r == EX_USAGE)
r = EX_BADUSAGE;
- exit (r);
+ /* XXX - experimental */
+ else if (r == EX_DISKFALLBACK)
+ {
+ char *command_line;
+
+ command_line = savestring (the_printed_command_except_trap ? the_printed_command_except_trap : "");
+ r = execute_disk_command (words, (REDIRECT *)0, command_line,
+ -1, -1, async, (struct fd_bitmap *)0, flags|CMD_NO_FORK);
+ }
+ sh_exit (r);
}
}
else
- exit (execute_function (var, words, flags, fds_to_close, async, 1));
+ {
+ r = execute_function (var, words, flags, fds_to_close, async, 1);
+ fflush (stdout);
+ sh_exit (r);
+ }
}
/* Execute a builtin or function in the current shell context. If BUILTIN
{
int result;
REDIRECT *saved_undo_list;
- sh_builtin_func_t *saved_this_shell_builtin;
+#if defined (PROCESS_SUBSTITUTION)
+ int ofifo, nfifo, osize;
+ char *ofifo_list;
+#endif
+
+#if defined (PROCESS_SUBSTITUTION)
+ begin_unwind_frame ("saved_fifos");
+ /* If we return, we longjmp and don't get a chance to restore the old
+ fifo list, so we add an unwind protect to free it */
+ ofifo = num_fifos ();
+ ofifo_list = copy_fifo_list (&osize);
+ if (ofifo_list)
+ add_unwind_protect (xfree, ofifo_list);
+#endif
if (do_redirections (redirects, RX_ACTIVE|RX_UNDOABLE) != 0)
{
- cleanup_redirects (redirection_undo_list);
- redirection_undo_list = (REDIRECT *)NULL;
+ undo_partial_redirects ();
dispose_exec_redirects ();
+#if defined (PROCESS_SUBSTITUTION)
+ free (ofifo_list);
+#endif
return (EX_REDIRFAIL); /* was EXECUTION_FAILURE */
}
- saved_this_shell_builtin = this_shell_builtin;
saved_undo_list = redirection_undo_list;
/* Calling the "exec" builtin changes redirections forever. */
if (saved_undo_list)
{
- begin_unwind_frame ("saved redirects");
+ begin_unwind_frame ("saved-redirects");
add_unwind_protect (cleanup_redirects, (char *)saved_undo_list);
}
result = execute_function (var, words, flags, fds_to_close, 0, 0);
/* We do this before undoing the effects of any redirections. */
+ fflush (stdout);
+ fpurge (stdout);
if (ferror (stdout))
clearerr (stdout);
and preserve the redirections. */
if (builtin == command_builtin && this_shell_builtin == exec_builtin)
{
+ int discard;
+
+ discard = 0;
if (saved_undo_list)
- dispose_redirects (saved_undo_list);
+ {
+ dispose_redirects (saved_undo_list);
+ discard = 1;
+ }
redirection_undo_list = exec_redirection_undo_list;
saved_undo_list = exec_redirection_undo_list = (REDIRECT *)NULL;
- discard_unwind_frame ("saved_redirects");
+ if (discard)
+ discard_unwind_frame ("saved-redirects");
}
if (saved_undo_list)
{
redirection_undo_list = saved_undo_list;
- discard_unwind_frame ("saved redirects");
+ discard_unwind_frame ("saved-redirects");
}
- if (redirection_undo_list)
- {
- cleanup_redirects (redirection_undo_list);
- redirection_undo_list = (REDIRECT *)NULL;
- }
+ undo_partial_redirects ();
+
+#if defined (PROCESS_SUBSTITUTION)
+ /* Close any FIFOs created by this builtin or function. */
+ nfifo = num_fifos ();
+ if (nfifo > ofifo)
+ close_new_fifos (ofifo_list, osize);
+ if (ofifo_list)
+ free (ofifo_list);
+ discard_unwind_frame ("saved_fifos");
+#endif
return (result);
}
if (job_control == 0)
#endif
{
+ /* Make sure we get the original signal dispositions now so we don't
+ confuse the trap builtin later if the subshell tries to use it to
+ reset SIGINT/SIGQUIT. Don't call set_signal_ignored; that sets
+ the value of original_signals to SIG_IGN. Posix interpretation 751. */
+ get_original_signal (SIGINT);
set_signal_handler (SIGINT, SIG_IGN);
- set_signal_ignored (SIGINT);
+
+ get_original_signal (SIGQUIT);
set_signal_handler (SIGQUIT, SIG_IGN);
- set_signal_ignored (SIGQUIT);
}
}
this gnarly hair, for no good reason.
NOTE: callers expect this to fork or exit(). */
-static void
+
+/* Name of a shell function to call when a command name is not found. */
+#ifndef NOTFOUND_HOOK
+# define NOTFOUND_HOOK "command_not_found_handle"
+#endif
+
+static int
execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
async, fds_to_close, cmdflags)
WORD_LIST *words;
struct fd_bitmap *fds_to_close;
int cmdflags;
{
- char *pathname, *command, **args;
- int nofork;
+ char *pathname, *command, **args, *p;
+ int nofork, stdpath, result;
pid_t pid;
+ SHELL_VAR *hookf;
+ WORD_LIST *wl;
- nofork = (cmdflags & CMD_NO_FORK); /* Don't fork, just exec, if no pipes */
+ stdpath = (cmdflags & CMD_STDPATH); /* use command -p path */
+ nofork = (cmdflags & CMD_NO_FORK); /* Don't fork, just exec, if no pipes */
pathname = words->word->word;
+ p = 0;
+ result = EXECUTION_SUCCESS;
#if defined (RESTRICTED_SHELL)
command = (char *)NULL;
- if (restricted && xstrchr (pathname, '/'))
+ if (restricted && mbschr (pathname, '/'))
{
internal_error (_("%s: restricted: cannot specify `/' in command names"),
pathname);
- last_command_exit_value = EXECUTION_FAILURE;
+ result = last_command_exit_value = EXECUTION_FAILURE;
/* If we're not going to fork below, we must already be in a child
process or a context in which it's safe to call exit(2). */
}
#endif /* RESTRICTED_SHELL */
- command = search_for_command (pathname);
+ command = search_for_command (pathname, CMDSRCH_HASH|(stdpath ? CMDSRCH_STDPATH : 0));
+ QUIT;
if (command)
{
+ /* If we're optimizing out the fork (implicit `exec'), decrement the
+ shell level like `exec' would do. */
+ if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE)
+ adjust_shell_level (-1);
+
maybe_make_export_env ();
put_command_name_into_env (command);
}
if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE)
pid = 0;
else
- pid = make_child (savestring (command_line), async);
+ pid = make_child (p = savestring (command_line), async);
if (pid == 0)
{
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;
+
+#if defined (JOB_CONTROL)
+ FREE (p);
+#endif
+
/* restore_original_signals may have undone the work done
by make_child to ensure that SIGINT and SIGQUIT are ignored
in asynchronous children. */
if (async)
interactive = 0;
- subshell_environment = SUBSHELL_FORK;
+ subshell_environment |= SUBSHELL_FORK; /* XXX - was just = */
if (redirects && (do_redirections (redirects, RX_ACTIVE) != 0))
{
if (command == 0)
{
- internal_error (_("%s: command not found"), pathname);
- exit (EX_NOTFOUND); /* Posix.2 says the exit status is 127 */
+ hookf = find_function (NOTFOUND_HOOK);
+ if (hookf == 0)
+ {
+ /* Make sure filenames are displayed using printable characters */
+ pathname = printable_filename (pathname, 0);
+ internal_error (_("%s: command not found"), pathname);
+ exit (EX_NOTFOUND); /* Posix.2 says the exit status is 127 */
+ }
+
+ /* We don't want to manage process groups for processes we start
+ from here, so we turn off job control and don't attempt to
+ manipulate the terminal's process group. */
+ without_job_control ();
+
+#if defined (JOB_CONTROL)
+ set_sigchld_handler ();
+#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. */
else
{
parent_return:
+ QUIT;
+
/* Make sure that the pipes are closed in the parent. */
close_pipes (pipe_in, pipe_out);
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
- unlink_fifo_list ();
+#if 0
+ if (variable_context == 0)
+ unlink_fifo_list ();
+#endif
#endif
FREE (command);
+ return (result);
}
}
history_lines_this_session = 0;
#endif
-#if defined (JOB_CONTROL)
/* Forget about the way job control was working. We are in a subshell. */
without_job_control ();
+
+#if defined (JOB_CONTROL)
set_sigchld_handler ();
init_job_stats ();
#endif /* JOB_CONTROL */
shell_variables = shell_variables->down;
clear_unwind_protect_list (0);
+ /* XXX -- are there other things we should be resetting here? */
+ parse_and_execute_level = 0; /* nothing left to restore it */
/* We're no longer inside a shell function. */
- variable_context = return_catch_flag = 0;
+ variable_context = return_catch_flag = funcnest = evalnest = sourcenest = 0;
+
+ executing_list = 0; /* XXX */
/* If we're not interactive, close the file descriptor from which we're
reading the current shell script. */
# define SETOSTYPE(x)
#endif
+#define HASH_BANG_BUFSIZ 128
+
#define READ_SAMPLE_BUF(file, buf, len) \
do \
{ \
fd = open(file, O_RDONLY); \
if (fd >= 0) \
{ \
- len = read (fd, buf, 80); \
+ len = read (fd, buf, HASH_BANG_BUFSIZ); \
close (fd); \
} \
else \
char *command;
char **args, **env;
{
- struct stat finfo;
int larray, i, fd;
- char sample[80];
+ char sample[HASH_BANG_BUFSIZ];
int sample_len;
SETOSTYPE (0); /* Some systems use for USG/POSIX semantics */
execve (command, args, env);
i = errno; /* error from execve() */
+ CHECK_TERMSIG;
SETOSTYPE (1);
/* If we get to this point, then start checking out the file.
Maybe it is something we can hack ourselves. */
if (i != ENOEXEC)
{
- if ((stat (command, &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
+ /* make sure this is set correctly for file_error/report_error */
+ last_command_exit_value = (i == ENOENT) ? EX_NOTFOUND : EX_NOEXEC; /* XXX Posix.2 says that exit status is 126 */
+ if (file_isdir (command))
+#if defined (EISDIR)
+ internal_error (_("%s: %s"), command, strerror (EISDIR));
+#else
internal_error (_("%s: is a directory"), command);
+#endif
else if (executable_file (command) == 0)
{
errno = i;
run it for some reason. See why. */
#if defined (HAVE_HASH_BANG_EXEC)
READ_SAMPLE_BUF (command, sample, sample_len);
+ if (sample_len > 0)
+ sample[sample_len - 1] = '\0';
if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
{
char *interp;
errno = i;
file_error (command);
}
- return ((i == ENOENT) ? EX_NOTFOUND : EX_NOEXEC); /* XXX Posix.2 says that exit status is 126 */
+ return (last_command_exit_value);
}
/* This file is executable.
#endif
if (check_binary_file (sample, sample_len))
{
- internal_error (_("%s: cannot execute binary file"), command);
+ internal_error (_("%s: cannot execute binary file: %s"), command, strerror (i));
+ errno = i;
return (EX_BINARY_FILE);
}
}
/* We have committed to attempting to execute the contents of this file
as shell commands. */
+ reset_parser ();
initialize_subshell ();
set_sigint_handler ();
unbind_args (); /* remove the positional parameters */
- longjmp (subshell_top_level, 1);
+#if defined (PROCESS_SUBSTITUTION)
+ clear_fifo_list (); /* pipe fds are what they are now */
+#endif
+
+ sh_longjmp (subshell_top_level, 1);
/*NOTREACHED*/
}
static int
-execute_intern_function (name, function)
+execute_intern_function (name, funcdef)
WORD_DESC *name;
- COMMAND *function;
+ FUNCTION_DEF *funcdef;
{
SHELL_VAR *var;
+ char *t;
if (check_identifier (name, posixly_correct) == 0)
{
if (posixly_correct && interactive_shell == 0)
{
- last_command_exit_value = EX_USAGE;
+ last_command_exit_value = EX_BADUSAGE;
jump_to_top_level (ERREXIT);
}
return (EXECUTION_FAILURE);
}
+ if (strchr (name->word, CTLESC)) /* WHY? */
+ {
+ t = dequote_escapes (name->word);
+ free (name->word);
+ name->word = t;
+ }
+
+ /* Posix interpretation 383 */
+ if (posixly_correct && find_special_builtin (name->word))
+ {
+ internal_error (_("`%s': is a special builtin"), name->word);
+ last_command_exit_value = EX_BADUSAGE;
+ jump_to_top_level (interactive_shell ? DISCARD : ERREXIT);
+ }
+
var = find_function (name->word);
if (var && (readonly_p (var) || noassign_p (var)))
{
return (EXECUTION_FAILURE);
}
- bind_function (name->word, function);
+#if defined (DEBUGGER)
+ bind_function_def (name->word, funcdef, 1);
+#endif
+
+ bind_function (name->word, funcdef->command);
return (EXECUTION_SUCCESS);
}
dup_error (pipe_in, 0);
if (pipe_in > 0)
close (pipe_in);
+#ifdef __CYGWIN__
+ /* Let stdio know the fd may have changed from text to binary mode. */
+ freopen (NULL, "r", stdin);
+#endif /* __CYGWIN__ */
}
if (pipe_out != NO_PIPE)
{
if (dup2 (1, 2) < 0)
dup_error (1, 2);
}
+#ifdef __CYGWIN__
+ /* Let stdio know the fd may have changed from text to binary mode, and
+ make sure to preserve stdout line buffering. */
+ freopen (NULL, "w", stdout);
+ sh_setlinebuf (stdout);
+#endif /* __CYGWIN__ */
}
}