This file is part of GNU Bash, the Bourne Again SHell.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
#pragma alloca
#endif
#include <stdio.h>
#include "chartypes.h"
#include "bashtypes.h"
#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
# include <sys/file.h>
#endif
#include "filecntl.h"
#include "posixstat.h"
#include <signal.h>
#ifndef _MINIX
# include <sys/param.h>
#endif
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "posixtime.h"
#if defined (HAVE_SYS_RESOURCE_H) && !defined (RLIMTYPE)
# include <sys/resource.h>
#endif
#if defined (HAVE_SYS_TIMES_H) && defined (HAVE_TIMES)
# include <sys/times.h>
#endif
#include <errno.h>
#if !defined (errno)
extern int errno;
#endif
#include "bashansi.h"
#include "bashintl.h"
#include "memalloc.h"
#include "shell.h"
#include <y.tab.h>
#include "flags.h"
#include "builtins.h"
#include "hashlib.h"
#include "jobs.h"
#include "execute_cmd.h"
#include "findcmd.h"
#include "redir.h"
#include "trap.h"
#include "pathexp.h"
#include "hashcmd.h"
#if defined (COND_COMMAND)
# include "test.h"
#endif
#include "builtins/common.h"
#include "builtins/builtext.h"
#include <glob/strmatch.h>
#include <tilde/tilde.h>
#if defined (BUFFERED_INPUT)
# include "input.h"
#endif
#if defined (ALIAS)
# include "alias.h"
#endif
#if defined (HISTORY)
# include "bashhist.h"
#endif
extern int posixly_correct;
extern int expand_aliases;
extern int autocd;
extern int breaking, continuing, loop_level;
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 0
extern char *glob_argv_flags;
#endif
extern int close __P((int));
static void close_pipes __P((int, int));
static void do_piping __P((int, int));
static void bind_lastarg __P((char *));
static int shell_control_structure __P((enum command_type));
static void cleanup_redirects __P((REDIRECT *));
#if defined (JOB_CONTROL)
static int restore_signal_mask __P((sigset_t *));
#endif
static void async_redirect_stdin __P((void));
static int builtin_status __P((int));
static int execute_for_command __P((FOR_COM *));
#if defined (SELECT_COMMAND)
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));
static char *select_query __P((WORD_LIST *, int, char *, int));
static int execute_select_command __P((SELECT_COM *));
#endif
#if defined (DPAREN_ARITHMETIC)
static int execute_arith_command __P((ARITH_COM *));
#endif
#if defined (COND_COMMAND)
static int execute_cond_node __P((COND_COM *));
static int execute_cond_command __P((COND_COM *));
#endif
#if defined (COMMAND_TIMING)
static int mkfmt __P((char *, int, int, time_t, int));
static void print_formatted_time __P((FILE *, char *,
time_t, int, time_t, int,
time_t, int, int));
static int time_command __P((COMMAND *, int, int, int, struct fd_bitmap *));
#endif
#if defined (ARITH_FOR_COMMAND)
static intmax_t eval_arith_for_expr __P((WORD_LIST *, int *));
static int execute_arith_for_command __P((ARITH_FOR_COM *));
#endif
static int execute_case_command __P((CASE_COM *));
static int execute_while_command __P((WHILE_COM *));
static int execute_until_command __P((WHILE_COM *));
static int execute_while_or_until __P((WHILE_COM *, int));
static int execute_if_command __P((IF_COM *));
static int execute_null_command __P((REDIRECT *, int, int, int));
static void fix_assignment_words __P((WORD_LIST *));
static int execute_simple_command __P((SIMPLE_COM *, int, int, int, struct fd_bitmap *));
static int execute_builtin __P((sh_builtin_func_t *, WORD_LIST *, int, int));
static int execute_function __P((SHELL_VAR *, WORD_LIST *, int, struct fd_bitmap *, int, int));
static int execute_builtin_or_function __P((WORD_LIST *, sh_builtin_func_t *,
SHELL_VAR *,
REDIRECT *, struct fd_bitmap *, int));
static void execute_subshell_builtin_or_function __P((WORD_LIST *, REDIRECT *,
sh_builtin_func_t *,
SHELL_VAR *,
int, int, int,
struct fd_bitmap *,
int));
static void 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 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 *));
so that reader_loop can set it to zero before executing a command. */
int stdin_redir;
`test' needs this, for example. */
char *this_command_name;
the_printed_command), except when a trap is being executed. Useful for
a debugger to know where exactly the program is currently executing. */
char *the_printed_command_except_trap;
int return_catch_flag;
int return_catch_value;
procenv_t return_catch;
int last_command_exit_value;
was terminated by a signal, and, if so, which one. */
int last_command_exit_signal;
that I made in the shell. */
REDIRECT *redirection_undo_list = (REDIRECT *)NULL;
redirections performed by the `exec' builtin. These are redirections
that must be undone even when exec discards redirection_undo_list. */
REDIRECT *exec_redirection_undo_list = (REDIRECT *)NULL;
currently executing (e.g. `eval echo a' would have it set to 2). */
int executing_builtin = 0;
int executing_list = 0;
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;
environment. */
int subshell_environment;
int subshell_level = 0;
SHELL_VAR *this_shell_function;
int match_ignore_case = 0;
struct stat SB;
static int special_builtin_failed;
static COMMAND *currently_executing_command;
static int function_line_number;
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 line_number_for_err_trap;
struct fd_bitmap *current_fds_to_close = (struct fd_bitmap *)NULL;
#define FD_BITMAP_DEFAULT_SIZE 32
information from the shell to its children about file descriptors
to close. */
struct fd_bitmap *
new_fd_bitmap (size)
int size;
{
struct fd_bitmap *ret;
ret = (struct fd_bitmap *)xmalloc (sizeof (struct fd_bitmap));
ret->size = size;
if (size)
{
ret->bitmap = (char *)xmalloc (size);
memset (ret->bitmap, '\0', size);
}
else
ret->bitmap = (char *)NULL;
return (ret);
}
void
dispose_fd_bitmap (fdbp)
struct fd_bitmap *fdbp;
{
FREE (fdbp->bitmap);
free (fdbp);
}
void
close_fd_bitmap (fdbp)
struct fd_bitmap *fdbp;
{
register int i;
if (fdbp)
{
for (i = 0; i < fdbp->size; i++)
if (fdbp->bitmap[i])
{
close (i);
fdbp->bitmap[i] = 0;
}
}
}
int
executing_line_number ()
{
if (executing && showing_function_line == 0 &&
(variable_context == 0 || interactive_shell == 0) &&
currently_executing_command)
{
#if defined (COND_COMMAND)
if (currently_executing_command->type == cm_cond)
return currently_executing_command->value.Cond->line;
#endif
#if defined (DPAREN_ARITHMETIC)
else 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)
return currently_executing_command->value.ArithFor->line;
#endif
return line_number;
}
else
return line_number;
}
read_command () places into GLOBAL_COMMAND. See "command.h" for the
details of the command structure.
EXECUTION_SUCCESS or EXECUTION_FAILURE are the only possible
return values. Executing a command with nothing in it returns
EXECUTION_SUCCESS. */
int
execute_command (command)
COMMAND *command;
{
struct fd_bitmap *bitmap;
int result;
current_fds_to_close = (struct fd_bitmap *)NULL;
bitmap = new_fd_bitmap (FD_BITMAP_DEFAULT_SIZE);
begin_unwind_frame ("execute-command");
add_unwind_protect (dispose_fd_bitmap, (char *)bitmap);
result = execute_command_internal (command, 0, NO_PIPE, NO_PIPE, bitmap);
dispose_fd_bitmap (bitmap);
discard_unwind_frame ("execute-command");
#if defined (PROCESS_SUBSTITUTION)
returns. */
if (variable_context == 0)
unlink_fifo_list ();
#endif
QUIT;
return (result);
}
static int
shell_control_structure (type)
enum command_type type;
{
switch (type)
{
#if defined (ARITH_FOR_COMMAND)
case cm_arith_for:
#endif
#if defined (SELECT_COMMAND)
case cm_select:
#endif
#if defined (DPAREN_ARITHMETIC)
case cm_arith:
#endif
#if defined (COND_COMMAND)
case cm_cond:
#endif
case cm_case:
case cm_while:
case cm_until:
case cm_if:
case cm_for:
case cm_group:
case cm_function_def:
return (1);
default:
return (0);
}
}
for loops. */
static void
cleanup_redirects (list)
REDIRECT *list;
{
do_redirections (list, RX_ACTIVE);
dispose_redirects (list);
}
#if 0
static void
cleanup_func_redirects (list)
REDIRECT *list;
{
do_redirections (list, RX_ACTIVE);
}
#endif
void
dispose_exec_redirects ()
{
if (exec_redirection_undo_list)
{
dispose_redirects (exec_redirection_undo_list);
exec_redirection_undo_list = (REDIRECT *)NULL;
}
}
#if defined (JOB_CONTROL)
is interrupted or errors occur while creating a pipeline. */
static int
restore_signal_mask (set)
sigset_t *set;
{
return (sigprocmask (SIG_SETMASK, set, (sigset_t *)NULL));
}
#endif
#ifdef DEBUG
void
open_files ()
{
register int i;
int f, fd_table_size;
fd_table_size = getdtablesize ();
fprintf (stderr, "pid %ld open files:", (long)getpid ());
for (i = 3; i < fd_table_size; i++)
{
if ((f = fcntl (i, F_GETFD, 0)) != -1)
fprintf (stderr, " %d (%s)", i, f ? "close" : "open");
}
fprintf (stderr, "\n");
}
#endif
static void
async_redirect_stdin ()
{
int fd;
fd = open ("/dev/null", O_RDONLY);
if (fd > 0)
{
dup2 (fd, 0);
close (fd);
}
else if (fd < 0)
internal_error (_("cannot redirect standard input from /dev/null: %s"), strerror (errno));
}
#define DESCRIBE_PID(pid) do { if (interactive) describe_pid (pid); } while (0)
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
from and where it goes. They can have the value of NO_PIPE, which means
I/O is stdin/stdout.
FDS_TO_CLOSE is a list of file descriptors to close once the child has
been forked. This list often contains the unusable sides of pipes, etc.
EXECUTION_SUCCESS or EXECUTION_FAILURE are the only possible
return values. Executing a command with nothing in it returns
EXECUTION_SUCCESS. */
int
execute_command_internal (command, asynchronous, pipe_in, pipe_out,
fds_to_close)
COMMAND *command;
int asynchronous;
int pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
int exec_result, user_subshell, invert, ignore_return, was_error_trap;
REDIRECT *my_undo_list, *exec_undo_list;
volatile int last_pid;
volatile int save_line_number;
#if 0
if (command == 0 || breaking || continuing || read_but_dont_execute)
return (EXECUTION_SUCCESS);
#else
if (breaking || continuing)
return (last_command_exit_value);
if (command == 0 || read_but_dont_execute)
return (EXECUTION_SUCCESS);
#endif
QUIT;
run_pending_traps ();
#if 0
if (running_trap == 0)
#endif
currently_executing_command = command;
invert = (command->flags & CMD_INVERT_RETURN) != 0;
we don't want a failing command to inadvertently cause the shell
to exit. */
if (exit_immediately_on_error && invert)
command->flags |= CMD_IGNORE_RETURN;
exec_result = EXECUTION_SUCCESS;
a shell control-structure, and it has a pipe, then we do the command
in a subshell. */
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 (execute_coproc (command, pipe_in, pipe_out, fds_to_close));
#endif
user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0);
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;
control and call execute_command () on the command again. */
line_number_for_err_trap = line_number;
paren_pid = make_child (savestring (make_command_string (command)),
asynchronous);
if (paren_pid == 0)
exit (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
else
{
close_pipes (pipe_in, pipe_out);
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
unlink_fifo_list ();
#endif
then we should simply return and let the last command in the
pipe be waited for. If we are not in a pipeline, or are the
last command in the pipeline, then we wait for the subshell
and return its exit status as usual. */
if (pipe_out != NO_PIPE)
return (EXECUTION_SUCCESS);
stop_pipeline (asynchronous, (COMMAND *)NULL);
if (asynchronous == 0)
{
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;
last_command_exit_value = wait_for (paren_pid);
if (invert)
exec_result = ((last_command_exit_value == EXECUTION_SUCCESS)
? EXECUTION_FAILURE
: EXECUTION_SUCCESS);
else
exec_result = last_command_exit_value;
if (user_subshell && 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 (user_subshell && 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);
}
return (last_command_exit_value = exec_result);
}
else
{
DESCRIBE_PID (paren_pid);
run_pending_traps ();
return (EXECUTION_SUCCESS);
}
}
}
#if defined (COMMAND_TIMING)
if (command->flags & CMD_TIME_PIPELINE)
{
if (asynchronous)
{
command->flags |= CMD_FORCE_SUBSHELL;
exec_result = execute_command_internal (command, 1, pipe_in, pipe_out, fds_to_close);
}
else
{
exec_result = time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close);
#if 0
if (running_trap == 0)
#endif
currently_executing_command = (COMMAND *)NULL;
}
return (exec_result);
}
#endif
if (shell_control_structure (command->type) && command->redirects)
stdin_redir = stdin_redirects (command->redirects);
redirection.) */
if (do_redirections (command->redirects, RX_ACTIVE|RX_UNDOABLE) != 0)
{
cleanup_redirects (redirection_undo_list);
redirection_undo_list = (REDIRECT *)NULL;
dispose_exec_redirects ();
return (last_command_exit_value = EXECUTION_FAILURE);
}
if (redirection_undo_list)
{
my_undo_list = (REDIRECT *)copy_redirects (redirection_undo_list);
dispose_redirects (redirection_undo_list);
redirection_undo_list = (REDIRECT *)NULL;
}
else
my_undo_list = (REDIRECT *)NULL;
if (exec_redirection_undo_list)
{
exec_undo_list = (REDIRECT *)copy_redirects (exec_redirection_undo_list);
dispose_redirects (exec_redirection_undo_list);
exec_redirection_undo_list = (REDIRECT *)NULL;
}
else
exec_undo_list = (REDIRECT *)NULL;
if (my_undo_list || exec_undo_list)
begin_unwind_frame ("loop_redirections");
if (my_undo_list)
add_unwind_protect ((Function *)cleanup_redirects, my_undo_list);
if (exec_undo_list)
add_unwind_protect ((Function *)dispose_redirects, exec_undo_list);
ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
QUIT;
switch (command->type)
{
case cm_simple:
{
save_line_number = line_number;
call to execute_simple_command if a longjmp occurs as the
result of a `return' builtin. This is true for sure with gcc. */
#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)
command->value.Simple->flags |= CMD_IGNORE_RETURN;
if (command->flags & CMD_STDIN_REDIR)
command->value.Simple->flags |= CMD_STDIN_REDIR;
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);
line_number = save_line_number;
command immediately following its definition. */
dispose_used_env_vars ();
#if (defined (ultrix) && defined (mips)) || defined (C_ALLOCA)
may be using the alloca emulation code. */
(void) alloca (0);
#endif
the child. */
when the shell is compiled without job control. */
if (already_making_children && pipe_out == NO_PIPE &&
last_made_pid != last_pid)
{
stop_pipeline (asynchronous, (COMMAND *)NULL);
if (asynchronous)
{
DESCRIBE_PID (last_made_pid);
}
else
#if !defined (JOB_CONTROL)
startup files. */
if (last_made_pid != last_asynchronous_pid)
#endif
commands, this causes the last simple command in
the function to be waited for twice. This also causes
subshells forked to execute builtin commands (e.g., in
pipelines) to be waited for twice. */
exec_result = wait_for (last_made_pid);
}
}
only the failure of a simple command. */
if (was_error_trap && ignore_return == 0 && invert == 0 && pipe_in == NO_PIPE && pipe_out == NO_PIPE && 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 && pipe_in == NO_PIPE && pipe_out == NO_PIPE && exec_result != EXECUTION_SUCCESS)))
{
last_command_exit_value = exec_result;
run_pending_traps ();
jump_to_top_level (ERREXIT);
}
break;
case cm_for:
if (ignore_return)
command->value.For->flags |= CMD_IGNORE_RETURN;
exec_result = execute_for_command (command->value.For);
break;
#if defined (ARITH_FOR_COMMAND)
case cm_arith_for:
if (ignore_return)
command->value.ArithFor->flags |= CMD_IGNORE_RETURN;
exec_result = execute_arith_for_command (command->value.ArithFor);
break;
#endif
#if defined (SELECT_COMMAND)
case cm_select:
if (ignore_return)
command->value.Select->flags |= CMD_IGNORE_RETURN;
exec_result = execute_select_command (command->value.Select);
break;
#endif
case cm_case:
if (ignore_return)
command->value.Case->flags |= CMD_IGNORE_RETURN;
exec_result = execute_case_command (command->value.Case);
break;
case cm_while:
if (ignore_return)
command->value.While->flags |= CMD_IGNORE_RETURN;
exec_result = execute_while_command (command->value.While);
break;
case cm_until:
if (ignore_return)
command->value.While->flags |= CMD_IGNORE_RETURN;
exec_result = execute_until_command (command->value.While);
break;
case cm_if:
if (ignore_return)
command->value.If->flags |= CMD_IGNORE_RETURN;
exec_result = execute_if_command (command->value.If);
break;
case cm_group:
'{}' command, or via a function call. If we are executed via a
function call, we have already taken care of the function being
executed in the background (down there in execute_simple_command ()),
and this command should *not* be marked as asynchronous. If we
are executing a regular '{}' group command, and asynchronous == 1,
we must want to execute the whole command in the background, so we
need a subshell, and we want the stuff executed in that subshell
(this group command) to be executed in the foreground of that
subshell (i.e. there will not be *another* subshell forked).
What we do is to force a subshell if asynchronous, and then call
execute_command_internal again with asynchronous still set to 1,
but with the original group command, so the printed command will
look right.
The code above that handles forking off subshells will note that
both subshell and async are on, and turn off async in the child
after forking the subshell (but leave async set in the parent, so
the normal call to describe_pid is made). This turning off
async is *crucial*; if it is not done, this will fall into an
infinite loop of executions through this spot in subshell after
subshell until the process limit is exhausted. */
if (asynchronous)
{
command->flags |= CMD_FORCE_SUBSHELL;
exec_result =
execute_command_internal (command, 1, pipe_in, pipe_out,
fds_to_close);
}
else
{
if (ignore_return && command->value.Group->command)
command->value.Group->command->flags |= CMD_IGNORE_RETURN;
exec_result =
execute_command_internal (command->value.Group->command,
asynchronous, pipe_in, pipe_out,
fds_to_close);
}
break;
case cm_connection:
exec_result = execute_connection (command, asynchronous,
pipe_in, pipe_out, fds_to_close);
break;
#if defined (DPAREN_ARITHMETIC)
case cm_arith:
if (ignore_return)
command->value.Arith->flags |= CMD_IGNORE_RETURN;
exec_result = execute_arith_command (command->value.Arith);
break;
#endif
#if defined (COND_COMMAND)
case cm_cond:
if (ignore_return)
command->value.Cond->flags |= CMD_IGNORE_RETURN;
save_line_number = line_number;
exec_result = execute_cond_command (command->value.Cond);
line_number = save_line_number;
break;
#endif
case cm_function_def:
exec_result = execute_intern_function (command->value.Function_def->name,
command->value.Function_def->command);
break;
default:
command_error ("execute_command", CMDERR_BADTYPE, command->type, 0);
}
if (my_undo_list)
{
do_redirections (my_undo_list, RX_ACTIVE);
dispose_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 (invert)
exec_result = (exec_result == EXECUTION_SUCCESS)
? EXECUTION_FAILURE
: EXECUTION_SUCCESS;
#if defined (DPAREN_ARITHMETIC) || defined (COND_COMMAND)
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;
}
#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);
}
#if defined (COMMAND_TIMING)
#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
extern struct timeval *difftimeval __P((struct timeval *, struct timeval *, struct timeval *));
extern struct timeval *addtimeval __P((struct timeval *, struct timeval *, struct timeval *));
extern int timeval_to_cpu __P((struct timeval *, struct timeval *, struct timeval *));
#endif
#define POSIX_TIMEFORMAT "real %2R\nuser %2U\nsys %2S"
#define BASH_TIMEFORMAT "\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS"
static const int precs[] = { 0, 100, 10, 1 };
static int
mkfmt (buf, prec, lng, sec, sec_fraction)
char *buf;
int prec, lng;
time_t sec;
int sec_fraction;
{
time_t min;
char abuf[INT_STRLEN_BOUND(time_t) + 1];
int ind, aind;
ind = 0;
abuf[sizeof(abuf) - 1] = '\0';
if (lng)
{
min = sec / 60;
sec %= 60;
aind = sizeof(abuf) - 2;
do
abuf[aind--] = (min % 10) + '0';
while (min /= 10);
aind++;
while (abuf[aind])
buf[ind++] = abuf[aind++];
buf[ind++] = 'm';
}
aind = sizeof (abuf) - 2;
do
abuf[aind--] = (sec % 10) + '0';
while (sec /= 10);
aind++;
while (abuf[aind])
buf[ind++] = abuf[aind++];
nonzero. PREC is not greater than 3. SEC_FRACTION is between 0
and 999. */
if (prec != 0)
{
buf[ind++] = '.';
for (aind = 1; aind <= prec; aind++)
{
buf[ind++] = (sec_fraction / precs[aind]) + '0';
sec_fraction %= precs[aind];
}
}
if (lng)
buf[ind++] = 's';
buf[ind] = '\0';
return (ind);
}
sequences:
%[prec][l][RUS]
where the optional `prec' is a precision, meaning the number of
characters after the decimal point, the optional `l' means to format
using minutes and seconds (MMmNN[.FF]s), like the `times' builtin',
and the last character is one of
R number of seconds of `real' time
U number of seconds of `user' time
S number of seconds of `system' time
An occurrence of `%%' in the format string is translated to a `%'. The
result is printed to FP, a pointer to a FILE. The other variables are
the seconds and thousandths of a second of real, user, and system time,
resectively. */
static void
print_formatted_time (fp, format, rs, rsf, us, usf, ss, ssf, cpu)
FILE *fp;
char *format;
time_t rs;
int rsf;
time_t us;
int usf;
time_t ss;
int ssf, cpu;
{
int prec, lng, len;
char *str, *s, ts[INT_STRLEN_BOUND (time_t) + sizeof ("mSS.FFFF")];
time_t sum;
int sum_frac;
int sindex, ssize;
len = strlen (format);
ssize = (len + 64) - (len % 64);
str = (char *)xmalloc (ssize);
sindex = 0;
for (s = format; *s; s++)
{
if (*s != '%' || s[1] == '\0')
{
RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64);
str[sindex++] = *s;
}
else if (s[1] == '%')
{
s++;
RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64);
str[sindex++] = *s;
}
else if (s[1] == 'P')
{
s++;
#if 0
if (cpu > 10000)
cpu = 10000;
#endif
sum = cpu / 100;
sum_frac = (cpu % 100) * 10;
len = mkfmt (ts, 2, 0, sum, sum_frac);
RESIZE_MALLOCED_BUFFER (str, sindex, len, ssize, 64);
strcpy (str + sindex, ts);
sindex += len;
}
else
{
prec = 3;
lng = 0;
s++;
if (DIGIT (*s))
{
prec = *s++ - '0';
if (prec > 3) prec = 3;
}
if (*s == 'l')
{
lng = 1;
s++;
}
if (*s == 'R' || *s == 'E')
len = mkfmt (ts, prec, lng, rs, rsf);
else if (*s == 'U')
len = mkfmt (ts, prec, lng, us, usf);
else if (*s == 'S')
len = mkfmt (ts, prec, lng, ss, ssf);
else
{
internal_error (_("TIMEFORMAT: `%c': invalid format character"), *s);
free (str);
return;
}
RESIZE_MALLOCED_BUFFER (str, sindex, len, ssize, 64);
strcpy (str + sindex, ts);
sindex += len;
}
}
str[sindex] = '\0';
fprintf (fp, "%s\n", str);
fflush (fp);
free (str);
}
static int
time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
COMMAND *command;
int asynchronous, pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
int rv, posix_time, old_flags;
time_t rs, us, ss;
int rsf, usf, ssf;
int cpu;
char *time_format;
#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
struct timeval real, user, sys;
struct timeval before, after;
# if defined (HAVE_STRUCT_TIMEZONE)
struct timezone dtz;
# endif
struct rusage selfb, selfa, kidsb, kidsa;
#else
# if defined (HAVE_TIMES)
clock_t tbefore, tafter, real, user, sys;
struct tms before, after;
# endif
#endif
#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
# if defined (HAVE_STRUCT_TIMEZONE)
gettimeofday (&before, &dtz);
# else
gettimeofday (&before, (void *)NULL);
# endif
getrusage (RUSAGE_SELF, &selfb);
getrusage (RUSAGE_CHILDREN, &kidsb);
#else
# if defined (HAVE_TIMES)
tbefore = times (&before);
# endif
#endif
posix_time = (command->flags & CMD_TIME_POSIX);
old_flags = command->flags;
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;
rs = us = ss = 0;
rsf = usf = ssf = cpu = 0;
#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
# if defined (HAVE_STRUCT_TIMEZONE)
gettimeofday (&after, &dtz);
# else
gettimeofday (&after, (void *)NULL);
# endif
getrusage (RUSAGE_SELF, &selfa);
getrusage (RUSAGE_CHILDREN, &kidsa);
difftimeval (&real, &before, &after);
timeval_to_secs (&real, &rs, &rsf);
addtimeval (&user, difftimeval(&after, &selfb.ru_utime, &selfa.ru_utime),
difftimeval(&before, &kidsb.ru_utime, &kidsa.ru_utime));
timeval_to_secs (&user, &us, &usf);
addtimeval (&sys, difftimeval(&after, &selfb.ru_stime, &selfa.ru_stime),
difftimeval(&before, &kidsb.ru_stime, &kidsa.ru_stime));
timeval_to_secs (&sys, &ss, &ssf);
cpu = timeval_to_cpu (&real, &user, &sys);
#else
# if defined (HAVE_TIMES)
tafter = times (&after);
real = tafter - tbefore;
clock_t_to_secs (real, &rs, &rsf);
user = (after.tms_utime - before.tms_utime) + (after.tms_cutime - before.tms_cutime);
clock_t_to_secs (user, &us, &usf);
sys = (after.tms_stime - before.tms_stime) + (after.tms_cstime - before.tms_cstime);
clock_t_to_secs (sys, &ss, &ssf);
cpu = (real == 0) ? 0 : ((user + sys) * 10000) / real;
# else
rs = us = ss = 0;
rsf = usf = ssf = cpu = 0;
# endif
#endif
if (posix_time)
time_format = POSIX_TIMEFORMAT;
else if ((time_format = get_string_value ("TIMEFORMAT")) == 0)
time_format = BASH_TIMEFORMAT;
if (time_format && *time_format)
print_formatted_time (stderr, time_format, rs, rsf, us, usf, ss, ssf, cpu);
return rv;
}
#endif
called after make_child and we must be running in the child process.
The caller will return or exit() immediately with the value this returns. */
static int
execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
COMMAND *command;
int asynchronous;
int pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
int user_subshell, return_code, function_value, should_redir_stdin, invert;
int ois, user_coproc;
COMMAND *tcom;
USE_VAR(user_subshell);
USE_VAR(user_coproc);
USE_VAR(invert);
USE_VAR(tcom);
USE_VAR(asynchronous);
subshell_level++;
should_redir_stdin = (asynchronous && (command->flags & CMD_STDIN_REDIR) &&
pipe_in == NO_PIPE &&
stdin_redirects (command->redirects) == 0);
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
environment, but don't wait for that subshell to finish'',
and "{ foo ; bar ; } &" seems to me to be like functions or
builtins in the background, which executed in a subshell
environment. I just don't see the need to fork two subshells. */
async' shell is not interactive, however. */
if (asynchronous)
{
#if defined (JOB_CONTROL)
control is active, we want to prevent exec from putting the
subshell back into the original process group, carefully
undoing all the work we just did in make_child. */
original_pgrp = -1;
#endif
ois = interactive_shell;
interactive_shell = 0;
run `(command) &' but to allow scripts that have enabled alias
expansion with `shopt -s expand_alias' to continue to expand
aliases. */
if (ois != interactive_shell)
expand_aliases = 0;
}
login_shell = interactive = 0;
if (user_subshell)
subshell_environment = SUBSHELL_PAREN;
else
{
subshell_environment = 0;
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 ();
restore_original_signals ();
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 ();
asynchronous = 0;
}
#if defined (JOB_CONTROL)
set_sigchld_handler ();
#endif
set_sigint_handler ();
#if defined (JOB_CONTROL)
only for subshells. */
without_job_control ();
#endif
if (fds_to_close)
close_fd_bitmap (fds_to_close);
do_piping (pipe_in, pipe_out);
#if defined (COPROCESS_SUPPORT)
coproc_closeall ();
#endif
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. */
if (user_subshell)
{
stdin_redir = stdin_redirects (command->redirects);
restore_default_signal (0);
}
redirect the standard input from /dev/null in the absence of
any specific redirection involving stdin. */
if (should_redir_stdin && stdin_redir == 0)
async_redirect_stdin ();
if (command->redirects)
{
if (do_redirections (command->redirects, RX_ACTIVE) != 0)
exit (invert ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
dispose_redirects (command->redirects);
command->redirects = (REDIRECT *)NULL;
}
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;
if (command->flags & CMD_TIME_POSIX)
tcom->flags |= CMD_TIME_POSIX;
if ((command->flags & CMD_IGNORE_RETURN) && tcom != command)
tcom->flags |= CMD_IGNORE_RETURN;
might be able to get away without forking and simply exec.
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 || user_coproc) && (tcom->type == cm_simple || tcom->type == cm_subshell) &&
((tcom->flags & CMD_TIME_PIPELINE) == 0) &&
((tcom->flags & CMD_INVERT_RETURN) == 0))
{
tcom->flags |= CMD_NO_FORK;
if (tcom->type == cm_simple)
tcom->value.Simple->flags |= CMD_NO_FORK;
}
invert = (tcom->flags & CMD_INVERT_RETURN) != 0;
tcom->flags &= ~CMD_INVERT_RETURN;
need to handle a possible `return'. */
function_value = 0;
if (return_catch_flag)
function_value = setjmp (return_catch);
if (function_value)
return_code = return_catch_value;
else
return_code = execute_command_internal (tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
if (invert)
return_code = (return_code == EXECUTION_SUCCESS) ? EXECUTION_FAILURE
: EXECUTION_SUCCESS;
to do the `shell cleanup' things, such as running traps[0]. */
if (user_subshell && signal_is_trapped (0))
{
last_command_exit_value = return_code;
return_code = run_exit_trap ();
}
subshell_level--;
return (return_code);
}
#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;
}
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 struct cpelement *cpl_search __P((pid_t));
static struct cpelement *cpl_searchbyname __P((char *));
static void cpl_prune __P((void));
Coproc sh_coproc = { 0, NO_PID, -1, -1, 0, 0 };
cplist_t coproc_list = {0, 0, 0};
static struct cpelement *
cpe_alloc (cp)
Coproc *cp;
{
struct cpelement *cpe;
cpe = (struct cpelement *)xmalloc (sizeof (struct cpelement));
cpe->coproc = cp;
cpe->next = (struct cpelement *)0;
return cpe;
}
static void
cpe_dispose (cpe)
struct cpelement *cpe;
{
free (cpe);
}
static struct cpelement *
cpl_add (cp)
Coproc *cp;
{
struct cpelement *cpe;
cpe = cpe_alloc (cp);
if (coproc_list.head == 0)
{
coproc_list.head = coproc_list.tail = cpe;
coproc_list.ncoproc = 0;
}
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;
break;
}
if (p == 0)
return 0;
#if defined (DEBUG)
itrace("cpl_delete: deleting %d", pid);
#endif
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;
return (p);
}
static void
cpl_reap ()
{
struct cpelement *prev, *p;
for (prev = p = coproc_list.head; p; prev = p, p = p->next)
if (p->coproc->c_flags & COPROC_DEAD)
{
prev->next = p->next;
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;
#if defined (DEBUG)
itrace("cpl_reap: deleting %d", p->coproc->c_pid);
#endif
coproc_dispose (p->coproc);
cpe_dispose (p);
}
}
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;
}
found. If not found, return NULL. */
static struct cpelement *
cpl_search (pid)
pid_t pid;
{
struct cpelement *cp;
for (cp = coproc_list.head ; cp; cp = cp->next)
if (cp->coproc->c_pid == pid)
return cp;
return (struct cpelement *)NULL;
}
cpelement struct if found. If not found, return NULL. */
static struct cpelement *
cpl_searchbyname (name)
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;
}
#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
way to not preclude additional coprocs later (using the list management
package above). */
struct coproc *
getcoprocbypid (pid)
pid_t pid;
{
return (pid == sh_coproc.c_pid ? &sh_coproc : 0);
}
struct coproc *
getcoprocbyname (name)
const char *name;
{
return ((sh_coproc.c_name && STREQ (sh_coproc.c_name, name)) ? &sh_coproc : 0);
}
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 = 0;
}
struct coproc *
coproc_alloc (name, pid)
char *name;
pid_t pid;
{
struct coproc *cp;
cp = &sh_coproc;
coproc_init (cp);
cp->c_name = savestring (name);
cp->c_pid = pid;
return (cp);
}
void
coproc_dispose (cp)
struct coproc *cp;
{
if (cp == 0)
return;
coproc_unsetvars (cp);
FREE (cp->c_name);
coproc_close (cp);
coproc_init (cp);
}
void
coproc_flush ()
{
coproc_dispose (&sh_coproc);
}
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 ()
{
coproc_close (&sh_coproc);
}
void
coproc_reap ()
{
struct coproc *cp;
cp = &sh_coproc;
if (cp && (cp->c_flags & COPROC_DEAD))
coproc_dispose (cp);
}
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;
{
coproc_checkfd (&sh_coproc, fd);
}
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;
}
void
coproc_pidchk (pid, status)
pid_t pid;
{
struct coproc *cp;
cp = getcoprocbypid (pid);
#if 0
if (cp)
itrace("coproc_pidchk: pid %d has died", pid);
#endif
if (cp)
{
cp->c_status = status;
cp->c_flags |= COPROC_DEAD;
cp->c_flags &= ~COPROC_RUNNING;
#if 0
coproc_dispose (cp);
#endif
}
}
void
coproc_setvars (cp)
struct coproc *cp;
{
SHELL_VAR *v;
char *namevar, *t;
int l;
#if defined (ARRAY_VARS)
arrayind_t ind;
#endif
if (cp->c_name == 0)
return;
l = strlen (cp->c_name);
namevar = xmalloc (l + 16);
#if defined (ARRAY_VARS)
v = find_variable (cp->c_name);
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;
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);
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 (namevar);
#if defined (ARRAY_VARS)
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];
pid_t coproc_pid;
Coproc *cp;
char *tcmd;
if (sh_coproc.c_pid != NO_PID)
{
#if 0
internal_error ("execute_coproc: coproc [%d:%s] already exists", sh_coproc.c_pid, sh_coproc.c_name);
return (last_command_exit_value = EXECUTION_FAILURE);
#else
internal_warning ("execute_coproc: coproc [%d:%s] still exists", sh_coproc.c_pid, sh_coproc.c_name);
#endif
}
coproc_init (&sh_coproc);
command_string_index = 0;
tcmd = make_command_string (command);
sh_openpipe ((int *)&rpipe);
sh_openpipe ((int *)&wpipe);
coproc_pid = make_child (savestring (tcmd), 1);
if (coproc_pid == 0)
{
close (rpipe[0]);
close (wpipe[1]);
exit (execute_in_subshell (command, 1, wpipe[0], rpipe[1], fds_to_close));
}
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];
SET_CLOSE_ON_EXEC (cp->c_rfd);
SET_CLOSE_ON_EXEC (cp->c_wfd);
coproc_setvars (cp);
#if 0
itrace ("execute_coproc: [%d] %s", 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 (EXECUTION_SUCCESS);
}
#endif
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;
COMMAND *cmd;
struct fd_bitmap *fd_bitmap;
#if defined (JOB_CONTROL)
sigset_t set, oset;
BLOCK_CHILD (set, oset);
#endif
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 == '|')
{
if (pipe (fildes) < 0)
{
sys_error (_("pipe error"));
#if defined (JOB_CONTROL)
terminate_current_pipeline ();
kill_current_pipeline ();
UNBLOCK_CHILD (oset);
#endif
last_command_exit_value = EXECUTION_FAILURE;
of closing all of the open file descriptors. */
throw_to_top_level ();
return (EXECUTION_FAILURE);
}
code, the read end of the pipe (fildes[0]) stays open
in the first process, so that process will never get a
SIGPIPE. There is no way to signal the first process
that it should close fildes[0] after forking, so it
remains open. No SIGPIPE is ever sent because there
is still a file descriptor open for reading connected
to the pipe. We take care of that here. This passes
around a bitmap of file descriptors that must be
closed after making a child process in execute_simple_command. */
If fildes[0] is less than fds_to_close->size, then
use fds_to_close->size. */
new_bitmap_size = (fildes[0] < fds_to_close->size)
? fds_to_close->size
: fildes[0] + 8;
fd_bitmap = new_fd_bitmap (new_bitmap_size);
xbcopy ((char *)fds_to_close->bitmap, (char *)fd_bitmap->bitmap, fds_to_close->size);
fd_bitmap->bitmap[fildes[0]] = 1;
want all these file descriptors to be closed when
unwind-protects are run, and the storage used for the
bitmaps freed up. */
begin_unwind_frame ("pipe-file-descriptors");
add_unwind_protect (dispose_fd_bitmap, fd_bitmap);
add_unwind_protect (close_fd_bitmap, fd_bitmap);
if (prev >= 0)
add_unwind_protect (close, prev);
dummyfd = fildes[1];
add_unwind_protect (close, dummyfd);
#if defined (JOB_CONTROL)
add_unwind_protect (restore_signal_mask, &oset);
#endif
if (ignore_return && cmd->value.Connection->first)
cmd->value.Connection->first->flags |= CMD_IGNORE_RETURN;
execute_command_internal (cmd->value.Connection->first, asynchronous,
prev, fildes[1], fd_bitmap);
if (prev >= 0)
close (prev);
prev = fildes[0];
close (fildes[1]);
dispose_fd_bitmap (fd_bitmap);
discard_unwind_frame ("pipe-file-descriptors");
cmd = cmd->value.Connection->second;
}
if (ignore_return && cmd)
cmd->flags |= CMD_IGNORE_RETURN;
exec_result = execute_command_internal (cmd, asynchronous, prev, pipe_out, fds_to_close);
if (prev >= 0)
close (prev);
#if defined (JOB_CONTROL)
UNBLOCK_CHILD (oset);
#endif
QUIT;
return (exec_result);
}
static int
execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
COMMAND *command;
int asynchronous, pipe_in, pipe_out;
struct fd_bitmap *fds_to_close;
{
REDIRECT *rp;
COMMAND *tc, *second;
int ignore_return, exec_result, was_error_trap, invert;
volatile int save_line_number;
ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;
switch (command->value.Connection->connector)
{
case '&':
tc = command->value.Connection->first;
if (tc == 0)
return (EXECUTION_SUCCESS);
rp = tc->redirects;
if (ignore_return)
tc->flags |= CMD_IGNORE_RETURN;
tc->flags |= CMD_AMPERSAND;
if we are currently in a subshell via `( xxx )', or if job
control is not active then the standard input for an
asynchronous command is forced to /dev/null. */
#if defined (JOB_CONTROL)
if ((subshell_environment || !job_control) && !stdin_redir)
#else
if (!stdin_redir)
#endif
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;
second = command->value.Connection->second;
if (second)
{
if (ignore_return)
second->flags |= CMD_IGNORE_RETURN;
exec_result = execute_command_internal (second, asynchronous, pipe_in, pipe_out, fds_to_close);
}
break;
case ';':
if (ignore_return)
{
if (command->value.Connection->first)
command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
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;
exec_result = execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close);
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:
case OR_OR:
if (asynchronous)
{
&& or || stuff in a subshell. Force a subshell and just call
execute_command_internal again. Leave asynchronous on
so that we get a report from the parent shell about the
background job. */
command->flags |= CMD_FORCE_SUBSHELL;
exec_result = execute_command_internal (command, 1, pipe_in, pipe_out, fds_to_close);
break;
}
and the connector is AND_AND, or the result is not successful
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;
exec_result = execute_command (command->value.Connection->first);
QUIT;
if (((command->value.Connection->connector == AND_AND) &&
(exec_result == EXECUTION_SUCCESS)) ||
((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;
exec_result = execute_command (command->value.Connection->second);
}
executing_list--;
break;
default:
command_error ("execute_connection", CMDERR_BADCONN, command->value.Connection->connector, 0);
jump_to_top_level (DISCARD);
exec_result = EXECUTION_FAILURE;
}
return exec_result;
}
#define REAP() \
do \
{ \
if (!interactive_shell) \
reap_dead_jobs (); \
} \
while (0)
DO command; DONE */
static int
execute_for_command (for_command)
FOR_COM *for_command;
{
register WORD_LIST *releaser, *list;
SHELL_VAR *v;
char *identifier;
int retval, save_line_number;
#if 0
SHELL_VAR *old_value = (SHELL_VAR *)NULL;
#endif
save_line_number = line_number;
if (check_identifier (for_command->name, 1) == 0)
{
if (posixly_correct && interactive_shell == 0)
{
last_command_exit_value = EX_BADUSAGE;
jump_to_top_level (ERREXIT);
}
return (EXECUTION_FAILURE);
}
loop_level++;
identifier = for_command->name->word;
list = releaser = expand_words_no_vars (for_command->map_list);
begin_unwind_frame ("for");
add_unwind_protect (dispose_words, releaser);
#if 0
if (lexical_scoping)
{
old_value = copy_variable (find_variable (identifier));
if (old_value)
add_unwind_protect (dispose_variable, old_value);
}
#endif
if (for_command->flags & CMD_IGNORE_RETURN)
for_command->action->flags |= CMD_IGNORE_RETURN;
for (retval = EXECUTION_SUCCESS; list; list = list->next)
{
QUIT;
line_number = for_command->line;
command_string_index = 0;
print_for_command_head (for_command);
if (echo_command_at_execute)
xtrace_print_for_command_head (for_command);
a debug trap. */
#if 0
if (signal_in_progress (DEBUG_TRAP) == 0 && (this_command_name == 0 || (STREQ (this_command_name, "trap") == 0)))
#else
if (signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
#endif
{
FREE (the_printed_command_except_trap);
the_printed_command_except_trap = savestring (the_printed_command);
}
retval = run_debug_trap ();
#if defined (DEBUGGER)
skip the command. */
if (debugging_mode && retval != EXECUTION_SUCCESS)
continue;
#endif
this_command_name = (char *)NULL;
v = bind_variable (identifier, list->word->word, 0);
if (readonly_p (v) || noassign_p (v))
{
line_number = save_line_number;
if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
{
last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level (FORCE_EOF);
}
else
{
dispose_words (releaser);
discard_unwind_frame ("for");
loop_level--;
return (EXECUTION_FAILURE);
}
}
retval = execute_command (for_command->action);
REAP ();
QUIT;
if (breaking)
{
breaking--;
break;
}
if (continuing)
{
continuing--;
if (continuing)
break;
}
}
loop_level--;
line_number = save_line_number;
#if 0
if (lexical_scoping)
{
if (!old_value)
unbind_variable (identifier);
else
{
SHELL_VAR *new_value;
new_value = bind_variable (identifier, value_cell(old_value), 0);
new_value->attributes = old_value->attributes;
dispose_variable (old_value);
}
}
#endif
dispose_words (releaser);
discard_unwind_frame ("for");
return (retval);
}
#if defined (ARITH_FOR_COMMAND)
for (( init ; step ; test ))
do
body
done
The execution should be exactly equivalent to
eval \(\( init \)\)
while eval \(\( test \)\) ; do
body;
eval \(\( step \)\)
done
*/
static intmax_t
eval_arith_for_expr (l, okp)
WORD_LIST *l;
int *okp;
{
WORD_LIST *new;
intmax_t expresult;
int r;
new = expand_words_no_vars (l);
if (new)
{
if (echo_command_at_execute)
xtrace_print_arith_cmd (new);
this_command_name = "((";
command_string_index = 0;
print_arith_command (new);
if (signal_in_progress (DEBUG_TRAP) == 0)
{
FREE (the_printed_command_except_trap);
the_printed_command_except_trap = savestring (the_printed_command);
}
r = run_debug_trap ();
skip the command. */
#if defined (DEBUGGER)
if (debugging_mode == 0 || r == EXECUTION_SUCCESS)
expresult = evalexp (new->word->word, okp);
else
{
expresult = 0;
if (okp)
*okp = 1;
}
#else
expresult = evalexp (new->word->word, okp);
#endif
dispose_words (new);
}
else
{
expresult = 0;
if (okp)
*okp = 1;
}
return (expresult);
}
static int
execute_arith_for_command (arith_for_command)
ARITH_FOR_COM *arith_for_command;
{
intmax_t expresult;
int expok, body_status, arith_lineno, save_lineno;
body_status = EXECUTION_SUCCESS;
loop_level++;
save_lineno = line_number;
if (arith_for_command->flags & CMD_IGNORE_RETURN)
arith_for_command->action->flags |= CMD_IGNORE_RETURN;
this_command_name = "((";
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;
expresult = eval_arith_for_expr (arith_for_command->init, &expok);
if (expok == 0)
{
line_number = save_lineno;
return (EXECUTION_FAILURE);
}
while (1)
{
line_number = arith_lineno;
expresult = eval_arith_for_expr (arith_for_command->test, &expok);
line_number = save_lineno;
if (expok == 0)
{
body_status = EXECUTION_FAILURE;
break;
}
REAP ();
if (expresult == 0)
break;
QUIT;
body_status = execute_command (arith_for_command->action);
QUIT;
if (breaking)
{
breaking--;
break;
}
if (continuing)
{
continuing--;
if (continuing)
break;
}
line_number = arith_lineno;
expresult = eval_arith_for_expr (arith_for_command->step, &expok);
line_number = save_lineno;
if (expok == 0)
{
body_status = EXECUTION_FAILURE;
break;
}
}
loop_level--;
line_number = save_lineno;
return (body_status);
}
#endif
#if defined (SELECT_COMMAND)
static int LINES, COLS, tabsize;
#define RP_SPACE ") "
#define RP_SPACE_LEN 2
#define NUMBER_LEN(s) \
((s < 10) ? 1 \
: ((s < 100) ? 2 \
: ((s < 1000) ? 3 \
: ((s < 10000) ? 4 \
: ((s < 100000) ? 5 \
: 6)))))
static int
print_index_and_element (len, ind, list)
int len, ind;
WORD_LIST *list;
{
register WORD_LIST *l;
register int i;
if (list == 0)
return (0);
for (i = ind, l = list; l && --i; l = l->next)
;
fprintf (stderr, "%*d%s%s", len, ind, RP_SPACE, l->word->word);
return (STRLEN (l->word->word));
}
static void
indent (from, to)
int from, to;
{
while (from < to)
{
if ((to / tabsize) > (from / tabsize))
{
putc ('\t', stderr);
from += tabsize - from % tabsize;
}
else
{
putc (' ', stderr);
from++;
}
}
}
static void
print_select_list (list, list_len, max_elem_len, indices_len)
WORD_LIST *list;
int list_len, max_elem_len, indices_len;
{
int ind, row, elem_len, pos, cols, rows;
int first_column_indices_len, other_indices_len;
if (list == 0)
{
putc ('\n', stderr);
return;
}
cols = max_elem_len ? COLS / max_elem_len : 1;
if (cols == 0)
cols = 1;
rows = list_len ? list_len / cols + (list_len % cols != 0) : 1;
cols = list_len ? list_len / rows + (list_len % rows != 0) : 1;
if (rows == 1)
{
rows = cols;
cols = 1;
}
first_column_indices_len = NUMBER_LEN (rows);
other_indices_len = indices_len;
for (row = 0; row < rows; row++)
{
ind = row;
pos = 0;
while (1)
{
indices_len = (pos == 0) ? first_column_indices_len : other_indices_len;
elem_len = print_index_and_element (indices_len, ind + 1, list);
elem_len += indices_len + RP_SPACE_LEN;
ind += rows;
if (ind >= list_len)
break;
indent (pos + elem_len, pos + max_elem_len);
pos += max_elem_len;
}
putc ('\n', stderr);
}
}
LIST_LEN. Then display PROMPT and wait for the user to enter a number.
If the number is between 1 and LIST_LEN, return that selection. If EOF
is read, return a null string. If a blank line is entered, or an invalid
number is entered, the loop is executed again. */
static char *
select_query (list, list_len, prompt, print_menu)
WORD_LIST *list;
int list_len;
char *prompt;
int print_menu;
{
int max_elem_len, indices_len, len;
intmax_t reply;
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;
#if 0
t = get_string_value ("TABSIZE");
tabsize = (t && *t) ? atoi (t) : 8;
if (tabsize <= 0)
tabsize = 8;
#else
tabsize = 8;
#endif
max_elem_len = 0;
for (l = list; l; l = l->next)
{
len = STRLEN (l->word->word);
if (len > max_elem_len)
max_elem_len = len;
}
indices_len = NUMBER_LEN (list_len);
max_elem_len += indices_len + RP_SPACE_LEN + 2;
while (1)
{
if (print_menu)
print_select_list (list, list_len, max_elem_len, indices_len);
fprintf (stderr, "%s", prompt);
fflush (stderr);
QUIT;
if (read_builtin ((WORD_LIST *)NULL) == EXECUTION_FAILURE)
{
putchar ('\n');
return ((char *)NULL);
}
repl_string = get_string_value ("REPLY");
if (*repl_string == 0)
{
print_menu = 1;
continue;
}
if (legal_number (repl_string, &reply) == 0)
return "";
if (reply < 1 || reply > list_len)
return "";
for (l = list; l && --reply; l = l->next)
;
return (l->word->word);
}
}
SELECT word IN list DO command_list DONE
Only `break' or `return' in command_list will terminate
the command. */
static int
execute_select_command (select_command)
SELECT_COM *select_command;
{
WORD_LIST *releaser, *list;
SHELL_VAR *v;
char *identifier, *ps3_prompt, *selection;
int retval, list_len, show_menu, save_line_number;
if (check_identifier (select_command->name, 1) == 0)
return (EXECUTION_FAILURE);
save_line_number = line_number;
line_number = select_command->line;
command_string_index = 0;
print_select_command_head (select_command);
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);
}
retval = run_debug_trap ();
#if defined (DEBUGGER)
skip the command. */
if (debugging_mode && retval != EXECUTION_SUCCESS)
return (EXECUTION_SUCCESS);
#endif
loop_level++;
identifier = select_command->name->word;
word splitting, pathname expansion, and quote removal. */
list = releaser = expand_words_no_vars (select_command->map_list);
list_len = list_length (list);
if (list == 0 || list_len == 0)
{
if (list)
dispose_words (list);
line_number = save_line_number;
return (EXECUTION_SUCCESS);
}
begin_unwind_frame ("select");
add_unwind_protect (dispose_words, releaser);
if (select_command->flags & CMD_IGNORE_RETURN)
select_command->action->flags |= CMD_IGNORE_RETURN;
retval = EXECUTION_SUCCESS;
show_menu = 1;
while (1)
{
line_number = select_command->line;
ps3_prompt = get_string_value ("PS3");
if (ps3_prompt == 0)
ps3_prompt = "#? ";
QUIT;
selection = select_query (list, list_len, ps3_prompt, show_menu);
QUIT;
if (selection == 0)
{
fails, so we want to return failure in this case. */
retval = EXECUTION_FAILURE;
break;
}
v = bind_variable (identifier, selection, 0);
if (readonly_p (v) || noassign_p (v))
{
if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
{
last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level (FORCE_EOF);
}
else
{
dispose_words (releaser);
discard_unwind_frame ("select");
loop_level--;
line_number = save_line_number;
return (EXECUTION_FAILURE);
}
}
retval = execute_command (select_command->action);
REAP ();
QUIT;
if (breaking)
{
breaking--;
break;
}
if (continuing)
{
continuing--;
if (continuing)
break;
}
#if defined (KSH_COMPATIBLE_SELECT)
show_menu = 0;
selection = get_string_value ("REPLY");
if (selection && *selection == '\0')
show_menu = 1;
#endif
}
loop_level--;
line_number = save_line_number;
dispose_words (releaser);
discard_unwind_frame ("select");
return (retval);
}
#endif
The pattern_list is a linked list of pattern clauses; each clause contains
some patterns to compare word_desc against, and an associated command to
execute. */
static int
execute_case_command (case_command)
CASE_COM *case_command;
{
register WORD_LIST *list;
WORD_LIST *wlist, *es;
PATTERN_LIST *clauses;
char *word, *pattern;
int retval, match, ignore_return, save_line_number;
save_line_number = line_number;
line_number = case_command->line;
command_string_index = 0;
print_case_command_head (case_command);
if (echo_command_at_execute)
xtrace_print_case_command_head (case_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);
}
retval = run_debug_trap();
#if defined (DEBUGGER)
skip the command. */
if (debugging_mode && retval != EXECUTION_SUCCESS)
{
line_number = save_line_number;
return (EXECUTION_SUCCESS);
}
#endif
wlist = expand_word_unsplit (case_command->word, 0);
word = wlist ? string_list (wlist) : 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);
#define EXIT_CASE() goto exit_case_command
for (clauses = case_command->clauses; clauses; clauses = clauses->next)
{
QUIT;
for (list = clauses->patterns; list; list = list->next)
{
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);
else
{
pattern = (char *)xmalloc (1);
pattern[0] = '\0';
}
Posix.2, section 3.9.4.3), the strmatch () call must be able
to recognize backslashes as escape characters. */
match = strmatch (pattern, word, FNMATCH_EXTFLAG|FNMATCH_IGNCASE) != FNM_NOMATCH;
free (pattern);
dispose_words (es);
if (match)
{
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;
}
}
exit_case_command:
free (word);
discard_unwind_frame ("case");
line_number = save_line_number;
return (retval);
}
#define CMD_WHILE 0
#define CMD_UNTIL 1
Repeatedly execute action while executing test produces
EXECUTION_SUCCESS. */
static int
execute_while_command (while_command)
WHILE_COM *while_command;
{
return (execute_while_or_until (while_command, CMD_WHILE));
}
static int
execute_until_command (while_command)
WHILE_COM *while_command;
{
return (execute_while_or_until (while_command, CMD_UNTIL));
}
two is that the test value is treated differently. TYPE is
CMD_WHILE or CMD_UNTIL. The return value for both commands should
be EXECUTION_SUCCESS if no commands in the body are executed, and
the status of the last command executed in the body otherwise. */
static int
execute_while_or_until (while_command, type)
WHILE_COM *while_command;
int type;
{
int return_value, body_status;
body_status = EXECUTION_SUCCESS;
loop_level++;
while_command->test->flags |= CMD_IGNORE_RETURN;
if (while_command->flags & CMD_IGNORE_RETURN)
while_command->action->flags |= CMD_IGNORE_RETURN;
while (1)
{
return_value = execute_command (while_command->test);
REAP ();
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. */
if (type == CMD_WHILE && return_value != EXECUTION_SUCCESS)
{
if (breaking)
breaking--;
break;
}
if (type == CMD_UNTIL && return_value == EXECUTION_SUCCESS)
{
if (breaking)
breaking--;
break;
}
QUIT;
body_status = execute_command (while_command->action);
QUIT;
if (breaking)
{
breaking--;
break;
}
if (continuing)
{
continuing--;
if (continuing)
break;
}
}
loop_level--;
return (body_status);
}
IF also allows ELIF in the place of ELSE IF, but
the parser makes *that* stupidity transparent. */
static int
execute_if_command (if_command)
IF_COM *if_command;
{
int return_value, save_line_number;
save_line_number = line_number;
if_command->test->flags |= CMD_IGNORE_RETURN;
return_value = execute_command (if_command->test);
line_number = save_line_number;
if (return_value == EXECUTION_SUCCESS)
{
QUIT;
if (if_command->true_case && (if_command->flags & CMD_IGNORE_RETURN))
if_command->true_case->flags |= CMD_IGNORE_RETURN;
return (execute_command (if_command->true_case));
}
else
{
QUIT;
if (if_command->false_case && (if_command->flags & CMD_IGNORE_RETURN))
if_command->false_case->flags |= CMD_IGNORE_RETURN;
return (execute_command (if_command->false_case));
}
}
#if defined (DPAREN_ARITHMETIC)
static int
execute_arith_command (arith_command)
ARITH_COM *arith_command;
{
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;
if (variable_context && interactive_shell)
line_number -= function_line_number;
command_string_index = 0;
print_arith_command (arith_command->exp);
if (signal_in_progress (DEBUG_TRAP) == 0)
{
FREE (the_printed_command_except_trap);
the_printed_command_except_trap = savestring (the_printed_command);
}
update the line number information and before we expand the various
words in the expression. */
retval = run_debug_trap ();
#if defined (DEBUGGER)
skip the command. */
if (debugging_mode && retval != EXECUTION_SUCCESS)
{
line_number = save_line_number;
return (EXECUTION_SUCCESS);
}
#endif
new = expand_words_no_vars (arith_command->exp);
at the back and print it. */
if (echo_command_at_execute)
xtrace_print_arith_cmd (new);
if (new)
{
exp = new->next ? string_list (new) : new->word->word;
expresult = evalexp (exp, &expok);
line_number = save_line_number;
if (exp != new->word->word)
free (exp);
dispose_words (new);
}
else
{
expresult = 0;
expok = 1;
}
if (expok == 0)
return (EXECUTION_FAILURE);
return (expresult == 0 ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}
#endif
#if defined (COND_COMMAND)
static char * const nullstr = "";
static int
execute_cond_node (cond)
COND_COM *cond;
{
int result, invert, patmatch, rmatch, mflags;
char *arg1, *arg2;
invert = (cond->flags & CMD_INVERT_RETURN);
if (cond->type == COND_EXPR)
result = execute_cond_node (cond->left);
else if (cond->type == COND_OR)
{
result = execute_cond_node (cond->left);
if (result != EXECUTION_SUCCESS)
result = execute_cond_node (cond->right);
}
else if (cond->type == COND_AND)
{
result = execute_cond_node (cond->left);
if (result == EXECUTION_SUCCESS)
result = execute_cond_node (cond->right);
}
else if (cond->type == COND_UNARY)
{
arg1 = cond_expand_word (cond->left->op, 0);
if (arg1 == 0)
arg1 = nullstr;
if (echo_command_at_execute)
xtrace_print_cond_term (cond->type, invert, cond->op, arg1, (char *)NULL);
result = unary_test (cond->op->word, arg1) ? EXECUTION_SUCCESS : EXECUTION_FAILURE;
if (arg1 != nullstr)
free (arg1);
}
else if (cond->type == COND_BINARY)
{
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
arg1 = cond_expand_word (cond->left->op, 0);
if (arg1 == 0)
arg1 = nullstr;
arg2 = cond_expand_word (cond->right->op,
(rmatch && shell_compatibility_level > 31) ? 2 : (patmatch ? 1 : 0));
if (arg2 == 0)
arg2 = nullstr;
if (echo_command_at_execute)
xtrace_print_cond_term (cond->type, invert, cond->op, arg1, arg2);
#if defined (COND_REGEXP)
if (rmatch)
{
mflags = SHMAT_PWARN;
#if defined (ARRAY_VARS)
mflags |= SHMAT_SUBEXP;
#endif
result = sh_regmatch (arg1, arg2, mflags);
}
else
#endif
{
int oe;
oe = extended_glob;
extended_glob = 1;
result = binary_test (cond->op->word, arg1, arg2, TEST_PATMATCH|TEST_ARITHEXP)
? EXECUTION_SUCCESS
: EXECUTION_FAILURE;
extended_glob = oe;
}
if (arg1 != nullstr)
free (arg1);
if (arg2 != nullstr)
free (arg2);
}
else
{
command_error ("execute_cond_node", CMDERR_BADTYPE, cond->type, 0);
jump_to_top_level (DISCARD);
result = EXECUTION_FAILURE;
}
if (invert)
result = (result == EXECUTION_SUCCESS) ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
return result;
}
static int
execute_cond_command (cond_command)
COND_COM *cond_command;
{
int retval, save_line_number;
retval = EXECUTION_SUCCESS;
save_line_number = line_number;
this_command_name = "[[";
line_number = cond_command->line;
if (variable_context && interactive_shell)
line_number -= function_line_number;
command_string_index = 0;
print_cond_command (cond_command);
if (signal_in_progress (DEBUG_TRAP) == 0)
{
FREE (the_printed_command_except_trap);
the_printed_command_except_trap = savestring (the_printed_command);
}
update the line number information. */
retval = run_debug_trap ();
#if defined (DEBUGGER)
skip the command. */
if (debugging_mode && retval != EXECUTION_SUCCESS)
{
line_number = save_line_number;
return (EXECUTION_SUCCESS);
}
#endif
#if 0
debug_print_cond_command (cond_command);
#endif
last_command_exit_value = retval = execute_cond_node (cond_command);
line_number = save_line_number;
return (retval);
}
#endif
static void
bind_lastarg (arg)
char *arg;
{
SHELL_VAR *var;
if (arg == 0)
arg = "";
var = bind_variable ("_", arg, 0);
VUNSETATTR (var, att_exported);
}
to be run asynchronously. This handles all the side effects that are
supposed to take place. */
static int
execute_null_command (redirects, pipe_in, pipe_out, async)
REDIRECT *redirects;
int pipe_in, pipe_out, async;
{
int r;
if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
{
care of it. Just fork, do piping and redirections, and exit. */
if (make_child ((char *)NULL, async) == 0)
{
restore_original_signals ();
do_piping (pipe_in, pipe_out);
#if defined (COPROCESS_SUPPORT)
coproc_closeall ();
#endif
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);
else
exit (EXECUTION_FAILURE);
}
else
{
close_pipes (pipe_in, pipe_out);
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
unlink_fifo_list ();
#endif
return (EXECUTION_SUCCESS);
}
}
else
{
redirections that are specified. The user expects the side
effects to take place. If the redirections fail, then return
failure. Otherwise, if a command substitution took place while
expanding the command or a redirection, return the value of that
substitution. Otherwise, return EXECUTION_SUCCESS. */
r = do_redirections (redirects, RX_ACTIVE|RX_UNDOABLE);
cleanup_redirects (redirection_undo_list);
redirection_undo_list = (REDIRECT *)NULL;
if (r != 0)
return (EXECUTION_FAILURE);
else if (last_command_subst_pid != NO_PID)
return (last_command_exit_value);
else
return (EXECUTION_SUCCESS);
}
}
given as arguments to builtins with the ASSIGNMENT_BUILTIN flag set. */
static void
fix_assignment_words (words)
WORD_LIST *words;
{
WORD_LIST *w;
struct builtin *b;
int assoc;
if (words == 0)
return;
b = 0;
assoc = 0;
for (w = words; w; w = w->next)
if (w->word->flags & W_ASSIGNMENT)
{
if (b == 0)
{
b = builtin_address_internal (words->word->word, 0);
if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
return;
else if (b && (b->flags & ASSIGNMENT_BUILTIN))
words->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;
#endif
}
#if defined (ARRAY_VARS)
assignment statements. This is a bit of a kludge. */
else if (w->word->word[0] == '-' && strchr (w->word->word, 'A'))
{
if (b == 0)
{
b = builtin_address_internal (words->word->word, 0);
if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
return;
else if (b && (b->flags & ASSIGNMENT_BUILTIN))
words->word->flags |= W_ASSNBLTIN;
}
if (words->word->flags & W_ASSNBLTIN)
assoc = 1;
}
#endif
}
to PATHNAME, is a directory. Used by the autocd code below. */
static int
is_dirname (pathname)
char *pathname;
{
char *temp;
temp = search_for_command (pathname);
return (temp ? file_isdir (temp) : file_isdir (pathname));
}
real execution of commands here. Fork a process, set things up,
execute the command. */
static int
execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
SIMPLE_COM *simple_command;
int pipe_in, pipe_out, async;
struct fd_bitmap *fds_to_close;
{
WORD_LIST *words, *lastword;
char *command_line, *lastarg, *temp;
int first_word_quoted, result, builtin_is_special, already_forked, dofork;
pid_t old_last_async_pid;
sh_builtin_func_t *builtin;
SHELL_VAR *func;
result = EXECUTION_SUCCESS;
special_builtin_failed = builtin_is_special = 0;
command_line = (char *)0;
if (variable_context && interactive_shell)
line_number -= function_line_number;
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;
}
update the line number information. */
result = run_debug_trap ();
#if defined (DEBUGGER)
skip the command. */
if (debugging_mode && result != EXECUTION_SUCCESS)
return (EXECUTION_SUCCESS);
#endif
first_word_quoted =
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;
make the child early, before word expansion. This keeps assignment
statements from affecting the parent shell's environment when they
should not. */
dofork = pipe_in != NO_PIPE || pipe_out != NO_PIPE || async;
the shell to fork here. */
if (dofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE &&
simple_command->words && simple_command->words->word &&
simple_command->words->word->word &&
(simple_command->words->word->word[0] == '%'))
dofork = 0;
if (dofork)
{
vast majority of cases. */
maybe_make_export_env ();
the process/job associated with this child. */
if (make_child (savestring (the_printed_command_except_trap), async) == 0)
{
already_forked = 1;
simple_command->flags |= CMD_NO_FORK;
subshell_environment = SUBSHELL_FORK;
if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
subshell_environment |= SUBSHELL_PIPE;
if (async)
subshell_environment |= SUBSHELL_ASYNC;
pathological cases where one of the pipe file descriptors
is < 2. */
if (fds_to_close)
close_fd_bitmap (fds_to_close);
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;
}
else
{
close_pipes (pipe_in, pipe_out);
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
unlink_fifo_list ();
#endif
command_line = (char *)NULL;
bind_lastarg ((char *)NULL);
return (result);
}
}
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);
if (simple_command->flags & CMD_IGNORE_RETURN)
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
words = copy_word_list (simple_command->words);
Perhaps all the words consisted of `$foo', and there was
no variable `$foo'. */
if (words == 0)
{
this_command_name = 0;
result = execute_null_command (simple_command->redirects,
pipe_in, pipe_out,
already_forked ? 0 : async);
if (already_forked)
exit (result);
else
{
bind_lastarg ((char *)NULL);
set_pipestatus_from_exit (result);
return (result);
}
}
lastarg = (char *)NULL;
begin_unwind_frame ("simple-command");
if (echo_command_at_execute)
xtrace_print_word_list (words, 1);
builtin = (sh_builtin_func_t *)NULL;
func = (SHELL_VAR *)NULL;
if ((simple_command->flags & CMD_NO_FUNCTIONS) == 0)
{
don't set builtin_is_special anywhere other than here, because
this path is followed only when the `command' builtin is *not*
being used, and we don't want to exit the shell if a special
builtin executed with `command builtin' fails. `command' is not
a special builtin. */
if (posixly_correct)
{
builtin = find_special_builtin (words->word->word);
if (builtin)
builtin_is_special = 1;
}
if (builtin == 0)
func = find_function (words->word->word);
}
non-interactive shell to exit. */
if (builtin_is_special && interactive_shell == 0 && tempenv_assign_error)
{
last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level (ERREXIT);
}
add_unwind_protect (dispose_words, words);
QUIT;
for (lastword = words; lastword->next; lastword = lastword->next)
;
lastarg = lastword->word->word;
#if defined (JOB_CONTROL)
if (words->word->word[0] == '%' && already_forked == 0)
{
this_command_name = async ? "bg" : "fg";
last_shell_builtin = this_shell_builtin;
this_shell_builtin = builtin_address (this_command_name);
result = (*this_shell_builtin) (words);
goto return_result;
}
If they do, find out whether this word is a candidate for a running
job. */
if (job_control && already_forked == 0 && async == 0 &&
!first_word_quoted &&
!words->next &&
words->word->word[0] &&
!simple_command->redirects &&
pipe_in == NO_PIPE &&
pipe_out == NO_PIPE &&
(temp = get_string_value ("auto_resume")))
{
int job, jflags, started_status;
jflags = JM_STOPPED|JM_FIRSTMATCH;
if (STREQ (temp, "exact"))
jflags |= JM_EXACT;
else if (STREQ (temp, "substring"))
jflags |= JM_SUBSTRING;
else
jflags |= JM_PREFIX;
job = get_job_by_name (words->word->word, jflags);
if (job != NO_JOB)
{
run_unwind_frame ("simple-command");
this_command_name = "fg";
last_shell_builtin = this_shell_builtin;
this_shell_builtin = builtin_address ("fg");
started_status = start_job (job, 1);
return ((started_status < 0) ? EXECUTION_FAILURE : started_status);
}
}
#endif
run_builtin:
this_command_name = words->word->word;
QUIT;
We have already found special builtins by this time, so we do not
set builtin_is_special. If this is a function or builtin, and we
have pipes, then fork a subshell in here. Otherwise, just execute
the command directly. */
if (func == 0 && builtin == 0)
builtin = find_shell_builtin (this_command_name);
last_shell_builtin = this_shell_builtin;
this_shell_builtin = builtin;
if (builtin || func)
{
if (builtin)
unwind_protect_int (executing_builtin);
if (already_forked)
{
restore_original_signals ();
if (async)
{
if ((simple_command->flags & CMD_STDIN_REDIR) &&
pipe_in == NO_PIPE &&
(stdin_redirects (simple_command->redirects) == 0))
async_redirect_stdin ();
setup_async_signals ();
}
subshell_level++;
execute_subshell_builtin_or_function
(words, simple_command->redirects, builtin, func,
pipe_in, pipe_out, async, fds_to_close,
simple_command->flags);
subshell_level--;
}
else
{
result = execute_builtin_or_function
(words, builtin, func, simple_command->redirects, fds_to_close,
simple_command->flags);
if (builtin)
{
if (result > EX_SHERRBASE)
{
result = builtin_status (result);
if (builtin_is_special)
special_builtin_failed = 1;
}
a special builtin, they persist after the builtin
completes. */
if (posixly_correct && builtin_is_special && temporary_env)
merge_temporary_env ();
}
else
{
if (result == EX_USAGE)
result = EX_BADUSAGE;
else if (result > EX_SHERRBASE)
result = EXECUTION_FAILURE;
}
set_pipestatus_from_exit (result);
goto return_result;
}
}
if (autocd && interactive && words->word && is_dirname (words->word->word))
{
words = make_word_list (make_word ("cd"), words);
xtrace_print_word_list (words, 0);
goto run_builtin;
}
if (command_line == 0)
command_line = savestring (the_printed_command_except_trap);
#if defined (PROCESS_SUBSTITUTION)
if ((subshell_environment & SUBSHELL_COMSUB) && (simple_command->flags & CMD_NO_FORK) && fifos_pending() > 0)
simple_command->flags &= ~CMD_NO_FORK;
#endif
execute_disk_command (words, simple_command->redirects, command_line,
pipe_in, pipe_out, async, fds_to_close,
simple_command->flags);
return_result:
bind_lastarg (lastarg);
FREE (command_line);
dispose_words (words);
discard_unwind_frame ("simple-command");
this_command_name = (char *)NULL;
return (result);
}
function for this; it's a placeholder for future work. */
static int
builtin_status (result)
int result;
{
int r;
switch (result)
{
case EX_USAGE:
r = EX_BADUSAGE;
break;
case EX_REDIRFAIL:
case EX_BADSYNTAX:
case EX_BADASSIGN:
case EX_EXPFAIL:
r = EXECUTION_FAILURE;
break;
default:
r = EXECUTION_SUCCESS;
break;
}
return (r);
}
static int
execute_builtin (builtin, words, flags, subshell)
sh_builtin_func_t *builtin;
WORD_LIST *words;
int flags, subshell;
{
int old_e_flag, result, eval_unwind;
int isbltinenv;
#if 0
terminate_immediately++;
#endif
old_e_flag = exit_immediately_on_error;
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. 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);
exit_immediately_on_error = 0;
eval_unwind = 1;
}
else
eval_unwind = 0;
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);
if (isbltinenv)
{
if (subshell == 0)
begin_unwind_frame ("builtin_env");
if (temporary_env)
{
push_scope (VC_BLTNENV, temporary_env);
if (subshell == 0)
add_unwind_protect (pop_scope, (flags & CMD_COMMAND_BUILTIN) ? 0 : "1");
temporary_env = (HASH_TABLE *)NULL;
}
}
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)
{
begin_unwind_frame ("return_temp_env");
add_unwind_protect (merge_temporary_env, (char *)NULL);
}
executing_builtin++;
result = ((*builtin) (words->next));
longjmp'ing, we need to unwind. */
if (posixly_correct && subshell == 0 && builtin == return_builtin && temporary_env)
discard_unwind_frame ("return_temp_env");
if (subshell == 0 && isbltinenv)
run_unwind_frame ("builtin_env");
if (eval_unwind)
{
exit_immediately_on_error += old_e_flag;
discard_unwind_frame ("eval_builtin");
}
#if 0
terminate_immediately--;
#endif
return (result);
}
static int
execute_function (var, words, flags, fds_to_close, async, subshell)
SHELL_VAR *var;
WORD_LIST *words;
int flags;
struct fd_bitmap *fds_to_close;
int async, subshell;
{
int return_val, result;
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;
#endif
FUNCTION_DEF *shell_fn;
char *sfile, *t;
static int funcnest = 0;
USE_VAR(fc);
#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);
GET_ARRAY_FROM_VAR ("BASH_LINENO", bash_lineno_v, bash_lineno_a);
#endif
tc = (COMMAND *)copy_command (function_cell (var));
if (tc && (flags & CMD_IGNORE_RETURN))
tc->flags |= CMD_IGNORE_RETURN;
if (subshell == 0)
{
begin_unwind_frame ("function_calling");
push_context (var->name, subshell, temporary_env);
add_unwind_protect (pop_context, (char *)NULL);
unwind_protect_int (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 (loop_level);
}
else
push_context (var->name, subshell, temporary_env);
temporary_env = (HASH_TABLE *)NULL;
this_shell_function = var;
make_funcname_visible (1);
debug_trap = TRAP_STRING(DEBUG_TRAP);
error_trap = TRAP_STRING(ERROR_TRAP);
return_trap = TRAP_STRING(RETURN_TRAP);
return_trap is important here! unwind-protect commands are run
in reverse order of registration. If this causes problems, take
out the xfree unwind-protect calls and live with the small memory leak. */
if the function has the trace attribute set, it inherits the DEBUG trap */
if (debug_trap && ((trace_p (var) == 0) && function_trace_mode == 0))
{
if (subshell == 0)
{
debug_trap = savestring (debug_trap);
add_unwind_protect (xfree, debug_trap);
add_unwind_protect (set_debug_trap, debug_trap);
}
restore_default_signal (DEBUG_TRAP);
}
if (error_trap && error_trace_mode == 0)
{
if (subshell == 0)
{
error_trap = savestring (error_trap);
add_unwind_protect (xfree, error_trap);
add_unwind_protect (set_error_trap, error_trap);
}
restore_default_signal (ERROR_TRAP);
}
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);
}
restore_default_signal (RETURN_TRAP);
}
funcnest++;
#if defined (ARRAY_VARS)
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 (bash_source_a, sfile);
t = itos (executing_line_number ());
array_push (bash_lineno_a, t);
free (t);
#endif
all commands executed within the function body. */
remember_args (words->next, 1);
if (debugging_mode)
push_args (words->next);
line_number = function_line_number = tc->line;
#if defined (JOB_CONTROL)
if (subshell)
stop_pipeline (async, (COMMAND *)NULL);
#endif
fc = tc;
return_catch_flag++;
return_val = setjmp (return_catch);
if (return_val)
{
result = return_catch_value;
save_current = currently_executing_command;
run_return_trap ();
currently_executing_command = save_current;
}
else
{
execution rather than the execution of the body's first command. */
showing_function_line = 1;
save_current = currently_executing_command;
result = run_debug_trap ();
#if defined (DEBUGGER)
skip the command. */
if (debugging_mode == 0 || result == EXECUTION_SUCCESS)
{
showing_function_line = 0;
currently_executing_command = save_current;
result = execute_command_internal (fc, 0, NO_PIPE, NO_PIPE, fds_to_close);
save_current = currently_executing_command;
run_return_trap ();
currently_executing_command = save_current;
}
#else
result = execute_command_internal (fc, 0, NO_PIPE, NO_PIPE, fds_to_close);
save_current = currently_executing_command;
run_return_trap ();
currently_executing_command = save_current;
#endif
showing_function_line = 0;
}
if (debugging_mode)
pop_args ();
if (subshell == 0)
run_unwind_frame ("function_calling");
funcnest--;
#if defined (ARRAY_VARS)
function. */
array_pop (bash_source_a);
array_pop (bash_lineno_a);
function. */
GET_ARRAY_FROM_VAR ("FUNCNAME", nfv, funcname_a);
if (nfv == funcname_v)
array_pop (funcname_a);
#endif
if (variable_context == 0 || this_shell_function == 0)
make_funcname_visible (0);
return (result);
}
a particular shell function. */
int
execute_shell_function (var, words)
SHELL_VAR *var;
WORD_LIST *words;
{
int ret;
struct fd_bitmap *bitmap;
bitmap = new_fd_bitmap (FD_BITMAP_DEFAULT_SIZE);
begin_unwind_frame ("execute-shell-function");
add_unwind_protect (dispose_fd_bitmap, (char *)bitmap);
ret = execute_function (var, words, 0, bitmap, 0, 0);
dispose_fd_bitmap (bitmap);
discard_unwind_frame ("execute-shell-function");
return ret;
}
routine does not return; it only calls exit(). If BUILTIN is non-null,
it points to a function to call to execute a shell builtin; otherwise
VAR points at the body of a function to execute. WORDS is the arguments
to the command, REDIRECTS specifies redirections to perform before the
command is executed. */
static void
execute_subshell_builtin_or_function (words, redirects, builtin, var,
pipe_in, pipe_out, async, fds_to_close,
flags)
WORD_LIST *words;
REDIRECT *redirects;
sh_builtin_func_t *builtin;
SHELL_VAR *var;
int pipe_in, pipe_out, async;
struct fd_bitmap *fds_to_close;
int flags;
{
int result, r, funcvalue;
#if defined (JOB_CONTROL)
int jobs_hack;
jobs_hack = (builtin == jobs_builtin) &&
((subshell_environment & SUBSHELL_ASYNC) == 0 || pipe_out != NO_PIPE);
#endif
login_shell = interactive = 0;
if (async)
subshell_environment |= SUBSHELL_ASYNC;
if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
subshell_environment |= SUBSHELL_PIPE;
maybe_make_export_env ();
#if defined (JOB_CONTROL)
all jobs begun by this subshell are in the same process group as
the shell itself. */
if (jobs_hack)
kill_current_pipeline ();
else
without_job_control ();
set_sigchld_handler ();
#endif
set_sigint_handler ();
if (fds_to_close)
close_fd_bitmap (fds_to_close);
do_piping (pipe_in, pipe_out);
if (do_redirections (redirects, RX_ACTIVE) != 0)
exit (EXECUTION_FAILURE);
if (builtin)
{
so we don't go back up to main(). */
result = setjmp (top_level);
or pipeline */
funcvalue = 0;
if (return_catch_flag && builtin == return_builtin)
funcvalue = setjmp (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);
}
}
else
{
r = execute_function (var, words, flags, fds_to_close, async, 1);
fflush (stdout);
exit (r);
}
}
is non-null, it is the builtin command to execute, otherwise VAR points
to the body of a function. WORDS are the command's arguments, REDIRECTS
are the redirections to perform. FDS_TO_CLOSE is the usual bitmap of
file descriptors to close.
If BUILTIN is exec_builtin, the redirections specified in REDIRECTS are
not undone before this function returns. */
static int
execute_builtin_or_function (words, builtin, var, redirects,
fds_to_close, flags)
WORD_LIST *words;
sh_builtin_func_t *builtin;
SHELL_VAR *var;
REDIRECT *redirects;
struct fd_bitmap *fds_to_close;
int flags;
{
int result;
REDIRECT *saved_undo_list;
sh_builtin_func_t *saved_this_shell_builtin;
if (do_redirections (redirects, RX_ACTIVE|RX_UNDOABLE) != 0)
{
cleanup_redirects (redirection_undo_list);
redirection_undo_list = (REDIRECT *)NULL;
dispose_exec_redirects ();
return (EX_REDIRFAIL);
}
saved_this_shell_builtin = this_shell_builtin;
saved_undo_list = redirection_undo_list;
if (builtin == exec_builtin)
{
dispose_redirects (saved_undo_list);
saved_undo_list = exec_redirection_undo_list;
exec_redirection_undo_list = (REDIRECT *)NULL;
}
else
dispose_exec_redirects ();
if (saved_undo_list)
{
begin_unwind_frame ("saved redirects");
add_unwind_protect (cleanup_redirects, (char *)saved_undo_list);
}
redirection_undo_list = (REDIRECT *)NULL;
if (builtin)
result = execute_builtin (builtin, words, flags, 0);
else
result = execute_function (var, words, flags, fds_to_close, 0, 0);
fflush (stdout);
fpurge (stdout);
if (ferror (stdout))
clearerr (stdout);
set to `exec_builtin', we know that we have something like
`command exec [redirection]', since otherwise `exec' would have
overwritten the shell and we wouldn't get here. In this case, we
want to behave as if the `command' builtin had not been specified
and preserve the redirections. */
if (builtin == command_builtin && this_shell_builtin == exec_builtin)
{
if (saved_undo_list)
dispose_redirects (saved_undo_list);
redirection_undo_list = exec_redirection_undo_list;
saved_undo_list = exec_redirection_undo_list = (REDIRECT *)NULL;
discard_unwind_frame ("saved_redirects");
}
if (saved_undo_list)
{
redirection_undo_list = saved_undo_list;
discard_unwind_frame ("saved redirects");
}
if (redirection_undo_list)
{
cleanup_redirects (redirection_undo_list);
redirection_undo_list = (REDIRECT *)NULL;
}
return (result);
}
void
setup_async_signals ()
{
#if defined (__BEOS__) && !defined (__HAIKU__)
set_signal_handler (SIGHUP, SIG_IGN);
#endif
#if defined (JOB_CONTROL)
if (job_control == 0)
#endif
{
set_signal_handler (SIGINT, SIG_IGN);
set_signal_ignored (SIGINT);
set_signal_handler (SIGQUIT, SIG_IGN);
set_signal_ignored (SIGQUIT);
}
}
somewhere.
1) fork ()
2) connect pipes
3) look up the command
4) do redirections
5) execve ()
6) If the execve failed, see if the file has executable mode set.
If so, and it isn't a directory, then execute its contents as
a shell script.
Note that the filename hashing stuff has to take place up here,
in the parent. This is probably why the Bourne style shells
don't handle it, since that would require them to go through
this gnarly hair, for no good reason.
NOTE: callers expect this to fork or exit(). */
#ifndef NOTFOUND_HOOK
# define NOTFOUND_HOOK "command_not_found_handle"
#endif
static void
execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
async, fds_to_close, cmdflags)
WORD_LIST *words;
REDIRECT *redirects;
char *command_line;
int pipe_in, pipe_out, async;
struct fd_bitmap *fds_to_close;
int cmdflags;
{
char *pathname, *command, **args;
int nofork;
pid_t pid;
SHELL_VAR *hookf;
WORD_LIST *wl;
nofork = (cmdflags & CMD_NO_FORK);
pathname = words->word->word;
#if defined (RESTRICTED_SHELL)
command = (char *)NULL;
if (restricted && xstrchr (pathname, '/'))
{
internal_error (_("%s: restricted: cannot specify `/' in command names"),
pathname);
last_command_exit_value = EXECUTION_FAILURE;
process or a context in which it's safe to call exit(2). */
if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE)
exit (last_command_exit_value);
else
goto parent_return;
}
#endif
command = search_for_command (pathname);
if (command)
{
maybe_make_export_env ();
put_command_name_into_env (command);
}
of COMMAND, since we want the error messages to be redirected. */
don't bother to fork, just directly exec the command. */
if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE)
pid = 0;
else
pid = make_child (savestring (command_line), async);
if (pid == 0)
{
int old_interactive;
#if 0
#if !defined (ARG_MAX) || ARG_MAX >= 10240
if (posixly_correct == 0)
put_gnu_argv_flags_into_env ((long)getpid (), glob_argv_flags);
#endif
#endif
restore_original_signals ();
by make_child to ensure that SIGINT and SIGQUIT are ignored
in asynchronous children. */
if (async)
{
if ((cmdflags & CMD_STDIN_REDIR) &&
pipe_in == NO_PIPE &&
(stdin_redirects (redirects) == 0))
async_redirect_stdin ();
setup_async_signals ();
}
file descriptors manipulated by redirection and piping.
Some file descriptors still need to be closed in all children
because of the way bash does pipes; fds_to_close is a
bitmap of all such file descriptors. */
if (fds_to_close)
close_fd_bitmap (fds_to_close);
do_piping (pipe_in, pipe_out);
old_interactive = interactive;
if (async)
interactive = 0;
subshell_environment = SUBSHELL_FORK;
if (redirects && (do_redirections (redirects, RX_ACTIVE) != 0))
{
#if defined (PROCESS_SUBSTITUTION)
result of redirections. */
unlink_fifo_list ();
#endif
exit (EXECUTION_FAILURE);
}
if (async)
interactive = old_interactive;
if (command == 0)
{
hookf = find_function (NOTFOUND_HOOK);
if (hookf == 0)
{
internal_error (_("%s: command not found"), pathname);
exit (EX_NOTFOUND);
}
wl = make_word_list (make_word (NOTFOUND_HOOK), words);
exit (execute_shell_function (hookf, wl));
}
leave it there, in the same format that the user used to
type it in. */
args = strvec_from_word_list (words, 0, 0, (int *)NULL);
exit (shell_execve (command, args, export_env));
}
else
{
parent_return:
close_pipes (pipe_in, pipe_out);
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
unlink_fifo_list ();
#endif
FREE (command);
}
}
corresponds to a valid interpreter name or argument character, or
whitespace. The MSDOS define is to allow \r to be treated the same
as \n. */
#if !defined (MSDOS)
# define STRINGCHAR(ind) \
(ind < sample_len && !whitespace (sample[ind]) && sample[ind] != '\n')
# define WHITECHAR(ind) \
(ind < sample_len && whitespace (sample[ind]))
#else
# define STRINGCHAR(ind) \
(ind < sample_len && !whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r')
# define WHITECHAR(ind) \
(ind < sample_len && whitespace (sample[ind]))
#endif
static char *
getinterp (sample, sample_len, endp)
char *sample;
int sample_len, *endp;
{
register int i;
char *execname;
int start;
for (i = 2; i < sample_len && whitespace (sample[i]); i++)
;
for (start = i; STRINGCHAR(i); i++)
;
execname = substring (sample, start, i);
if (endp)
*endp = i;
return execname;
}
#if !defined (HAVE_HASH_BANG_EXEC)
the #! executable format, then help out. SAMPLE is the text read
from the file, SAMPLE_LEN characters. COMMAND is the name of
the script; it and ARGS, the arguments given by the user, will
become arguments to the specified interpreter. ENV is the environment
to pass to the interpreter.
The word immediately following the #! is the interpreter to execute.
A single argument to the interpreter is allowed. */
static int
execute_shell_script (sample, sample_len, command, args, env)
char *sample;
int sample_len;
char *command;
char **args, **env;
{
char *execname, *firstarg;
int i, start, size_increment, larry;
execname = getinterp (sample, sample_len, &i);
size_increment = 1;
for (firstarg = (char *)NULL, start = i; WHITECHAR(i); i++)
;
interpreter. */
if (STRINGCHAR(i))
{
for (start = i; STRINGCHAR(i); i++)
;
firstarg = substring ((char *)sample, start, i);
size_increment = 2;
}
larry = strvec_len (args) + size_increment;
args = strvec_resize (args, larry + 1);
for (i = larry - 1; i; i--)
args[i] = args[i - size_increment];
args[0] = execname;
if (firstarg)
{
args[1] = firstarg;
args[2] = command;
}
else
args[1] = command;
args[larry] = (char *)NULL;
return (shell_execve (execname, args, env));
}
#undef STRINGCHAR
#undef WHITECHAR
#endif
static void
initialize_subshell ()
{
#if defined (ALIAS)
delete_all_aliases ();
#endif
#if defined (HISTORY)
subshell. */
history_lines_this_session = 0;
#endif
#if defined (JOB_CONTROL)
without_job_control ();
set_sigchld_handler ();
init_job_stats ();
#endif
reset_shell_flags ();
reset_shell_options ();
reset_shopt_options ();
sourced file with a temporary environment supplied to the `source/.'
builtin. Such variables are not supposed to be exported (empirical
testing with sh and ksh). Just throw it away; don't worry about a
memory leak. */
if (vc_isbltnenv (shell_variables))
shell_variables = shell_variables->down;
clear_unwind_protect_list (0);
parse_and_execute_level = 0;
variable_context = return_catch_flag = 0;
executing_list = 0;
reading the current shell script. */
if (interactive_shell == 0)
unset_bash_input (0);
}
#if defined (HAVE_SETOSTYPE) && defined (_POSIX_SOURCE)
# define SETOSTYPE(x) __setostype(x)
#else
# define SETOSTYPE(x)
#endif
#define READ_SAMPLE_BUF(file, buf, len) \
do \
{ \
fd = open(file, O_RDONLY); \
if (fd >= 0) \
{ \
len = read (fd, buf, 80); \
close (fd); \
} \
else \
len = -1; \
} \
while (0)
exec failures. */
int
shell_execve (command, args, env)
char *command;
char **args, **env;
{
int larray, i, fd;
char sample[80];
int sample_len;
SETOSTYPE (0);
execve (command, args, env);
i = errno;
CHECK_TERMSIG;
SETOSTYPE (1);
Maybe it is something we can hack ourselves. */
if (i != ENOEXEC)
{
if (file_isdir (command))
internal_error (_("%s: is a directory"), command);
else if (executable_file (command) == 0)
{
errno = i;
file_error (command);
}
else if (i == E2BIG || i == ENOMEM)
{
errno = i;
file_error (command);
}
else
{
run it for some reason. See why. */
#if defined (HAVE_HASH_BANG_EXEC)
READ_SAMPLE_BUF (command, sample, sample_len);
if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
{
char *interp;
int ilen;
interp = getinterp (sample, sample_len, (int *)NULL);
ilen = strlen (interp);
errno = i;
if (interp[ilen - 1] == '\r')
{
interp = xrealloc (interp, ilen + 2);
interp[ilen - 1] = '^';
interp[ilen] = 'M';
interp[ilen + 1] = '\0';
}
sys_error (_("%s: %s: bad interpreter"), command, interp ? interp : "");
FREE (interp);
return (EX_NOEXEC);
}
#endif
errno = i;
file_error (command);
}
return ((i == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
}
If it begins with #!, then help out people with losing operating
systems. Otherwise, check to see if it is a binary file by seeing
if the contents of the first line (or up to 80 characters) are in the
ASCII set. If it's a text file, execute the contents as shell commands,
otherwise return 126 (EX_BINARY_FILE). */
READ_SAMPLE_BUF (command, sample, sample_len);
if (sample_len == 0)
return (EXECUTION_SUCCESS);
If so, the format of the line is "#! interpreter [argument]".
A single argument is allowed. The BSD kernel restricts
the length of the entire line to 32 characters (32 bytes
being the size of the BSD exec header), but we allow 80
characters. */
if (sample_len > 0)
{
#if !defined (HAVE_HASH_BANG_EXEC)
if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
return (execute_shell_script (sample, sample_len, command, args, env));
else
#endif
if (check_binary_file (sample, sample_len))
{
internal_error (_("%s: cannot execute binary file"), command);
return (EX_BINARY_FILE);
}
}
as shell commands. */
initialize_subshell ();
set_sigint_handler ();
larray = strvec_len (args) + 1;
args = strvec_resize (args, larray + 1);
for (i = larray - 1; i; i--)
args[i] = args[i - 1];
args[0] = shell_name;
args[1] = command;
args[larray] = (char *)NULL;
if (args[0][0] == '-')
args[0]++;
#if defined (RESTRICTED_SHELL)
if (restricted)
change_flag ('r', FLAG_OFF);
#endif
if (subshell_argv)
{
for (i = 1; i < subshell_argc; i++)
free (subshell_argv[i]);
free (subshell_argv);
}
dispose_command (currently_executing_command);
currently_executing_command = (COMMAND *)NULL;
subshell_argc = larray;
subshell_argv = args;
subshell_envp = env;
unbind_args ();
longjmp (subshell_top_level, 1);
}
static int
execute_intern_function (name, function)
WORD_DESC *name;
COMMAND *function;
{
SHELL_VAR *var;
if (check_identifier (name, posixly_correct) == 0)
{
if (posixly_correct && interactive_shell == 0)
{
last_command_exit_value = EX_BADUSAGE;
jump_to_top_level (ERREXIT);
}
return (EXECUTION_FAILURE);
}
var = find_function (name->word);
if (var && (readonly_p (var) || noassign_p (var)))
{
if (readonly_p (var))
internal_error (_("%s: readonly function"), var->name);
return (EXECUTION_FAILURE);
}
bind_function (name->word, function);
return (EXECUTION_SUCCESS);
}
#if defined (INCLUDE_UNUSED)
#if defined (PROCESS_SUBSTITUTION)
void
close_all_files ()
{
register int i, fd_table_size;
fd_table_size = getdtablesize ();
if (fd_table_size > 256)
fd_table_size = 256;
for (i = 3; i < fd_table_size; i++)
close (i);
}
#endif
#endif
static void
close_pipes (in, out)
int in, out;
{
if (in >= 0)
close (in);
if (out >= 0)
close (out);
}
static void
dup_error (oldd, newd)
int oldd, newd;
{
sys_error (_("cannot duplicate fd %d to fd %d"), oldd, newd);
}
NO_PIPE and REDIRECT_BOTH are handled correctly. */
static void
do_piping (pipe_in, pipe_out)
int pipe_in, pipe_out;
{
if (pipe_in != NO_PIPE)
{
if (dup2 (pipe_in, 0) < 0)
dup_error (pipe_in, 0);
if (pipe_in > 0)
close (pipe_in);
}
if (pipe_out != NO_PIPE)
{
if (pipe_out != REDIRECT_BOTH)
{
if (dup2 (pipe_out, 1) < 0)
dup_error (pipe_out, 1);
if (pipe_out == 0 || pipe_out > 1)
close (pipe_out);
}
else
{
if (dup2 (1, 2) < 0)
dup_error (1, 2);
}
}
}