X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=execute_cmd.c;h=79abc28ce128fc63e41f71872e7d5dc2a2443649;hb=28ef6c316f1aff914bb95ac09787a3c83c1815fd;hp=5f57840f98cfafe4e87ccd5f0f18074f7cd10c1d;hpb=d166f048818e10cf3799aa24a174fb22835f1acc;p=platform%2Fupstream%2Fbash.git diff --git a/execute_cmd.c b/execute_cmd.c index 5f57840..79abc28 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -6,7 +6,7 @@ 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 @@ -16,7 +16,7 @@ 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) @@ -26,11 +26,15 @@ #include #include #include "bashtypes.h" -#include +#ifndef _MINIX +# include +#endif #include "filecntl.h" #include "posixstat.h" #include -#include +#ifndef _MINIX +# include +#endif #if defined (HAVE_UNISTD_H) # include @@ -40,21 +44,9 @@ # include #endif -/* Some systems require this, mostly for the definition of `struct timezone'. - For example, Dynix/ptx has that definition in rather than - sys/time.h */ -#if defined (TIME_WITH_SYS_TIME) -# include -# include -#else -# if defined (HAVE_SYS_TIME_H) -# include -# else -# include -# endif -#endif +#include "posixtime.h" -#if defined (HAVE_SYS_RESOURCE_H) +#if defined (HAVE_SYS_RESOURCE_H) && !defined (RLIMTYPE) # include #endif @@ -78,10 +70,16 @@ extern int errno; #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" /* list of builtins */ @@ -121,20 +119,23 @@ extern int close (); /* Static functions defined and used in this file. */ static void close_pipes (), do_piping (), bind_lastarg (); static void cleanup_redirects (); -static void add_undo_close_redirect (), add_exec_redirect (); -static int add_undo_redirect (); -static int do_redirection_internal (), do_redirections (); -static int expandable_redirection_filename (); -static char *find_user_command_internal (), *find_user_command_in_path (); -static char *find_in_path_element (), *find_absolute_program (); static int execute_for_command (); #if defined (SELECT_COMMAND) static int execute_select_command (); #endif +#if defined (DPAREN_ARITHMETIC) +static int execute_arith_command (); +#endif +#if defined (COND_COMMAND) +static int execute_cond_command (); +#endif #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 (); @@ -148,6 +149,8 @@ static void execute_disk_command (); 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; @@ -165,21 +168,6 @@ struct stat SB; /* used for debugging */ static int special_builtin_failed; -/* Spare redirector used when translating [N]>&WORD or [N]<&WORD to a new - redirection and when creating the redirection undo list. */ -static REDIRECTEE rd; - -/* Set to errno when a here document cannot be created for some reason. - Used to print a reasonable error message. */ -static int heredoc_errno; - -/* The file name which we would try to execute, except that it isn't - possible to execute it. This is the first file that matches the - name that we are looking for while we are searching $PATH for a - suitable one to execute. If we cannot find a suitable executable - file, then we use this one. */ -static char *file_to_lose_on; - /* For catching RETURN in a function. */ int return_catch_flag; int return_catch_value; @@ -201,9 +189,8 @@ REDIRECT *exec_redirection_undo_list = (REDIRECT *)NULL; environment. */ int subshell_environment; -/* Non-zero if we should stat every command found in the hash table to - make sure it still exists. */ -int check_hashed_filenames; +/* Currently-executing shell function. */ +SHELL_VAR *this_shell_function; struct fd_bitmap *current_fds_to_close = (struct fd_bitmap *)NULL; @@ -296,7 +283,10 @@ execute_command (command) 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); @@ -310,9 +300,18 @@ shell_control_structure (type) switch (type) { case cm_for: +#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: @@ -345,7 +344,7 @@ cleanup_func_redirects (list) } #endif -static void +void dispose_exec_redirects () { if (exec_redirection_undo_list) @@ -384,41 +383,6 @@ open_files () fprintf (stderr, "\n"); } -static int -stdin_redirects (redirs) - REDIRECT *redirs; -{ - REDIRECT *rp; - int n; - - for (n = 0, rp = redirs; rp; rp = rp->next) - switch (rp->instruction) - { - case r_input_direction: - case r_inputa_direction: - case r_input_output: - case r_reading_until: - case r_deblank_reading_until: - n++; - break; - case r_duplicating_input: - case r_duplicating_input_word: - case r_close_this: - n += (rp->redirector == 0); - break; - case r_output_direction: - case r_appending_to: - case r_duplicating_output: - case r_err_and_out: - case r_output_force: - case r_duplicating_output_word: - break; - } - - return n; -} - - static void async_redirect_stdin () { @@ -458,7 +422,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, { int exec_result, invert, ignore_return, was_debug_trap; REDIRECT *my_undo_list, *exec_undo_list; - pid_t last_pid; + volatile pid_t last_pid; if (command == 0 || breaking || continuing || read_but_dont_execute) return (EXECUTION_SUCCESS); @@ -481,8 +445,11 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, /* 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))) { @@ -493,128 +460,8 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, 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 (user_subshell && command->type == cm_simple) - { - 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); @@ -638,11 +485,13 @@ execute_command_internal (command, asynchronous, 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 { @@ -769,14 +618,24 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, commands, this causes the last simple command in the function to be waited for twice. */ exec_result = wait_for (last_made_pid); +#if defined (RECYCLES_PIDS) + /* LynxOS, for one, recycles pids very quickly -- so quickly + that a new process may have the same pid as the last one + created. This has been reported to fix the problem. */ + if (exec_result == 0) + last_made_pid = NO_PID; +#endif } } 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) || + ((posixly_correct && interactive == 0 && special_builtin_failed) || (exit_immediately_on_error && (exec_result != EXECUTION_SUCCESS)))) { last_command_exit_value = exec_result; @@ -792,6 +651,14 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, 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) @@ -873,14 +740,29 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, 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; + exec_result = execute_cond_command (command->value.Cond); + break; +#endif + case cm_function_def: exec_result = execute_intern_function (command->value.Function_def->name, command->value.Function_def->command); break; default: - programming_error - ("execute_command: bad command type `%d'", command->type); + command_error ("execute_command", CMDERR_BADTYPE, command->type, 0); } if (my_undo_list) @@ -909,74 +791,12 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, } #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; - } - 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 . */ -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" @@ -1009,7 +829,7 @@ mkfmt (buf, prec, lng, sec, sec_fraction) while (min /= 10); aind++; while (abuf[aind]) - buf[ind++] = abuf[aind++]; + buf[ind++] = abuf[aind++]; buf[ind++] = 'm'; } @@ -1068,7 +888,8 @@ print_formatted_time (fp, format, rs, rsf, us, usf, ss, ssf, cpu) { int prec, lng, len; char *str, *s, ts[32]; - int sum, sum_frac; + long sum; + int sum_frac; int sindex, ssize; len = strlen (format); @@ -1079,16 +900,16 @@ print_formatted_time (fp, format, rs, rsf, us, usf, ss, ssf, cpu) for (s = format; *s; s++) { if (*s != '%' || s[1] == '\0') - { - RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64); - str[sindex++] = *s; - } + { + 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; - } + { + s++; + RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64); + str[sindex++] = *s; + } else if (s[1] == 'P') { s++; @@ -1182,6 +1003,9 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close) rv = execute_command_internal (command, asynchronous, pipe_in, pipe_out, fds_to_close); command->flags = old_flags; + rs = us = ss = 0L; + rsf = usf = ssf = cpu = 0; + #if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY) gettimeofday (&after, &dtz); getrusage (RUSAGE_SELF, &selfa); @@ -1232,6 +1056,165 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close) } #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; @@ -1297,7 +1280,7 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close) fd_bitmap->bitmap[fildes[0]] = 1; /* In case there are pipe or out-of-processes errors, we - want all these file descriptors to be closed when + 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"); @@ -1484,7 +1467,7 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close) break; default: - programming_error ("execute_connection: bad connector `%d'", command->value.Connection->connector); + command_error ("execute_connection", CMDERR_BADCONN, command->value.Connection->connector, 0); jump_to_top_level (DISCARD); exec_result = EXECUTION_FAILURE; } @@ -1492,24 +1475,13 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close) 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 */ @@ -1528,10 +1500,10 @@ execute_for_command (for_command) if (check_identifier (for_command->name, 1) == 0) { if (posixly_correct && interactive_shell == 0) - { - last_command_exit_value = EX_USAGE; - jump_to_top_level (EXITPROG); - } + { + last_command_exit_value = EX_USAGE; + jump_to_top_level (EXITPROG); + } return (EXECUTION_FAILURE); } @@ -1560,9 +1532,9 @@ execute_for_command (for_command) 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); @@ -1615,6 +1587,107 @@ execute_for_command (for_command) 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; @@ -1716,15 +1789,16 @@ print_select_list (list, list_len, max_elem_len, indices_len) /* Print the elements of LIST, one per line, preceded by an index from 1 to 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, the loop is - executed again. */ + 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) WORD_LIST *list; int list_len; char *prompt; { - int max_elem_len, indices_len, len, reply; + int max_elem_len, indices_len, len; + long reply; WORD_LIST *l; char *repl_string, *t; @@ -1747,7 +1821,7 @@ select_query (list, list_len, prompt) { len = STRLEN (l->word->word); if (len > max_elem_len) - max_elem_len = len; + max_elem_len = len; } indices_len = NUMBER_LEN (list_len); max_elem_len += indices_len + RP_SPACE_LEN + 2; @@ -1767,7 +1841,8 @@ select_query (list, list_len, prompt) repl_string = get_string_value ("REPLY"); if (*repl_string == 0) continue; - reply = atoi (repl_string); + if (legal_number (repl_string, &reply) == 0) + return ""; if (reply < 1 || reply > list_len) return ""; @@ -1832,9 +1907,9 @@ execute_select_command (select_command) 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); @@ -1849,12 +1924,12 @@ execute_select_command (select_command) return_val = setjmp (return_catch); if (return_val) - { + { retval = return_catch_value; break; - } + } else - retval = execute_command (select_command->action); + retval = execute_command (select_command->action); REAP (); QUIT; @@ -1864,6 +1939,13 @@ execute_select_command (select_command) breaking--; break; } + + if (continuing) + { + continuing--; + if (continuing) + break; + } } loop_level--; @@ -1895,7 +1977,7 @@ execute_case_command (case_command) 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); @@ -1924,7 +2006,7 @@ execute_case_command (case_command) es = expand_word_leave_quoted (list->word, 0); if (es && es->word && es->word->word && *(es->word->word)) - pattern = quote_string_for_globbing (es->word->word, 1); + pattern = quote_string_for_globbing (es->word->word, QGLOB_CVTNULL); else { pattern = xmalloc (1); @@ -1934,7 +2016,7 @@ execute_case_command (case_command) /* Since the pattern does not undergo quote removal (as per Posix.2, section 3.9.4.3), the fnmatch () call must be able to recognize backslashes as escape characters. */ - match = fnmatch (pattern, word, 0) != FNM_NOMATCH; + match = fnmatch (pattern, word, FNMATCH_EXTFLAG) != FNM_NOMATCH; free (pattern); dispose_words (es); @@ -2061,16 +2143,145 @@ execute_if_command (if_command) } } -static void -bind_lastarg (arg) - char *arg; +#if defined (DPAREN_ARITHMETIC) +static int +execute_arith_command (arith_command) + ARITH_COM *arith_command; { - SHELL_VAR *var; + int result, expok, expresult; + WORD_LIST *new, *p, *printit; + WORD_DESC *w; - if (arg == 0) - arg = ""; - var = bind_variable ("_", arg); - var->attributes &= ~att_exported; + result = 0; + + this_command_name = "(("; /* )) */ + /* If we're in a function, update the line number information. */ + if (variable_context) + line_number = arith_command->line - function_line_number; + + new = expand_words (arith_command->exp); + + /* If we're tracing, make a new word list with `((' at the front and `))' + at the back and print it. */ + if (echo_command_at_execute) + xtrace_print_arith_cmd (new); + + result = evalexp (new->word->word, &expok); + dispose_words (new); + + if (expok == 0) + return (EXECUTION_FAILURE); + + return (result == 0 ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} +#endif /* DPAREN_ARITHMETIC */ + +#if defined (COND_COMMAND) + +static char *nullstr = ""; + +static int +execute_cond_node (cond) + COND_COM *cond; +{ + int result, invert, patmatch, flags; + char *arg1, *arg2, *print2; + + 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) + { + 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')); + + arg1 = cond_expand_word (cond->left->op, 0); + if (arg1 == 0) + arg1 = nullstr; + arg2 = cond_expand_word (cond->right->op, patmatch); + if (arg2 == 0) + arg2 = nullstr; + + if (echo_command_at_execute) + xtrace_print_cond_term (cond->type, invert, cond->op, arg1, arg2); + + result = binary_test (cond->op->word, arg1, arg2, TEST_PATMATCH|TEST_ARITHEXP) + ? EXECUTION_SUCCESS + : EXECUTION_FAILURE; + 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 result; + + result = EXECUTION_SUCCESS; + + this_command_name = "[["; + /* If we're in a function, update the line number information. */ + if (variable_context) + line_number = cond_command->line - function_line_number; + +#if 0 + debug_print_cond_command (cond_command); +#endif + last_command_exit_value = result = execute_cond_node (cond_command); + return (result); +} +#endif /* COND_COMMAND */ + +static void +bind_lastarg (arg) + char *arg; +{ + SHELL_VAR *var; + + if (arg == 0) + arg = ""; + var = bind_variable ("_", arg); + VUNSETATTR (var, att_exported); } /* Execute a null command. Fork a subshell if the command uses pipes or is @@ -2079,7 +2290,8 @@ bind_lastarg (arg) 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) { @@ -2138,13 +2350,13 @@ fix_assignment_words (words) if (words == 0) return; - b = builtin_address_internal (words->word->word); + b = builtin_address_internal (words->word->word, 0); if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0) return; for (w = words; w; w = w->next) if (w->word->flags & W_ASSIGNMENT) - w->word->flags |= W_NOSPLIT; + w->word->flags |= (W_NOSPLIT|W_NOGLOB); } /* The meaty part of all the executions. We have to start hacking the @@ -2158,13 +2370,14 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) { WORD_LIST *words, *lastword; char *command_line, *lastarg, *temp; - int first_word_quoted, result, builtin_is_special; - pid_t old_last_command_subst_pid; + int first_word_quoted, result, builtin_is_special, already_forked, dofork; + pid_t old_last_command_subst_pid, old_last_async_pid; Function *builtin; SHELL_VAR *func; result = EXECUTION_SUCCESS; special_builtin_failed = builtin_is_special = 0; + command_line = (char *)0; /* If we're in a function, update the line number information. */ if (variable_context) @@ -2173,13 +2386,69 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) /* Remember what this command line looks like at invocation. */ command_string_index = 0; print_simple_command (simple_command); - command_line = xmalloc (1 + strlen (the_printed_command)); - strcpy (command_line, the_printed_command); /* XXX memory leak on errors */ first_word_quoted = simple_command->words ? (simple_command->words->word->flags & W_QUOTED): 0; old_last_command_subst_pid = last_command_subst_pid; + old_last_async_pid = last_asynchronous_pid; + + already_forked = dofork = 0; + + /* If we're in a pipeline or run in the background, set DOFORK so we + make the child early, before word expansion. This keeps assignment + statements from affecting the parent shell's environment when they + should not. */ + dofork = pipe_in != NO_PIPE || pipe_out != NO_PIPE || async; + + /* Something like `%2 &' should restart job 2 in the background, not cause + 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) + { + /* 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; + } + else + { + close_pipes (pipe_in, pipe_out); +#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD) + unlink_fifo_list (); +#endif + command_line = (char *)NULL; /* don't free this. */ + bind_lastarg ((char *)NULL); + return (result); + } + } /* If we are re-running this as the result of executing the `command' builtin, do not expand the command words a second time. */ @@ -2199,11 +2468,17 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) if (words == 0) { result = execute_null_command (simple_command->redirects, - pipe_in, pipe_out, async, + pipe_in, pipe_out, + already_forked ? 0 : async, old_last_command_subst_pid); - FREE (command_line); - bind_lastarg ((char *)NULL); - return (result); + if (already_forked) + exit (result); + else + { + bind_lastarg ((char *)NULL); + set_pipestatus_from_exit (result); + return (result); + } } lastarg = (char *)NULL; @@ -2243,7 +2518,7 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) #if defined (JOB_CONTROL) /* Is this command a job control related thing? */ - if (words->word->word[0] == '%') + if (words->word->word[0] == '%' && already_forked == 0) { this_command_name = async ? "bg" : "fg"; last_shell_builtin = this_shell_builtin; @@ -2255,7 +2530,7 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) /* One other possiblilty. The user may want to resume an existing job. If they do, find out whether this word is a candidate for a running job. */ - if (job_control && async == 0 && + if (job_control && already_forked == 0 && async == 0 && !first_word_quoted && !words->next && words->word->word[0] && @@ -2328,37 +2603,25 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) if (builtin || func) { - if ((pipe_in != NO_PIPE) || (pipe_out != NO_PIPE) || async) + if (already_forked) { - if (make_child (command_line, async) == 0) - { - /* reset_terminating_signals (); */ /* XXX */ - /* Cancel traps, in trap.c. */ - 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 (); - } + /* reset_terminating_signals (); */ /* XXX */ + /* Cancel traps, in trap.c. */ + restore_original_signals (); - execute_subshell_builtin_or_function - (words, simple_command->redirects, builtin, func, - pipe_in, pipe_out, async, fds_to_close, - simple_command->flags); - } - else + if (async) { - close_pipes (pipe_in, pipe_out); -#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD) - unlink_fifo_list (); -#endif - command_line = (char *)NULL; /* don't free this. */ - goto return_result; + if ((simple_command->flags & CMD_STDIN_REDIR) && + pipe_in == NO_PIPE && + (stdin_redirects (simple_command->redirects) == 0)) + async_redirect_stdin (); + setup_async_signals (); } + + execute_subshell_builtin_or_function + (words, simple_command->redirects, builtin, func, + pipe_in, pipe_out, async, fds_to_close, + simple_command->flags); } else { @@ -2369,9 +2632,9 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) { if (result > EX_SHERRBASE) { - result = builtin_status (result); - if (builtin_is_special) - special_builtin_failed = 1; + result = builtin_status (result); + if (builtin_is_special) + special_builtin_failed = 1; } /* In POSIX mode, if there are assignment statements preceding a special builtin, they persist after the builtin @@ -2384,13 +2647,18 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) if (result == EX_USAGE) result = EX_BADUSAGE; else if (result > EX_SHERRBASE) - result = EXECUTION_FAILURE; + result = EXECUTION_FAILURE; } + set_pipestatus_from_exit (result); + goto return_result; } } + if (command_line == 0) + command_line = savestring (the_printed_command); + execute_disk_command (words, simple_command->redirects, command_line, pipe_in, pipe_out, async, fds_to_close, simple_command->flags); @@ -2468,14 +2736,26 @@ execute_builtin (builtin, words, flags, subshell) 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 @@ -2483,8 +2763,8 @@ execute_builtin (builtin, words, flags, subshell) and `eval' are special builtins. */ if (posixly_correct && builtin_env) merge_builtin_env (); - dispose_builtin_env (); - discard_unwind_frame ("builtin_env"); + + run_unwind_frame ("builtin_env"); } if (eval_unwind) @@ -2506,6 +2786,7 @@ execute_function (var, words, flags, fds_to_close, async, subshell) 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)) @@ -2520,9 +2801,13 @@ execute_function (var, words, flags, fds_to_close, async, subshell) 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; @@ -2531,8 +2816,12 @@ execute_function (var, words, flags, fds_to_close, async, subshell) if (subshell == 0) { debug_trap = savestring (debug_trap); + /* XXX order is important here! unwind-protect commands are run + in reverse order of registering. If this causes problems, + take out the xfree unwind-protect and live with the small + memory leak. */ + add_unwind_protect (xfree, debug_trap); add_unwind_protect (set_debug_trap, debug_trap); - /* XXX - small memory leak here -- hard to fix */ } restore_default_signal (DEBUG_TRAP); } @@ -2542,14 +2831,19 @@ execute_function (var, words, flags, fds_to_close, async, subshell) 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); @@ -2582,9 +2876,34 @@ execute_function (var, words, flags, fds_to_close, async, subshell) 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 @@ -2604,6 +2923,12 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var, int flags; { 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; @@ -2618,8 +2943,7 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var, the shell itself. */ /* Allow the output of `jobs' to be piped. */ - if (builtin == jobs_builtin && !async && - (pipe_out != NO_PIPE || pipe_in != NO_PIPE)) + if (jobs_hack) kill_current_pipeline (); else without_job_control (); @@ -2629,11 +2953,11 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var, 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); @@ -2648,12 +2972,12 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var, else if (result) exit (EXECUTION_FAILURE); else - { - r = execute_builtin (builtin, words, flags, 1); - if (r == EX_USAGE) - r = EX_BADUSAGE; - exit (r); - } + { + r = execute_builtin (builtin, words, flags, 1); + if (r == EX_USAGE) + r = EX_BADUSAGE; + exit (r); + } } else exit (execute_function (var, words, flags, fds_to_close, async, 1)); @@ -2679,6 +3003,7 @@ execute_builtin_or_function (words, builtin, var, redirects, { int result; REDIRECT *saved_undo_list; + Function *saved_this_shell_builtin; if (do_redirections (redirects, 1, 1, 0) != 0) { @@ -2688,6 +3013,7 @@ execute_builtin_or_function (words, builtin, var, redirects, 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. */ @@ -2713,6 +3039,21 @@ execute_builtin_or_function (words, builtin, var, redirects, 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; @@ -2731,6 +3072,10 @@ execute_builtin_or_function (words, builtin, var, redirects, void setup_async_signals () { +#if defined (__BEOS__) + set_signal_handler (SIGHUP, SIG_IGN); /* they want csh-like behavior */ +#endif + #if defined (JOB_CONTROL) if (job_control == 0) #endif @@ -2770,7 +3115,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, { 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; @@ -2793,7 +3138,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, 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. */ @@ -2818,8 +3163,8 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, restore_original_signals (); /* restore_original_signals may have undone the work done - by make_child to ensure that SIGINT and SIGQUIT are ignored - in asynchronous children. */ + by make_child to ensure that SIGINT and SIGQUIT are ignored + in asynchronous children. */ if (async) { if ((cmdflags & CMD_STDIN_REDIR) && @@ -2829,6 +3174,14 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, 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) @@ -2839,14 +3192,6 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, 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) @@ -2893,6 +3238,24 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, 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; @@ -2908,37 +3271,24 @@ execute_shell_script (sample, sample_len, command, args, env) 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; } @@ -2962,6 +3312,9 @@ execute_shell_script (sample, sample_len, command, args, env) return (shell_execve (execname, args, env)); } +#undef STRINGCHAR +#undef WHITECHAR + #endif /* !HAVE_HASH_BANG_EXEC */ static void @@ -2989,21 +3342,21 @@ initialize_subshell () 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 defined (BUFFERED_INPUT) - if (interactive_shell == 0 && default_buffered_input >= 0) - { - close_buffered_fd (default_buffered_input); - default_buffered_input = bash_input.location.buffered_fd = -1; - } -#else - if (interactive_shell == 0 && default_input) - { - fclose (default_input); - default_input = (FILE *)NULL; - } -#endif + if (interactive_shell == 0) + unset_bash_input (1); } #if defined (HAVE_SETOSTYPE) && defined (_POSIX_SOURCE) @@ -3012,6 +3365,20 @@ initialize_subshell () # 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 @@ -3021,20 +3388,31 @@ shell_execve (command, args, env) { 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); } @@ -3044,41 +3422,37 @@ shell_execve (command, args, env) /* 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 (); @@ -3140,9 +3514,10 @@ execute_intern_function (name, function) } 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); } @@ -3188,7 +3563,7 @@ do_piping (pipe_in, pipe_out) if (dup2 (pipe_in, 0) < 0) sys_error ("cannot duplicate fd %d to fd 0", pipe_in); if (pipe_in > 0) - close (pipe_in); + close (pipe_in); } if (pipe_out != NO_PIPE) { @@ -3200,1224 +3575,9 @@ do_piping (pipe_in, pipe_out) close (pipe_out); } else - if (dup2 (1, 2) < 0) - sys_error ("cannot duplicate fd 1 to fd 2"); - } -} - -static void -redirection_error (temp, error) - REDIRECT *temp; - int error; -{ - char *filename; - - if (expandable_redirection_filename (temp)) - { - if (posixly_correct && !interactive_shell) - disallow_filename_globbing++; - filename = redirection_expand (temp->redirectee.filename); - if (posixly_correct && !interactive_shell) - disallow_filename_globbing--; - if (filename == 0) - filename = savestring (temp->redirectee.filename->word); - if (filename == 0) - { - filename = xmalloc (1); - filename[0] = '\0'; - } - } - else - filename = itos (temp->redirectee.dest); - - switch (error) - { - case AMBIGUOUS_REDIRECT: - internal_error ("%s: ambiguous redirect", filename); - break; - - case NOCLOBBER_REDIRECT: - internal_error ("%s: cannot overwrite existing file", filename); - break; - -#if defined (RESTRICTED_SHELL) - case RESTRICTED_REDIRECT: - internal_error ("%s: restricted: cannot redirect output", filename); - break; -#endif /* RESTRICTED_SHELL */ - - case HEREDOC_REDIRECT: - internal_error ("cannot create temp file for here document: %s", strerror (heredoc_errno)); - break; - - default: - internal_error ("%s: %s", filename, strerror (error)); - break; - } - - FREE (filename); -} - -/* Perform the redirections on LIST. If FOR_REAL, then actually make - input and output file descriptors, otherwise just do whatever is - neccessary for side effecting. INTERNAL says to remember how to - undo the redirections later, if non-zero. If SET_CLEXEC is non-zero, - file descriptors opened in do_redirection () have their close-on-exec - flag set. */ -static int -do_redirections (list, for_real, internal, set_clexec) - REDIRECT *list; - int for_real, internal, set_clexec; -{ - int error; - REDIRECT *temp; - - if (internal) - { - if (redirection_undo_list) - { - dispose_redirects (redirection_undo_list); - redirection_undo_list = (REDIRECT *)NULL; - } - if (exec_redirection_undo_list) - dispose_exec_redirects (); - } - - for (temp = list; temp; temp = temp->next) - { - error = do_redirection_internal (temp, for_real, internal, set_clexec); - if (error) { - redirection_error (temp, error); - return (error); + if (dup2 (1, 2) < 0) + sys_error ("cannot duplicate fd 1 to fd 2"); } } - return (0); -} - -/* Return non-zero if the redirection pointed to by REDIRECT has a - redirectee.filename that can be expanded. */ -static int -expandable_redirection_filename (redirect) - REDIRECT *redirect; -{ - switch (redirect->instruction) - { - case r_output_direction: - case r_appending_to: - case r_input_direction: - case r_inputa_direction: - case r_err_and_out: - case r_input_output: - case r_output_force: - case r_duplicating_input_word: - case r_duplicating_output_word: - return 1; - - default: - return 0; - } -} - -/* Expand the word in WORD returning a string. If WORD expands to - multiple words (or no words), then return NULL. */ -char * -redirection_expand (word) - WORD_DESC *word; -{ - char *result; - WORD_LIST *tlist1, *tlist2; - - tlist1 = make_word_list (copy_word (word), (WORD_LIST *)NULL); - tlist2 = expand_words_no_vars (tlist1); - dispose_words (tlist1); - - if (!tlist2 || tlist2->next) - { - /* We expanded to no words, or to more than a single word. - Dispose of the word list and return NULL. */ - if (tlist2) - dispose_words (tlist2); - return ((char *)NULL); - } - result = string_list (tlist2); /* XXX savestring (tlist2->word->word)? */ - dispose_words (tlist2); - return (result); -} - -/* Write the text of the here document pointed to by REDIRECTEE to the file - descriptor FD, which is already open to a temp file. Return 0 if the - write is successful, otherwise return errno. */ -static int -write_here_document (fd, redirectee) - int fd; - WORD_DESC *redirectee; -{ - char *document; - int document_len, fd2; - FILE *fp; - register WORD_LIST *t, *tlist; - - /* Expand the text if the word that was specified had - no quoting. The text that we expand is treated - exactly as if it were surrounded by double quotes. */ - - if (redirectee->flags & W_QUOTED) - { - document = redirectee->word; - document_len = strlen (document); - /* Set errno to something reasonable if the write fails. */ - if (write (fd, document, document_len) < document_len) - { - if (errno == 0) - errno = ENOSPC; - return (errno); - } - else - return 0; - } - - tlist = expand_string (redirectee->word, Q_HERE_DOCUMENT); - if (tlist) - { - /* Try using buffered I/O (stdio) and writing a word - at a time, letting stdio do the work of buffering - for us rather than managing our own strings. Most - stdios are not particularly fast, however -- this - may need to be reconsidered later. */ - if ((fd2 = dup (fd)) < 0 || (fp = fdopen (fd2, "w")) == NULL) - { - if (fd2 >= 0) - close (fd2); - return (errno); - } - errno = 0; - for (t = tlist; t; t = t->next) - { - /* This is essentially the body of - string_list_internal expanded inline. */ - document = t->word->word; - document_len = strlen (document); - if (t != tlist) - putc (' ', fp); /* separator */ - fwrite (document, document_len, 1, fp); - if (ferror (fp)) - { - if (errno == 0) - errno = ENOSPC; - fd2 = errno; - fclose(fp); - dispose_words (tlist); - return (fd2); - } - } - fclose (fp); - dispose_words (tlist); - } - return 0; -} - -/* Create a temporary file holding the text of the here document pointed to - by REDIRECTEE, and return a file descriptor open for reading to the temp - file. Return -1 on any error, and make sure errno is set appropriately. */ -static int -here_document_to_fd (redirectee) - WORD_DESC *redirectee; -{ - char filename[24]; - int r, fd; - - /* Make the filename for the temp file. */ - sprintf (filename, "/tmp/t%d-sh", (int)time ((time_t *) 0) + (int)getpid ()); - - /* Make sure we open it exclusively. */ - fd = open (filename, O_TRUNC | O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) - return (fd); - - errno = r = 0; /* XXX */ - /* write_here_document returns 0 on success, errno on failure. */ - if (redirectee->word) - r = write_here_document (fd, redirectee); - - close (fd); - if (r) - { - unlink (filename); - errno = r; - return (-1); - } - - /* XXX - this is raceable */ - /* Make the document really temporary. Also make it the input. */ - fd = open (filename, O_RDONLY, 0600); - - if (fd < 0) - { - r = errno; - unlink (filename); - errno = r; - return -1; - } - - if (unlink (filename) < 0) - { - r = errno; - close (fd); - errno = r; - return (-1); - } - - return (fd); -} - -/* Open FILENAME with FLAGS in noclobber mode, hopefully avoiding most - race conditions and avoiding the problem where the file is replaced - between the stat(2) and open(2). */ -static int -noclobber_open (filename, flags, ri) - char *filename; - int flags; - enum r_instruction ri; -{ - int r, fd; - struct stat finfo, finfo2; - - /* If the file exists and is a regular file, return an error - immediately. */ - r = stat (filename, &finfo); - if (r == 0 && (S_ISREG (finfo.st_mode))) - return (NOCLOBBER_REDIRECT); - - /* If the file was not present (r != 0), make sure we open it - exclusively so that if it is created before we open it, our open - will fail. Make sure that we do not truncate an existing file. - Note that we don't turn on O_EXCL unless the stat failed -- if - the file was not a regular file, we leave O_EXCL off. */ - flags &= ~O_TRUNC; - if (r != 0) - { - fd = open (filename, flags|O_EXCL, 0666); - return ((fd < 0 && errno == EEXIST) ? NOCLOBBER_REDIRECT : fd); - } - fd = open (filename, flags, 0666); - - /* If the open failed, return the file descriptor right away. */ - if (fd < 0) - return (errno == EEXIST ? NOCLOBBER_REDIRECT : fd); - - /* OK, the open succeeded, but the file may have been changed from a - non-regular file to a regular file between the stat and the open. - We are assuming that the O_EXCL open handles the case where FILENAME - did not exist and is symlinked to an existing file between the stat - and open. */ - - /* If we can open it and fstat the file descriptor, and neither check - revealed that it was a regular file, and the file has not been replaced, - return the file descriptor. */ - if ((fstat (fd, &finfo2) == 0) && (S_ISREG (finfo2.st_mode) == 0) && - r == 0 && (S_ISREG (finfo.st_mode) == 0) && - same_file (filename, filename, &finfo, &finfo2)) - return fd; - - /* The file has been replaced. badness. */ - close (fd); - errno = EEXIST; - return (NOCLOBBER_REDIRECT); -} - -/* Do the specific redirection requested. Returns errno or one of the - special redirection errors (*_REDIRECT) in case of error, 0 on success. - If FOR_REAL is zero, then just do whatever is neccessary to produce the - appropriate side effects. REMEMBERING, if non-zero, says to remember - how to undo each redirection. If SET_CLEXEC is non-zero, then - we set all file descriptors > 2 that we open to be close-on-exec. */ -static int -do_redirection_internal (redirect, for_real, remembering, set_clexec) - REDIRECT *redirect; - int for_real, remembering, set_clexec; -{ - WORD_DESC *redirectee; - int redir_fd, fd, redirector, r; - char *redirectee_word; - enum r_instruction ri; - REDIRECT *new_redirect; - - redirectee = redirect->redirectee.filename; - redir_fd = redirect->redirectee.dest; - redirector = redirect->redirector; - ri = redirect->instruction; - - if (ri == r_duplicating_input_word || ri == r_duplicating_output_word) - { - /* We have [N]>&WORD or [N]<&WORD. Expand WORD, then translate - the redirection into a new one and continue. */ - redirectee_word = redirection_expand (redirectee); - - if (redirectee_word == 0) - return (AMBIGUOUS_REDIRECT); - else if (redirectee_word[0] == '-' && redirectee_word[1] == '\0') - { - rd.dest = 0L; - new_redirect = make_redirection (redirector, r_close_this, rd); - } - else if (all_digits (redirectee_word)) - { - if (ri == r_duplicating_input_word) - { - rd.dest = atol (redirectee_word); - new_redirect = make_redirection (redirector, r_duplicating_input, rd); - } - else - { - rd.dest = atol (redirectee_word); - new_redirect = make_redirection (redirector, r_duplicating_output, rd); - } - } - else if (ri == r_duplicating_output_word && redirector == 1) - { - if (posixly_correct == 0) - { - rd.filename = make_bare_word (redirectee_word); - new_redirect = make_redirection (1, r_err_and_out, rd); - } - else - new_redirect = copy_redirect (redirect); - } - else - { - free (redirectee_word); - return (AMBIGUOUS_REDIRECT); - } - - free (redirectee_word); - - /* Set up the variables needed by the rest of the function from the - new redirection. */ - if (new_redirect->instruction == r_err_and_out) - { - char *alloca_hack; - - /* Copy the word without allocating any memory that must be - explicitly freed. */ - redirectee = (WORD_DESC *)alloca (sizeof (WORD_DESC)); - xbcopy ((char *)new_redirect->redirectee.filename, - (char *)redirectee, sizeof (WORD_DESC)); - - alloca_hack = (char *) - alloca (1 + strlen (new_redirect->redirectee.filename->word)); - redirectee->word = alloca_hack; - strcpy (redirectee->word, new_redirect->redirectee.filename->word); - } - else - /* It's guaranteed to be an integer, and shouldn't be freed. */ - redirectee = new_redirect->redirectee.filename; - - redir_fd = new_redirect->redirectee.dest; - redirector = new_redirect->redirector; - ri = new_redirect->instruction; - - /* Overwrite the flags element of the old redirect with the new value. */ - redirect->flags = new_redirect->flags; - dispose_redirects (new_redirect); - } - - switch (ri) - { - case r_output_direction: - case r_appending_to: - case r_input_direction: - case r_inputa_direction: - case r_err_and_out: /* command &>filename */ - case r_input_output: - case r_output_force: - if (posixly_correct && !interactive_shell) - disallow_filename_globbing++; - redirectee_word = redirection_expand (redirectee); - if (posixly_correct && !interactive_shell) - disallow_filename_globbing--; - - if (redirectee_word == 0) - return (AMBIGUOUS_REDIRECT); - -#if defined (RESTRICTED_SHELL) - if (restricted && (WRITE_REDIRECT (ri))) - { - free (redirectee_word); - return (RESTRICTED_REDIRECT); - } -#endif /* RESTRICTED_SHELL */ - - /* If we are in noclobber mode, you are not allowed to overwrite - existing files. Check before opening. */ - if (noclobber && OUTPUT_REDIRECT (ri)) - { - fd = noclobber_open (redirectee_word, redirect->flags, ri); - if (fd == NOCLOBBER_REDIRECT) - { - free (redirectee_word); - return (NOCLOBBER_REDIRECT); - } - } - else - { - fd = open (redirectee_word, redirect->flags, 0666); -#if defined (AFS) - if ((fd < 0) && (errno == EACCES)) - fd = open (redirectee_word, redirect->flags & ~O_CREAT, 0666); -#endif /* AFS */ - } - free (redirectee_word); - - if (fd < 0) - return (errno); - - if (for_real) - { - if (remembering) - /* Only setup to undo it if the thing to undo is active. */ - if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1)) - add_undo_redirect (redirector); - else - add_undo_close_redirect (redirector); - -#if defined (BUFFERED_INPUT) - check_bash_input (redirector); -#endif - - if ((fd != redirector) && (dup2 (fd, redirector) < 0)) - return (errno); - -#if defined (BUFFERED_INPUT) - /* Do not change the buffered stream for an implicit redirection - of /dev/null to fd 0 for asynchronous commands without job - control (r_inputa_direction). */ - if (ri == r_input_direction || ri == r_input_output) - duplicate_buffered_stream (fd, redirector); -#endif /* BUFFERED_INPUT */ - - /* - * If we're remembering, then this is the result of a while, for - * or until loop with a loop redirection, or a function/builtin - * executing in the parent shell with a redirection. In the - * function/builtin case, we want to set all file descriptors > 2 - * to be close-on-exec to duplicate the effect of the old - * for i = 3 to NOFILE close(i) loop. In the case of the loops, - * both sh and ksh leave the file descriptors open across execs. - * The Posix standard mentions only the exec builtin. - */ - if (set_clexec && (redirector > 2)) - SET_CLOSE_ON_EXEC (redirector); - } - - if (fd != redirector) - { -#if defined (BUFFERED_INPUT) - if (INPUT_REDIRECT (ri)) - close_buffered_fd (fd); - else -#endif /* !BUFFERED_INPUT */ - close (fd); /* Don't close what we just opened! */ - } - - /* If we are hacking both stdout and stderr, do the stderr - redirection here. */ - if (ri == r_err_and_out) - { - if (for_real) - { - if (remembering) - add_undo_redirect (2); - if (dup2 (1, 2) < 0) - return (errno); - } - } - break; - - case r_reading_until: - case r_deblank_reading_until: - /* REDIRECTEE is a pointer to a WORD_DESC containing the text of - the new input. Place it in a temporary file. */ - if (redirectee) - { - fd = here_document_to_fd (redirectee); - - if (fd < 0) - { - heredoc_errno = errno; - return (HEREDOC_REDIRECT); - } - - if (for_real) - { - if (remembering) - /* Only setup to undo it if the thing to undo is active. */ - if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1)) - add_undo_redirect (redirector); - else - add_undo_close_redirect (redirector); - -#if defined (BUFFERED_INPUT) - check_bash_input (redirector); -#endif - if (fd != redirector && dup2 (fd, redirector) < 0) - { - r = errno; - close (fd); - return (r); - } - -#if defined (BUFFERED_INPUT) - duplicate_buffered_stream (fd, redirector); -#endif - - if (set_clexec && (redirector > 2)) - SET_CLOSE_ON_EXEC (redirector); - } - -#if defined (BUFFERED_INPUT) - close_buffered_fd (fd); -#else - close (fd); -#endif - } - break; - - case r_duplicating_input: - case r_duplicating_output: - if (for_real && (redir_fd != redirector)) - { - if (remembering) - /* Only setup to undo it if the thing to undo is active. */ - if (fcntl (redirector, F_GETFD, 0) != -1) - add_undo_redirect (redirector); - else - add_undo_close_redirect (redirector); - -#if defined (BUFFERED_INPUT) - check_bash_input (redirector); -#endif - /* This is correct. 2>&1 means dup2 (1, 2); */ - if (dup2 (redir_fd, redirector) < 0) - return (errno); - -#if defined (BUFFERED_INPUT) - if (ri == r_duplicating_input) - duplicate_buffered_stream (redir_fd, redirector); -#endif /* BUFFERED_INPUT */ - - /* First duplicate the close-on-exec state of redirectee. dup2 - leaves the flag unset on the new descriptor, which means it - stays open. Only set the close-on-exec bit for file descriptors - greater than 2 in any case, since 0-2 should always be open - unless closed by something like `exec 2<&-'. */ - /* if ((already_set || set_unconditionally) && (ok_to_set)) - set_it () */ - if (((fcntl (redir_fd, F_GETFD, 0) == 1) || set_clexec) && - (redirector > 2)) - SET_CLOSE_ON_EXEC (redirector); - } - break; - - case r_close_this: - if (for_real) - { - if (remembering && (fcntl (redirector, F_GETFD, 0) != -1)) - add_undo_redirect (redirector); - -#if defined (BUFFERED_INPUT) - check_bash_input (redirector); - close_buffered_fd (redirector); -#else /* !BUFFERED_INPUT */ - close (redirector); -#endif /* !BUFFERED_INPUT */ - } - break; - - case r_duplicating_input_word: - case r_duplicating_output_word: - break; - } - return (0); -} - -#define SHELL_FD_BASE 10 - -/* Remember the file descriptor associated with the slot FD, - on REDIRECTION_UNDO_LIST. Note that the list will be reversed - before it is executed. Any redirections that need to be undone - even if REDIRECTION_UNDO_LIST is discarded by the exec builtin - are also saved on EXEC_REDIRECTION_UNDO_LIST. */ -static int -add_undo_redirect (fd) - int fd; -{ - int new_fd, clexec_flag; - REDIRECT *new_redirect, *closer, *dummy_redirect; - - new_fd = fcntl (fd, F_DUPFD, SHELL_FD_BASE); - - if (new_fd < 0) - { - sys_error ("redirection error"); - return (-1); - } - - clexec_flag = fcntl (fd, F_GETFD, 0); - - rd.dest = 0L; - closer = make_redirection (new_fd, r_close_this, rd); - dummy_redirect = copy_redirects (closer); - - rd.dest = (long)new_fd; - new_redirect = make_redirection (fd, r_duplicating_output, rd); - new_redirect->next = closer; - - closer->next = redirection_undo_list; - redirection_undo_list = new_redirect; - - /* Save redirections that need to be undone even if the undo list - is thrown away by the `exec' builtin. */ - add_exec_redirect (dummy_redirect); - - /* File descriptors used only for saving others should always be - marked close-on-exec. Unfortunately, we have to preserve the - close-on-exec state of the file descriptor we are saving, since - fcntl (F_DUPFD) sets the new file descriptor to remain open - across execs. If, however, the file descriptor whose state we - are saving is <= 2, we can just set the close-on-exec flag, - because file descriptors 0-2 should always be open-on-exec, - and the restore above in do_redirection() will take care of it. */ - if (clexec_flag || fd < 3) - SET_CLOSE_ON_EXEC (new_fd); - - return (0); -} - -/* Set up to close FD when we are finished with the current command - and its redirections. */ -static void -add_undo_close_redirect (fd) - int fd; -{ - REDIRECT *closer; - - rd.dest = 0L; - closer = make_redirection (fd, r_close_this, rd); - closer->next = redirection_undo_list; - redirection_undo_list = closer; -} - -static void -add_exec_redirect (dummy_redirect) - REDIRECT *dummy_redirect; -{ - dummy_redirect->next = exec_redirection_undo_list; - exec_redirection_undo_list = dummy_redirect; -} - -#define u_mode_bits(x) (((x) & 0000700) >> 6) -#define g_mode_bits(x) (((x) & 0000070) >> 3) -#define o_mode_bits(x) (((x) & 0000007) >> 0) -#define X_BIT(x) ((x) & 1) - -/* Return some flags based on information about this file. - The EXISTS bit is non-zero if the file is found. - The EXECABLE bit is non-zero the file is executble. - Zero is returned if the file is not found. */ -int -file_status (name) - char *name; -{ - struct stat finfo; - - /* Determine whether this file exists or not. */ - if (stat (name, &finfo) < 0) - return (0); - - /* If the file is a directory, then it is not "executable" in the - sense of the shell. */ - if (S_ISDIR (finfo.st_mode)) - return (FS_EXISTS|FS_DIRECTORY); - -#if defined (AFS) - /* We have to use access(2) to determine access because AFS does not - support Unix file system semantics. This may produce wrong - answers for non-AFS files when ruid != euid. I hate AFS. */ - if (access (name, X_OK) == 0) - return (FS_EXISTS | FS_EXECABLE); - else - return (FS_EXISTS); -#else /* !AFS */ - - /* Find out if the file is actually executable. By definition, the - only other criteria is that the file has an execute bit set that - we can use. */ - - /* Root only requires execute permission for any of owner, group or - others to be able to exec a file. */ - if (current_user.euid == (uid_t)0) - { - int bits; - - bits = (u_mode_bits (finfo.st_mode) | - g_mode_bits (finfo.st_mode) | - o_mode_bits (finfo.st_mode)); - - if (X_BIT (bits)) - return (FS_EXISTS | FS_EXECABLE); - } - - /* If we are the owner of the file, the owner execute bit applies. */ - if (current_user.euid == finfo.st_uid && X_BIT (u_mode_bits (finfo.st_mode))) - return (FS_EXISTS | FS_EXECABLE); - - /* If we are in the owning group, the group permissions apply. */ - if (group_member (finfo.st_gid) && X_BIT (g_mode_bits (finfo.st_mode))) - return (FS_EXISTS | FS_EXECABLE); - - /* If `others' have execute permission to the file, then so do we, - since we are also `others'. */ - if (X_BIT (o_mode_bits (finfo.st_mode))) - return (FS_EXISTS | FS_EXECABLE); - - return (FS_EXISTS); -#endif /* !AFS */ -} - -/* Return non-zero if FILE exists and is executable. - Note that this function is the definition of what an - executable file is; do not change this unless YOU know - what an executable file is. */ -int -executable_file (file) - char *file; -{ - int s; - - s = file_status (file); - return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0)); -} - -int -is_directory (file) - char *file; -{ - return (file_status (file) & FS_DIRECTORY); -} - -/* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command () - encounters a `.' as the directory pathname while scanning the - list of possible pathnames; i.e., if `.' comes before the directory - containing the file of interest. */ -int dot_found_in_search = 0; - -/* Locate the executable file referenced by NAME, searching along - the contents of the shell PATH variable. Return a new string - which is the full pathname to the file, or NULL if the file - couldn't be found. If a file is found that isn't executable, - and that is the only match, then return that. */ -char * -find_user_command (name) - char *name; -{ - return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS)); -} - -/* Locate the file referenced by NAME, searching along the contents - of the shell PATH variable. Return a new string which is the full - pathname to the file, or NULL if the file couldn't be found. This - returns the first file found. */ -char * -find_path_file (name) - char *name; -{ - return (find_user_command_internal (name, FS_EXISTS)); -} - -static char * -_find_user_command_internal (name, flags) - char *name; - int flags; -{ - char *path_list; - SHELL_VAR *var; - - /* Search for the value of PATH in both the temporary environment, and - in the regular list of variables. */ - if (var = find_variable_internal ("PATH", 1)) /* XXX could be array? */ - path_list = value_cell (var); - else - path_list = (char *)NULL; - - if (path_list == 0 || *path_list == '\0') - return (savestring (name)); - - return (find_user_command_in_path (name, path_list, flags)); -} - -static char * -find_user_command_internal (name, flags) - char *name; - int flags; -{ -#ifdef __WIN32__ - char *res, *dotexe; - - dotexe = xmalloc (strlen (name) + 5); - strcpy (dotexe, name); - strcat (dotexe, ".exe"); - res = _find_user_command_internal (dotexe, flags); - free (dotexe); - if (res == 0) - res = _find_user_command_internal (name, flags); - return res; -#else - return (_find_user_command_internal (name, flags)); -#endif -} - -/* Return the next element from PATH_LIST, a colon separated list of - paths. PATH_INDEX_POINTER is the address of an index into PATH_LIST; - the index is modified by this function. - Return the next element of PATH_LIST or NULL if there are no more. */ -static char * -get_next_path_element (path_list, path_index_pointer) - char *path_list; - int *path_index_pointer; -{ - char *path; - - path = extract_colon_unit (path_list, path_index_pointer); - - if (!path) - return (path); - - if (!*path) - { - free (path); - path = savestring ("."); - } - - return (path); -} - -/* Look for PATHNAME in $PATH. Returns either the hashed command - corresponding to PATHNAME or the first instance of PATHNAME found - in $PATH. Returns a newly-allocated string. */ -char * -search_for_command (pathname) - char *pathname; -{ - char *hashed_file, *command; - int temp_path, st; - SHELL_VAR *path; - - hashed_file = command = (char *)NULL; - - /* If PATH is in the temporary environment for this command, don't use the - hash table to search for the full pathname. */ - path = find_tempenv_variable ("PATH"); - temp_path = path != 0; - - /* Don't waste time trying to find hashed data for a pathname - that is already completely specified or if we're using a command- - specific value for PATH. */ - if (path == 0 && absolute_program (pathname) == 0) - hashed_file = find_hashed_filename (pathname); - - /* If a command found in the hash table no longer exists, we need to - look for it in $PATH. Thank you Posix.2. This forces us to stat - every command found in the hash table. */ - - if (hashed_file && (posixly_correct || check_hashed_filenames)) - { - st = file_status (hashed_file); - if ((st ^ (FS_EXISTS | FS_EXECABLE)) != 0) - { - remove_hashed_filename (pathname); - free (hashed_file); - hashed_file = (char *)NULL; - } - } - - if (hashed_file) - command = hashed_file; - else if (absolute_program (pathname)) - /* A command containing a slash is not looked up in PATH or saved in - the hash table. */ - command = savestring (pathname); - else - { - /* If $PATH is in the temporary environment, we've already retrieved - it, so don't bother trying again. */ - if (temp_path) - command = find_user_command_in_path (pathname, value_cell (path), - FS_EXEC_PREFERRED|FS_NODIRS); - else - command = find_user_command (pathname); - if (command && hashing_enabled && temp_path == 0) - remember_filename (pathname, command, dot_found_in_search, 1); - } - return (command); -} - -char * -user_command_matches (name, flags, state) - char *name; - int flags, state; -{ - register int i; - int path_index, name_len; - char *path_list, *path_element, *match; - struct stat dotinfo; - static char **match_list = NULL; - static int match_list_size = 0; - static int match_index = 0; - - if (state == 0) - { - /* Create the list of matches. */ - if (match_list == 0) - { - match_list_size = 5; - match_list = (char **)xmalloc (match_list_size * sizeof(char *)); - } - - /* Clear out the old match list. */ - for (i = 0; i < match_list_size; i++) - match_list[i] = 0; - - /* We haven't found any files yet. */ - match_index = 0; - - if (absolute_program (name)) - { - match_list[0] = find_absolute_program (name, flags); - match_list[1] = (char *)NULL; - path_list = (char *)NULL; - } - else - { - name_len = strlen (name); - file_to_lose_on = (char *)NULL; - dot_found_in_search = 0; - stat (".", &dotinfo); - path_list = get_string_value ("PATH"); - path_index = 0; - } - - while (path_list && path_list[path_index]) - { - path_element = get_next_path_element (path_list, &path_index); - - if (path_element == 0) - break; - - match = find_in_path_element (name, path_element, flags, name_len, &dotinfo); - - free (path_element); - - if (match == 0) - continue; - - if (match_index + 1 == match_list_size) - { - match_list_size += 10; - match_list = (char **)xrealloc (match_list, (match_list_size + 1) * sizeof (char *)); - } - - match_list[match_index++] = match; - match_list[match_index] = (char *)NULL; - FREE (file_to_lose_on); - file_to_lose_on = (char *)NULL; - } - - /* We haven't returned any strings yet. */ - match_index = 0; - } - - match = match_list[match_index]; - - if (match) - match_index++; - - return (match); -} - -/* Turn PATH, a directory, and NAME, a filename, into a full pathname. - This allocates new memory and returns it. */ -static char * -make_full_pathname (path, name, name_len) - char *path, *name; - int name_len; -{ - char *full_path; - int path_len; - - path_len = strlen (path); - full_path = xmalloc (2 + path_len + name_len); - strcpy (full_path, path); - full_path[path_len] = '/'; - strcpy (full_path + path_len + 1, name); - return (full_path); -} - -static char * -find_absolute_program (name, flags) - char *name; - int flags; -{ - int st; - - st = file_status (name); - - /* If the file doesn't exist, quit now. */ - if ((st & FS_EXISTS) == 0) - return ((char *)NULL); - - /* If we only care about whether the file exists or not, return - this filename. Otherwise, maybe we care about whether this - file is executable. If it is, and that is what we want, return it. */ - if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE))) - return (savestring (name)); - - return ((char *)NULL); -} - -static char * -find_in_path_element (name, path, flags, name_len, dotinfop) - char *name, *path; - int flags, name_len; - struct stat *dotinfop; -{ - int status; - char *full_path, *xpath; - - xpath = (*path == '~') ? bash_tilde_expand (path) : path; - - /* Remember the location of "." in the path, in all its forms - (as long as they begin with a `.', e.g. `./.') */ - if (dot_found_in_search == 0 && *xpath == '.') - dot_found_in_search = same_file (".", xpath, dotinfop, (struct stat *)NULL); - - full_path = make_full_pathname (xpath, name, name_len); - - status = file_status (full_path); - - if (xpath != path) - free (xpath); - - if ((status & FS_EXISTS) == 0) - { - free (full_path); - return ((char *)NULL); - } - - /* The file exists. If the caller simply wants the first file, here it is. */ - if (flags & FS_EXISTS) - return (full_path); - - /* If the file is executable, then it satisfies the cases of - EXEC_ONLY and EXEC_PREFERRED. Return this file unconditionally. */ - if ((status & FS_EXECABLE) && - (((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0))) - { - FREE (file_to_lose_on); - file_to_lose_on = (char *)NULL; - return (full_path); - } - - /* The file is not executable, but it does exist. If we prefer - an executable, then remember this one if it is the first one - we have found. */ - if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0) - file_to_lose_on = savestring (full_path); - - /* If we want only executable files, or we don't want directories and - this file is a directory, fail. */ - if ((flags & FS_EXEC_ONLY) || (flags & FS_EXEC_PREFERRED) || - ((flags & FS_NODIRS) && (status & FS_DIRECTORY))) - { - free (full_path); - return ((char *)NULL); - } - else - return (full_path); -} - -/* This does the dirty work for find_user_command_internal () and - user_command_matches (). - NAME is the name of the file to search for. - PATH_LIST is a colon separated list of directories to search. - FLAGS contains bit fields which control the files which are eligible. - Some values are: - FS_EXEC_ONLY: The file must be an executable to be found. - FS_EXEC_PREFERRED: If we can't find an executable, then the - the first file matching NAME will do. - FS_EXISTS: The first file found will do. - FS_NODIRS: Don't find any directories. -*/ -static char * -find_user_command_in_path (name, path_list, flags) - char *name; - char *path_list; - int flags; -{ - char *full_path, *path; - int path_index, name_len; - struct stat dotinfo; - - /* We haven't started looking, so we certainly haven't seen - a `.' as the directory path yet. */ - dot_found_in_search = 0; - - if (absolute_program (name)) - { - full_path = find_absolute_program (name, flags); - return (full_path); - } - - if (path_list == 0 || *path_list == '\0') - return (savestring (name)); /* XXX */ - - file_to_lose_on = (char *)NULL; - name_len = strlen (name); - stat (".", &dotinfo); - path_index = 0; - - while (path_list[path_index]) - { - /* Allow the user to interrupt out of a lengthy path search. */ - QUIT; - - path = get_next_path_element (path_list, &path_index); - if (path == 0) - break; - - /* Side effects: sets dot_found_in_search, possibly sets - file_to_lose_on. */ - full_path = find_in_path_element (name, path, flags, name_len, &dotinfo); - free (path); - - /* This should really be in find_in_path_element, but there isn't the - right combination of flags. */ - if (full_path && is_directory (full_path)) - { - free (full_path); - continue; - } - - if (full_path) - { - FREE (file_to_lose_on); - return (full_path); - } - } - - /* We didn't find exactly what the user was looking for. Return - the contents of FILE_TO_LOSE_ON which is NULL when the search - required an executable, or non-NULL if a file was found and the - search would accept a non-executable as a last resort. */ - return (file_to_lose_on); }