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 1, or (at your option)
+ the Free Software Foundation; either version 2, or (at your option)
any later version.
Bash is distributed in the hope that it will be useful, but WITHOUT
You should have received a copy of the GNU General Public License
along with Bash; see the file COPYING. If not, write to the Free
- Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+ Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
#include "config.h"
#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
# include <limits.h>
#endif
-/* Some systems require this, mostly for the definition of `struct timezone'.
- For example, Dynix/ptx has that definition in <time.h> rather than
- sys/time.h */
-#if defined (TIME_WITH_SYS_TIME)
-# include <sys/time.h>
-# include <time.h>
-#else
-# if defined (HAVE_SYS_TIME_H)
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
-#endif
+#include "posixtime.h"
#if defined (HAVE_SYS_RESOURCE_H) && !defined (RLIMTYPE)
# include <sys/resource.h>
#if defined (COMMAND_TIMING)
static int time_command ();
#endif
+#if defined (ARITH_FOR_COMMAND)
+static int execute_arith_for_command ();
+#endif
static int execute_case_command ();
static int execute_while_command (), execute_until_command ();
static int execute_while_or_until ();
static int execute_connection ();
static int execute_intern_function ();
+static int execute_in_subshell ();
+
/* The line number that the currently executing function starts on. */
static int function_line_number;
environment. */
int subshell_environment;
+/* Currently-executing shell function. */
+SHELL_VAR *this_shell_function;
+
struct fd_bitmap *current_fds_to_close = (struct fd_bitmap *)NULL;
#define FD_BITMAP_DEFAULT_SIZE 32L
discard_unwind_frame ("execute-command");
#if defined (PROCESS_SUBSTITUTION)
- unlink_fifo_list ();
+ /* don't unlink fifos if we're in a shell function; wait until the function
+ returns. */
+ if (variable_context == 0)
+ unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */
return (result);
switch (type)
{
case cm_for:
+#if defined (ARITH_FOR_COMMAND)
+ case cm_arith_for:
+#endif
#if defined (SELECT_COMMAND)
case cm_select:
#endif
/* If a command was being explicitly run in a subshell, or if it is
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 ((command->flags & (CMD_WANT_SUBSHELL|CMD_FORCE_SUBSHELL)) ||
+ 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)))
{
paren_pid = make_child (savestring (make_command_string (command)),
asynchronous);
if (paren_pid == 0)
- {
- int user_subshell, return_code, function_value, should_redir_stdin;
-
- should_redir_stdin = (asynchronous && (command->flags & CMD_STDIN_REDIR) &&
- pipe_in == NO_PIPE &&
- stdin_redirects (command->redirects) == 0);
-
- user_subshell = (command->flags & CMD_WANT_SUBSHELL) != 0;
- command->flags &= ~(CMD_FORCE_SUBSHELL | CMD_WANT_SUBSHELL | CMD_INVERT_RETURN);
-
- /* If a command is asynchronous in a subshell (like ( foo ) & or
- 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.
-
- 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. */
-
- /* Don't fork again, we are already in a subshell. A `doubly
- async' shell is not interactive, however. */
- if (asynchronous)
- {
-#if defined (JOB_CONTROL)
- /* If a construct like ( exec xxx yyy ) & is given while job
- 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 /* JOB_CONTROL */
- interactive_shell = 0;
- expand_aliases = 0;
- asynchronous = 0;
- }
-
- /* Subshells are neither login nor interactive. */
- login_shell = interactive = 0;
-
- subshell_environment = user_subshell ? SUBSHELL_PAREN : SUBSHELL_ASYNC;
-
- reset_terminating_signals (); /* in shell.c */
- /* Cancel traps, in trap.c. */
- restore_original_signals ();
- if (asynchronous)
- setup_async_signals ();
-
-#if defined (JOB_CONTROL)
- set_sigchld_handler ();
-#endif /* JOB_CONTROL */
-
- set_sigint_handler ();
-
-#if defined (JOB_CONTROL)
- /* Delete all traces that there were any jobs running. This is
- only for subshells. */
- without_job_control ();
-#endif /* JOB_CONTROL */
- do_piping (pipe_in, pipe_out);
-
- /* If this is a user subshell, set a flag if stdin was redirected.
- This is used later to decide whether to redirect fd 0 to
- /dev/null for async commands in the subshell. This adds more
- sh compatibility, but I'm not sure it's the right thing to do. */
- if (user_subshell)
- {
- stdin_redir = stdin_redirects (command->redirects);
- restore_default_signal (0);
- }
-
- if (fds_to_close)
- close_fd_bitmap (fds_to_close);
-
- /* If this is an asynchronous command (command &), we want to
- 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 ();
-
- /* Do redirections, then dispose of them before recursive call. */
- if (command->redirects)
- {
- if (do_redirections (command->redirects, 1, 0, 0) != 0)
- exit (EXECUTION_FAILURE);
-
- dispose_redirects (command->redirects);
- command->redirects = (REDIRECT *)NULL;
- }
-
- /* If this is a simple command, tell execute_disk_command that it
- 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, however, we cannot do this
- optimization. */
-#if 0
- if (user_subshell && command->type == cm_simple)
-#else
- if (user_subshell && command->type == cm_simple && (command->flags & CMD_TIME_PIPELINE) == 0)
-#endif
- {
- command->flags |= CMD_NO_FORK;
- command->value.Simple->flags |= CMD_NO_FORK;
- }
-
- /* If we're inside a function while executing this subshell, we
- need to handle a possible `return'. */
- function_value = 0;
- if (return_catch_flag)
- function_value = setjmp (return_catch);
-
- if (function_value)
- return_code = return_catch_value;
- else
- return_code = execute_command_internal
- (command, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
-
- /* If we were explicitly placed in a subshell with (), we need
- to do the `shell cleanup' things, such as running traps[0]. */
- if (user_subshell && signal_is_trapped (0))
- {
- last_command_exit_value = return_code;
- return_code = run_exit_trap ();
- }
-
- exit (return_code);
- }
+ exit (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
+ /* NOTREACHED */
else
{
close_pipes (pipe_in, pipe_out);
/* If we have to, invert the return value. */
if (invert)
- return ((last_command_exit_value == EXECUTION_SUCCESS)
- ? EXECUTION_FAILURE
- : EXECUTION_SUCCESS);
+ exec_result = ((last_command_exit_value == EXECUTION_SUCCESS)
+ ? EXECUTION_FAILURE
+ : EXECUTION_SUCCESS);
else
- return (last_command_exit_value);
+ exec_result = last_command_exit_value;
+
+ return (last_command_exit_value = exec_result);
}
else
{
}
if (was_debug_trap)
- run_debug_trap ();
+ {
+ last_command_exit_value = exec_result;
+ run_debug_trap ();
+ }
if (ignore_return == 0 && invert == 0 &&
((posixly_correct && interactive == 0 && special_builtin_failed) ||
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)
}
#if defined (COMMAND_TIMING)
-#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
-static struct timeval *
-difftimeval (d, t1, t2)
- struct timeval *d, *t1, *t2;
-{
- d->tv_sec = t2->tv_sec - t1->tv_sec;
- d->tv_usec = t2->tv_usec - t1->tv_usec;
- if (d->tv_usec < 0)
- {
- d->tv_usec += 1000000;
- d->tv_sec -= 1;
- if (d->tv_sec < 0) /* ??? -- BSD/OS does this */
- {
- d->tv_sec = 0;
- d->tv_usec = 0;
- }
- }
- return d;
-}
-
-static struct timeval *
-addtimeval (d, t1, t2)
- struct timeval *d, *t1, *t2;
-{
- d->tv_sec = t1->tv_sec + t2->tv_sec;
- d->tv_usec = t1->tv_usec + t2->tv_usec;
- if (d->tv_usec >= 1000000)
- {
- d->tv_usec -= 1000000;
- d->tv_sec += 1;
- }
- return d;
-}
-
-/* Do "cpu = ((user + sys) * 10000) / real;" with timevals.
- Barely-tested code from Deven T. Corzine <deven@ties.org>. */
-static int
-timeval_to_cpu (rt, ut, st)
- struct timeval *rt, *ut, *st; /* real, user, sys */
-{
- struct timeval t1, t2;
- register int i;
-
- addtimeval (&t1, ut, st);
- t2.tv_sec = rt->tv_sec;
- t2.tv_usec = rt->tv_usec;
- for (i = 0; i < 6; i++)
- {
- if ((t1.tv_sec > 99999999) || (t2.tv_sec > 99999999))
- break;
- t1.tv_sec *= 10;
- t1.tv_sec += t1.tv_usec / 100000;
- t1.tv_usec *= 10;
- t1.tv_usec %= 1000000;
- t2.tv_sec *= 10;
- t2.tv_sec += t2.tv_usec / 100000;
- t2.tv_usec *= 10;
- t2.tv_usec %= 1000000;
- }
- for (i = 0; i < 4; i++)
- {
- if (t1.tv_sec < 100000000)
- t1.tv_sec *= 10;
- else
- t2.tv_sec /= 10;
- }
-
- return ((t2.tv_sec == 0) ? 0 : t1.tv_sec / t2.tv_sec);
-}
-#endif /* HAVE_GETRUSAGE && HAVE_GETTIMEOFDAY */
+#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
+extern struct timeval *difftimeval();
+extern struct timeval *addtimeval();
+extern int timeval_to_cpu();
+#endif
#define POSIX_TIMEFORMAT "real %2R\nuser %2U\nsys %2S"
#define BASH_TIMEFORMAT "\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS"
}
#endif /* COMMAND_TIMING */
+/* Execute a command that's supposed to be in a subshell. This must be
+ 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;
+ COMMAND *tcom;
+
+ 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);
+
+ command->flags &= ~(CMD_FORCE_SUBSHELL | CMD_WANT_SUBSHELL | CMD_INVERT_RETURN);
+
+ /* If a command is asynchronous in a subshell (like ( foo ) & or
+ 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.
+
+ 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. */
+
+ /* Don't fork again, we are already in a subshell. A `doubly
+ async' shell is not interactive, however. */
+ if (asynchronous)
+ {
+#if defined (JOB_CONTROL)
+ /* If a construct like ( exec xxx yyy ) & is given while job
+ 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 /* JOB_CONTROL */
+ ois = interactive_shell;
+ interactive_shell = 0;
+ /* This test is to prevent alias expansion by interactive shells that
+ 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;
+ asynchronous = 0;
+ }
+
+ /* Subshells are neither login nor interactive. */
+ login_shell = interactive = 0;
+
+ subshell_environment = user_subshell ? SUBSHELL_PAREN : SUBSHELL_ASYNC;
+
+ reset_terminating_signals (); /* in sig.c */
+ /* Cancel traps, in trap.c. */
+ restore_original_signals ();
+ if (asynchronous)
+ setup_async_signals ();
+
+#if defined (JOB_CONTROL)
+ set_sigchld_handler ();
+#endif /* JOB_CONTROL */
+
+ set_sigint_handler ();
+
+#if defined (JOB_CONTROL)
+ /* Delete all traces that there were any jobs running. This is
+ only for subshells. */
+ without_job_control ();
+#endif /* JOB_CONTROL */
+
+ if (fds_to_close)
+ close_fd_bitmap (fds_to_close);
+
+ do_piping (pipe_in, pipe_out);
+
+ /* If this is a user subshell, set a flag if stdin was redirected.
+ This is used later to decide whether to redirect fd 0 to
+ /dev/null for async commands in the subshell. This adds more
+ sh compatibility, but I'm not sure it's the right thing to do. */
+ if (user_subshell)
+ {
+ stdin_redir = stdin_redirects (command->redirects);
+ restore_default_signal (0);
+ }
+
+ /* If this is an asynchronous command (command &), we want to
+ 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 ();
+
+ /* Do redirections, then dispose of them before recursive call. */
+ if (command->redirects)
+ {
+ if (do_redirections (command->redirects, 1, 0, 0) != 0)
+ exit (invert ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
+
+ dispose_redirects (command->redirects);
+ command->redirects = (REDIRECT *)NULL;
+ }
+
+ tcom = (command->type == cm_subshell) ? command->value.Subshell->command : command;
+
+ /* If this is a simple command, tell execute_disk_command that it
+ 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 && (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;
+
+ /* If we're inside a function while executing this subshell, we
+ need to handle a possible `return'. */
+ function_value = 0;
+ if (return_catch_flag)
+ function_value = setjmp (return_catch);
+
+ if (function_value)
+ return_code = return_catch_value;
+ else
+ return_code = execute_command_internal
+ (tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);
+
+ /* If we are asked to, invert the return value. */
+ if (invert)
+ return_code = (return_code == EXECUTION_SUCCESS) ? EXECUTION_FAILURE
+ : EXECUTION_SUCCESS;
+
+ /* If we were explicitly placed in a subshell with (), we need
+ to do the `shell cleanup' things, such as running traps[0]. */
+ if (user_subshell && signal_is_trapped (0))
+ {
+ last_command_exit_value = return_code;
+ return_code = run_exit_trap ();
+ }
+
+ return (return_code);
+ /* NOTREACHED */
+}
+
static int
execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
COMMAND *command;
return exec_result;
}
-#if defined (JOB_CONTROL)
-# define REAP() \
- do \
- { \
- if (!interactive_shell) \
- reap_dead_jobs (); \
- } \
- while (0)
-#else /* !JOB_CONTROL */
-# define REAP() \
- do \
- { \
- if (!interactive_shell) \
- cleanup_dead_jobs (); \
- } \
- while (0)
-#endif /* !JOB_CONTROL */
-
+#define REAP() \
+ do \
+ { \
+ if (!interactive_shell) \
+ reap_dead_jobs (); \
+ } \
+ while (0)
/* Execute a FOR command. The syntax is: FOR word_desc IN word_list;
DO command; DONE */
QUIT;
this_command_name = (char *)NULL;
v = bind_variable (identifier, list->word->word);
- if (readonly_p (v))
+ if (readonly_p (v) || noassign_p (v))
{
- if (interactive_shell == 0 && posixly_correct)
+ if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
{
last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level (FORCE_EOF);
return (retval);
}
+#if defined (ARITH_FOR_COMMAND)
+/* Execute an arithmetic for command. The syntax is
+
+ 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 long
+eval_arith_for_expr (l, okp)
+ WORD_LIST *l;
+ int *okp;
+{
+ WORD_LIST *new;
+ long expresult;
+
+ new = expand_words_no_vars (l);
+ if (echo_command_at_execute)
+ xtrace_print_arith_cmd (new);
+ expresult = evalexp (new->word->word, okp);
+ dispose_words (new);
+ return (expresult);
+}
+
+static int
+execute_arith_for_command (arith_for_command)
+ ARITH_FOR_COM *arith_for_command;
+{
+ long expresult;
+ int expok, result, body_status;
+
+ body_status = EXECUTION_SUCCESS;
+ loop_level++;
+
+ if (arith_for_command->flags & CMD_IGNORE_RETURN)
+ arith_for_command->action->flags |= CMD_IGNORE_RETURN;
+
+ this_command_name = "(("; /* )) for expression error messages */
+
+ if (variable_context)
+ line_number = arith_for_command->line - function_line_number;
+
+ /* Evaluate the initialization expression. */
+ expresult = eval_arith_for_expr (arith_for_command->init, &expok);
+ if (expok == 0)
+ return (EXECUTION_FAILURE);
+
+ while (1)
+ {
+ /* Evaluate the test expression. */
+ expresult = eval_arith_for_expr (arith_for_command->test, &expok);
+ if (expok == 0)
+ {
+ body_status = EXECUTION_FAILURE;
+ break;
+ }
+ REAP ();
+ if (expresult == 0)
+ break;
+
+ /* Execute the body of the arithmetic for command. */
+ QUIT;
+ body_status = execute_command (arith_for_command->action);
+ QUIT;
+
+ /* Handle any `break' or `continue' commands executed by the body. */
+ if (breaking)
+ {
+ breaking--;
+ break;
+ }
+
+ if (continuing)
+ {
+ continuing--;
+ if (continuing)
+ break;
+ }
+
+ /* Evaluate the step expression. */
+ expresult = eval_arith_for_expr (arith_for_command->step, &expok);
+ if (expok == 0)
+ {
+ body_status = EXECUTION_FAILURE;
+ break;
+ }
+ }
+
+ loop_level--;
+ return (body_status);
+}
+#endif
+
#if defined (SELECT_COMMAND)
static int LINES, COLS, tabsize;
break;
v = bind_variable (identifier, selection);
- if (readonly_p (v))
+ if (readonly_p (v) || noassign_p (v))
{
- if (interactive_shell == 0 && posixly_correct)
+ if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
{
last_command_exit_value = EXECUTION_FAILURE;
jump_to_top_level (FORCE_EOF);
breaking--;
break;
}
+
+ if (continuing)
+ {
+ continuing--;
+ if (continuing)
+ break;
+ }
}
loop_level--;
case_command->word->word = word;
}
- wlist = expand_word_no_split (case_command->word, 0);
+ wlist = expand_word_unsplit (case_command->word, 0);
word = wlist ? string_list (wlist) : savestring ("");
dispose_words (wlist);
if (arg == 0)
arg = "";
var = bind_variable ("_", arg);
- var->attributes &= ~att_exported;
+ VUNSETATTR (var, att_exported);
}
/* Execute a null command. Fork a subshell if the command uses pipes or is
static int
execute_null_command (redirects, pipe_in, pipe_out, async, old_last_command_subst_pid)
REDIRECT *redirects;
- int pipe_in, pipe_out, async, old_last_command_subst_pid;
+ int pipe_in, pipe_out, async;
+ pid_t old_last_command_subst_pid;
{
if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
{
/* XXX memory leak if expand_words() error causes a jump_to_top_level */
command_line = savestring (the_printed_command);
+ /* Do this now, because execute_disk_command will do it anyway in the
+ vast majority of cases. */
+ maybe_make_export_env ();
+
if (make_child (command_line, async) == 0)
{
already_forked = 1;
simple_command->flags |= CMD_NO_FORK;
+ subshell_environment = (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
+ ? (SUBSHELL_PIPE|SUBSHELL_FORK)
+ : (SUBSHELL_ASYNC|SUBSHELL_FORK);
+
+ /* We need to do this before piping to handle some really
+ 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 = -1;
last_asynchronous_pid = old_last_async_pid;
- subshell_environment = async ? SUBSHELL_ASYNC : SUBSHELL_FORK;
}
else
{
if (builtin || func)
{
- if (already_forked)
+ if (already_forked)
{
/* reset_terminating_signals (); */ /* XXX */
/* Cancel traps, in trap.c. */
add_unwind_protect (dispose_builtin_env, (char *)NULL);
dispose_used_env_vars ();
}
-#if 0
- else
- builtin_env = (char **)NULL;
-#endif
+ /* Otherwise we inherit builtin_env from our caller. */
+ }
+
+ /* `return' does a longjmp() back to a saved environment in execute_function.
+ If a variable assignment list preceded the command, and the shell is
+ running in POSIX mode, we need to merge that into the shell_variables
+ table, since `return' is a POSIX special builtin. */
+ if (posixly_correct && subshell == 0 && builtin == return_builtin && temporary_env)
+ {
+ begin_unwind_frame ("return_temp_env");
+ add_unwind_protect (merge_temporary_env, (char *)NULL);
}
result = ((*builtin) (words->next));
+ /* This shouldn't happen, but in case `return' comes back instead of
+ 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 && (builtin == source_builtin || builtin == eval_builtin))
{
/* In POSIX mode, if any variable assignments precede the `.' or
and `eval' are special builtins. */
if (posixly_correct && builtin_env)
merge_builtin_env ();
-#if 0
- dispose_builtin_env ();
- discard_unwind_frame ("builtin_env");
-#else
+
run_unwind_frame ("builtin_env");
-#endif
}
if (eval_unwind)
int return_val, result;
COMMAND *tc, *fc;
char *debug_trap;
+ SHELL_VAR *old_shell_function;
tc = (COMMAND *)copy_command (function_cell (var));
if (tc && (flags & CMD_IGNORE_RETURN))
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);
}
+ this_shell_function = var;
+ make_funcname_visible (1);
+
debug_trap = (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
? trap_list[DEBUG_TRAP]
: (char *)NULL;
if (temporary_env)
{
function_env = copy_array (temporary_env);
+ /* In POSIX mode, variable assignments preceding function names are
+ supposed to persist in the environment after the function returns,
+ as if a special builtin command had been executed. */
if (subshell == 0)
- add_unwind_protect (dispose_function_env, (char *)NULL);
+ {
+ if (posixly_correct)
+ add_unwind_protect (merge_function_env, (char *)NULL);
+ else
+ add_unwind_protect (dispose_function_env, (char *)NULL);
+ }
dispose_used_env_vars ();
}
-#if 0
- else
- function_env = (char **)NULL;
-#endif
+ /* Otherwise, we inherit function_env from our caller. */
remember_args (words->next, 1);
if (subshell == 0)
run_unwind_frame ("function_calling");
+ if (variable_context == 0 || this_shell_function == 0)
+ make_funcname_visible (0);
+
return (result);
}
+/* A convenience routine for use by other parts of the shell to execute
+ 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;
+}
+
/* Execute a shell builtin or function in a subshell environment. This
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
struct fd_bitmap *fds_to_close;
int flags;
{
- int result, r, jobs_hack;
-
- /* A subshell is neither a login shell nor interactive. */
- login_shell = interactive = 0;
+ int result, r;
+#if defined (JOB_CONTROL)
+ int jobs_hack;
jobs_hack = (builtin == jobs_builtin) &&
((subshell_environment & SUBSHELL_ASYNC) == 0 || pipe_out != NO_PIPE);
+#endif
+
+ /* A subshell is neither a login shell nor interactive. */
+ login_shell = interactive = 0;
subshell_environment = SUBSHELL_ASYNC;
set_sigint_handler ();
- do_piping (pipe_in, pipe_out);
-
if (fds_to_close)
close_fd_bitmap (fds_to_close);
+ do_piping (pipe_in, pipe_out);
+
if (do_redirections (redirects, 1, 0, 0) != 0)
exit (EXECUTION_FAILURE);
{
int result;
REDIRECT *saved_undo_list;
+ Function *saved_this_shell_builtin;
if (do_redirections (redirects, 1, 1, 0) != 0)
{
return (EX_REDIRFAIL); /* was EXECUTION_FAILURE */
}
+ saved_this_shell_builtin = this_shell_builtin;
saved_undo_list = redirection_undo_list;
/* Calling the "exec" builtin changes redirections forever. */
else
result = execute_function (var, words, flags, fds_to_close, 0, 0);
+ /* If we are executing the `command' builtin, but this_shell_builtin is
+ 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;
{
char *pathname, *command, **args;
int nofork;
- int pid;
+ pid_t pid;
nofork = (cmdflags & CMD_NO_FORK); /* Don't fork, just exec, if no pipes */
pathname = words->word->word;
put_command_name_into_env (command);
}
- /* We have to make the child before we check for the non-existance
+ /* We have to make the child before we check for the non-existence
of COMMAND, since we want the error messages to be redirected. */
/* If we can get away without forking and there are no pipes to deal with,
don't bother to fork, just directly exec the command. */
setup_async_signals ();
}
+ /* This functionality is now provided by close-on-exec of the
+ 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);
if (async)
subshell_environment = SUBSHELL_FORK;
- /* This functionality is now provided by close-on-exec of the
- 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);
-
if (redirects && (do_redirections (redirects, 1, 0, 0) != 0))
{
#if defined (PROCESS_SUBSTITUTION)
The word immediately following the #! is the interpreter to execute.
A single argument to the interpreter is allowed. */
+
+/* CPP defines to decide whether a particular index into the #! line
+ 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) \
+ (!whitespace (sample[ind]) && sample[ind] != '\n' && ind < sample_len)
+# define WHITECHAR(ind) \
+ (whitespace (sample[ind]) && sample[ind] != '\n' && ind < sample_len)
+#else /* MSDOS */
+# define STRINGCHAR(ind) \
+ (!whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r' && ind < sample_len)
+# define WHITECHAR(ind) \
+ (whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r' && ind < sample_len)
+#endif /* MSDOS */
+
static int
execute_shell_script (sample, sample_len, command, args, env)
unsigned char *sample;
for (i = 2; whitespace (sample[i]) && i < sample_len; i++)
;
- for (start = i;
- !whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
- i++)
+ for (start = i; STRINGCHAR(i); i++)
;
- larry = i - start;
- execname = xmalloc (1 + larry);
- strncpy (execname, (char *)(sample + start), larry);
- execname[larry] = '\0';
+ execname = substring ((char *)sample, start, i);
size_increment = 1;
/* Now the argument, if any. */
- firstarg = (char *)NULL;
- for (start = i;
- whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
- i++)
+ for (firstarg = (char *)NULL, start = i; WHITECHAR(i); i++)
;
/* If there is more text on the line, then it is an argument for the
interpreter. */
- if (i < sample_len && sample[i] != '\n' && !whitespace (sample[i]))
+
+ if (STRINGCHAR(i))
{
- for (start = i;
- !whitespace (sample[i]) && sample[i] != '\n' && i < sample_len;
- i++)
+ for (start = i; STRINGCHAR(i); i++)
;
- larry = i - start;
- firstarg = xmalloc (1 + larry);
- strncpy (firstarg, (char *)(sample + start), larry);
- firstarg[larry] = '\0';
-
+ firstarg = substring ((char *)sample, start, i);
size_increment = 2;
}
return (shell_execve (execname, args, env));
}
+#undef STRINGCHAR
+#undef WHITECHAR
+
#endif /* !HAVE_HASH_BANG_EXEC */
static void
reset_shell_options ();
reset_shopt_options ();
+ /* Zero out builtin_env, since this could be a shell script run from a
+ 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). */
+ builtin_env = 0;
+
+ clear_unwind_protect_list (0);
+
+ /* We're no longer inside a shell function. */
+ variable_context = return_catch_flag = 0;
+
/* If we're not interactive, close the file descriptor from which we're
reading the current shell script. */
if (interactive_shell == 0)
# define SETOSTYPE(x)
#endif
+#define READ_SAMPLE_BUF(file, buf, len) \
+ do \
+ { \
+ fd = open(file, O_RDONLY); \
+ if (fd >= 0) \
+ { \
+ len = read (fd, (char *)buf, 80); \
+ close (fd); \
+ } \
+ else \
+ len = -1; \
+ } \
+ while (0)
+
/* Call execve (), handling interpreting shell scripts, and handling
exec failures. */
int
{
struct stat finfo;
int larray, i, fd;
+ unsigned char sample[80];
+ int sample_len;
SETOSTYPE (0); /* Some systems use for USG/POSIX semantics */
execve (command, args, env);
+ i = errno; /* error from execve() */
SETOSTYPE (1);
/* If we get to this point, then start checking out the file.
Maybe it is something we can hack ourselves. */
- if (errno != ENOEXEC)
+ if (i != ENOEXEC)
{
- i = errno;
if ((stat (command, &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
internal_error ("%s: is a directory", command);
else
{
+#if defined (HAVE_HASH_BANG_EXEC)
+ READ_SAMPLE_BUF (command, sample, sample_len);
+ if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
+ {
+ errno = i;
+ sys_error ("%s: bad interpreter", command);
+ return (EX_NOEXEC);
+ }
+#endif
errno = i;
file_error (command);
}
/* This file is executable.
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 first line (or up to 80 characters) are in the ASCII set.
- Execute the contents as shell commands. */
- fd = open (command, O_RDONLY);
- if (fd >= 0)
- {
- unsigned char sample[80];
- int sample_len;
-
- sample_len = read (fd, (char *)sample, 80);
- close (fd);
+ 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 (sample_len == 0)
+ return (EXECUTION_SUCCESS);
- /* Is this supposed to be an executable script?
- 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)
- {
+ /* Is this supposed to be an executable script?
+ 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[0] == '#' && sample[1] == '!')
- return (execute_shell_script (sample, sample_len, command, args, env));
- else
+ 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);
- }
+ if (check_binary_file (sample, sample_len))
+ {
+ internal_error ("%s: cannot execute binary file", command);
+ return (EX_BINARY_FILE);
}
}
+ /* We have committed to attempting to execute the contents of this file
+ as shell commands. */
+
initialize_subshell ();
set_sigint_handler ();
}
var = find_function (name->word);
- if (var && readonly_p (var))
+ if (var && (readonly_p (var) || noassign_p (var)))
{
- internal_error ("%s: readonly function", var->name);
+ if (readonly_p (var))
+ internal_error ("%s: readonly function", var->name);
return (EXECUTION_FAILURE);
}
sys_error ("cannot duplicate fd %d to fd 0", pipe_in);
if (pipe_in > 0)
close (pipe_in);
-#ifdef __CYGWIN32__
- setmode (0, O_TEXT);
-#endif
}
if (pipe_out != NO_PIPE)
{
sys_error ("cannot duplicate fd %d to fd 1", pipe_out);
if (pipe_out == 0 || pipe_out > 1)
close (pipe_out);
-#ifdef __CYGWIN32__
- setmode (1, O_TEXT);
-#endif
}
else
{
if (dup2 (1, 2) < 0)
sys_error ("cannot duplicate fd 1 to fd 2");
-#ifdef __CYGWIN32__
- setmode (1, O_TEXT);
- setmode (2, O_TEXT);
-#endif
}
}
}