diff options
Diffstat (limited to 'execute_cmd.c')
| -rw-r--r-- | execute_cmd.c | 444 |
1 files changed, 306 insertions, 138 deletions
diff --git a/execute_cmd.c b/execute_cmd.c index 76a80766..3759376e 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -1,6 +1,6 @@ /* execute_cmd.c -- Execute a COMMAND structure. */ -/* Copyright (C) 1987-2016 Free Software Foundation, Inc. +/* Copyright (C) 1987-2018 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -66,6 +66,7 @@ extern int errno; #include "memalloc.h" #include "shell.h" #include <y.tab.h> /* use <...> so we pick it up from the build directory */ +#include "parser.h" #include "flags.h" #include "builtins.h" #include "hashlib.h" @@ -105,28 +106,13 @@ extern int errno; # include <mbstr.h> /* mbschr */ #endif -extern int dollar_dollar_pid; -extern int posixly_correct; -extern int expand_aliases; -extern int autocd; -extern int breaking, continuing, loop_level; -extern int parse_and_execute_level, running_trap, sourcelevel; -extern int command_string_index, line_number; -extern int dot_found_in_search; -extern int already_making_children; -extern int tempenv_assign_error; -extern char *the_printed_command, *shell_name; -extern pid_t last_command_subst_pid; -extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin; -extern char **subshell_argv, **subshell_envp; -extern int subshell_argc; +extern int command_string_index; +extern char *the_printed_command; extern time_t shell_start_time; #if 0 extern char *glob_argv_flags; #endif -extern int job_control; /* XXX */ - extern int close __P((int)); /* Static functions defined and used in this file. */ @@ -197,6 +183,7 @@ static char *getinterp __P((char *, int, int *)); static void initialize_subshell __P((void)); static int execute_in_subshell __P((COMMAND *, int, int, int, struct fd_bitmap *)); #if defined (COPROCESS_SUPPORT) +static void coproc_setstatus __P((struct coproc *, int)); static int execute_coproc __P((COMMAND *, int, int, struct fd_bitmap *)); #endif @@ -285,6 +272,8 @@ static int function_line_number; report the correct line number. Kind of a hack. */ static int showing_function_line; +static int connection_count; + /* $LINENO ($BASH_LINENO) for use by an ERR trap. Global so parse_and_execute can save and restore it. */ int line_number_for_err_trap; @@ -410,7 +399,7 @@ execute_command (command) #if defined (PROCESS_SUBSTITUTION) /* don't unlink fifos if we're in a shell function; wait until the function returns. */ - if (variable_context == 0) + if (variable_context == 0 && executing_list == 0) unlink_fifo_list (); #endif /* PROCESS_SUBSTITUTION */ @@ -461,6 +450,16 @@ cleanup_redirects (list) dispose_redirects (list); } +void +undo_partial_redirects () +{ + if (redirection_undo_list) + { + cleanup_redirects (redirection_undo_list); + redirection_undo_list = (REDIRECT *)NULL; + } +} + #if 0 /* Function to unwind_protect the redirections for functions and builtins. */ static void @@ -481,6 +480,16 @@ dispose_exec_redirects () } } +void +dispose_partial_redirects () +{ + if (redirection_undo_list) + { + dispose_redirects (redirection_undo_list); + redirection_undo_list = (REDIRECT *)NULL; + } +} + #if defined (JOB_CONTROL) /* A function to restore the signal mask to its proper value when the shell is interrupted or errors occur while creating a pipeline. */ @@ -552,7 +561,6 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, int exec_result, user_subshell, invert, ignore_return, was_error_trap; REDIRECT *my_undo_list, *exec_undo_list; char *tcmd; - volatile int last_pid; volatile int save_line_number; #if defined (PROCESS_SUBSTITUTION) volatile int ofifo, nfifo, osize, saved_fifo; @@ -590,11 +598,21 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, #if defined (COPROCESS_SUPPORT) if (command->type == cm_coproc) - return (execute_coproc (command, pipe_in, pipe_out, fds_to_close)); + return (last_command_exit_value = execute_coproc (command, pipe_in, pipe_out, fds_to_close)); #endif user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0); +#if defined (TIME_BEFORE_SUBSHELL) + if ((command->flags & CMD_TIME_PIPELINE) && user_subshell && asynchronous == 0) + { + command->flags |= CMD_FORCE_SUBSHELL; + exec_result = time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close); + currently_executing_command = (COMMAND *)NULL; + return (exec_result); + } +#endif + if (command->type == cm_subshell || (command->flags & (CMD_WANT_SUBSHELL|CMD_FORCE_SUBSHELL)) || (shell_control_structure (command->type) && @@ -602,12 +620,13 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, { pid_t paren_pid; int s; + char *p; /* Fork a subshell, turn off the subshell bit, turn off job control and call execute_command () on the command again. */ - line_number_for_err_trap = line_number; /* XXX - save value? */ + line_number_for_err_trap = line_number = command->value.Subshell->line; /* XXX - save value? */ tcmd = make_command_string (command); - paren_pid = make_child (savestring (tcmd), asynchronous); + paren_pid = make_child (p = savestring (tcmd), asynchronous); if (user_subshell && signal_is_trapped (ERROR_TRAP) && signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0) @@ -618,6 +637,9 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, if (paren_pid == 0) { +#if defined (JOB_CONTROL) + FREE (p); /* child doesn't use pointer */ +#endif /* We want to run the exit trap for forced {} subshells, and we want to note this before execute_in_subshell modifies the COMMAND struct. Need to keep in mind that execute_in_subshell @@ -722,6 +744,10 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, stdin_redir = stdin_redirects (command->redirects); #if defined (PROCESS_SUBSTITUTION) +# if !defined (HAVE_DEV_FD) + reap_procsubs (); +# endif + if (variable_context != 0) { ofifo = num_fifos (); @@ -738,38 +764,45 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, redirection.) */ if (do_redirections (command->redirects, RX_ACTIVE|RX_UNDOABLE) != 0) { - cleanup_redirects (redirection_undo_list); - redirection_undo_list = (REDIRECT *)NULL; + undo_partial_redirects (); dispose_exec_redirects (); #if defined (PROCESS_SUBSTITUTION) if (saved_fifo) - { + { free ((void *)ofifo_list); - discard_unwind_frame ("internal_fifos"); - } + discard_unwind_frame ("internal_fifos"); + } #endif return (last_command_exit_value = EXECUTION_FAILURE); } +#if 0 if (redirection_undo_list) { /* XXX - why copy here? */ my_undo_list = (REDIRECT *)copy_redirects (redirection_undo_list); - dispose_redirects (redirection_undo_list); - redirection_undo_list = (REDIRECT *)NULL; + dispose_partial_redirects (); } else my_undo_list = (REDIRECT *)NULL; +#else + my_undo_list = redirection_undo_list; + redirection_undo_list = (REDIRECT *)NULL; +#endif +#if 0 if (exec_redirection_undo_list) { /* XXX - why copy here? */ exec_undo_list = (REDIRECT *)copy_redirects (exec_redirection_undo_list); - dispose_redirects (exec_redirection_undo_list); - exec_redirection_undo_list = (REDIRECT *)NULL; + dispose_exec_redirects (); } else exec_undo_list = (REDIRECT *)NULL; +#else + exec_undo_list = exec_redirection_undo_list; + exec_redirection_undo_list = (REDIRECT *)NULL; +#endif if (my_undo_list || exec_undo_list) begin_unwind_frame ("loop_redirections"); @@ -795,7 +828,6 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, #if defined (RECYCLES_PIDS) last_made_pid = NO_PID; #endif - last_pid = last_made_pid; was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0; if (ignore_return && command->value.Simple) @@ -855,8 +887,13 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, } /* 2009/02/13 -- pipeline failure is processed elsewhere. This handles - only the failure of a simple command. */ - if (was_error_trap && ignore_return == 0 && invert == 0 && pipe_in == NO_PIPE && pipe_out == NO_PIPE && exec_result != EXECUTION_SUCCESS) + only the failure of a simple command. We don't want to run the error + trap if the command run by the `command' builtin fails; we want to + defer that until the command builtin itself returns failure. */ + if (was_error_trap && ignore_return == 0 && invert == 0 && + pipe_in == NO_PIPE && pipe_out == NO_PIPE && + (command->value.Simple->flags & CMD_COMMAND_BUILTIN) == 0 && + exec_result != EXECUTION_SUCCESS) { last_command_exit_value = exec_result; line_number = line_number_for_err_trap; @@ -1047,10 +1084,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, } if (my_undo_list) - { - do_redirections (my_undo_list, RX_ACTIVE); - dispose_redirects (my_undo_list); - } + cleanup_redirects (my_undo_list); if (exec_undo_list) dispose_redirects (exec_undo_list); @@ -1090,6 +1124,8 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, # endif set_pipestatus_from_exit (exec_result); break; + default: + break; } #endif @@ -1116,19 +1152,6 @@ extern int timeval_to_cpu __P((struct timeval *, struct timeval *, struct timeva static const int precs[] = { 0, 100, 10, 1 }; -#if defined (HAVE_LOCALE_H) && defined (HAVE_LOCALECONV) -static int -decpoint () -{ - struct lconv *lv; - - lv = localeconv (); - return (lv && lv->decimal_point && lv->decimal_point[0]) ? lv->decimal_point[0] : '.'; -} -#else -# define decpoint() '.' -#endif - /* Expand one `%'-prefixed escape sequence from a time format string. */ static int mkfmt (buf, prec, lng, sec, sec_fraction) @@ -1173,7 +1196,7 @@ mkfmt (buf, prec, lng, sec, sec_fraction) and 999. */ if (prec != 0) { - buf[ind++] = decpoint (); + buf[ind++] = locale_decpoint (); for (aind = 1; aind <= prec; aind++) { buf[ind++] = (sec_fraction / precs[aind]) + '0'; @@ -1357,8 +1380,10 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close) command->flags &= ~(CMD_TIME_PIPELINE|CMD_TIME_POSIX); code = setjmp_nosigs (top_level); if (code == NOT_JUMPED) - rv = execute_command_internal (command, asynchronous, pipe_in, pipe_out, fds_to_close); - command->flags = old_flags; + { + rv = execute_command_internal (command, asynchronous, pipe_in, pipe_out, fds_to_close); + command->flags = old_flags; + } COPY_PROCENV (save_top_level, top_level); rs = us = ss = 0; @@ -1415,6 +1440,7 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close) else time_format = BASH_TIMEFORMAT; } + if (time_format && *time_format) print_formatted_time (stderr, time_format, rs, rsf, us, usf, ss, ssf, cpu); @@ -1435,9 +1461,8 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close) int pipe_in, pipe_out; struct fd_bitmap *fds_to_close; { - int user_subshell, return_code, function_value, should_redir_stdin, invert; - int ois, user_coproc; - int result; + volatile int user_subshell, user_coproc, invert; + int return_code, function_value, should_redir_stdin, ois, result; volatile COMMAND *tcom; USE_VAR(user_subshell); @@ -1498,7 +1523,11 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close) login_shell = interactive = 0; if (user_subshell) - subshell_environment = SUBSHELL_PAREN; /* XXX */ + { + subshell_environment = SUBSHELL_PAREN; /* XXX */ + if (asynchronous) + subshell_environment |= SUBSHELL_ASYNC; + } else { subshell_environment = 0; /* XXX */ @@ -1534,11 +1563,9 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close) 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); @@ -1572,6 +1599,12 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close) if (should_redir_stdin && stdin_redir == 0) async_redirect_stdin (); +#if 0 + /* bash-5.0 */ + if (user_subshell && command->type == cm_subshell) + optimize_subshell_command (command->value.Subshell->command); +#endif + /* Do redirections, then dispose of them before recursive call. */ if (command->redirects) { @@ -1880,6 +1913,17 @@ cpl_searchbyname (name) return (struct cpelement *)NULL; } +static pid_t +cpl_firstactive () +{ + struct cpelement *cpe; + + for (cpe = coproc_list.head ; cpe; cpe = cpe->next) + if ((cpe->coproc->c_flags & COPROC_DEAD) == 0) + return cpe->coproc->c_pid; + return (pid_t)NO_PID; +} + #if 0 static void cpl_prune () @@ -2123,6 +2167,21 @@ coproc_fdrestore (cp) cp->c_wfd = cp->c_wsave; } +static void +coproc_setstatus (cp, status) + struct coproc *cp; + int status; +{ + cp->c_lock = 4; + cp->c_status = status; + cp->c_flags |= COPROC_DEAD; + cp->c_flags &= ~COPROC_RUNNING; + /* Don't dispose the coproc or unset the COPROC_XXX variables because + this is executed in a signal handler context. Wait until coproc_reap + takes care of it. */ + cp->c_lock = 0; +} + void coproc_pidchk (pid, status) pid_t pid; @@ -2133,24 +2192,26 @@ coproc_pidchk (pid, status) #if MULTIPLE_COPROCS struct cpelement *cpe; - cpe = cpl_delete (pid); + /* We're not disposing the coproc because this is executed in a signal + handler context */ + cpe = cpl_search (pid); cp = cpe ? cpe->coproc : 0; #else cp = getcoprocbypid (pid); #endif if (cp) - { - cp->c_lock = 4; - cp->c_status = status; - cp->c_flags |= COPROC_DEAD; - cp->c_flags &= ~COPROC_RUNNING; - /* Don't dispose the coproc or unset the COPROC_XXX variables because - this is executed in a signal handler context. Wait until coproc_reap - takes care of it. */ - cp->c_lock = 0; - } + coproc_setstatus (cp, status); } +pid_t +coproc_active () +{ +#if MULTIPLE_COPROCS + return (cpl_firstactive ()); +#else + return ((sh_coproc.c_flags & COPROC_DEAD) ? NO_PID : sh_coproc.c_pid); +#endif +} void coproc_setvars (cp) struct coproc *cp; @@ -2212,7 +2273,7 @@ coproc_setvars (cp) t = itos (cp->c_wfd); ind = 1; - bind_array_variable (cp->c_name, ind, t, 0); + v = bind_array_variable (cp->c_name, ind, t, 0); free (t); #else sprintf (namevar, "%s_READ", cp->c_name); @@ -2227,7 +2288,7 @@ coproc_setvars (cp) sprintf (namevar, "%s_PID", cp->c_name); t = itos (cp->c_pid); - bind_variable (namevar, t, 0); + v = bind_variable (namevar, t, 0); free (t); free (namevar); @@ -2270,17 +2331,33 @@ execute_coproc (command, pipe_in, pipe_out, fds_to_close) int rpipe[2], wpipe[2], estat, invert; pid_t coproc_pid; Coproc *cp; - char *tcmd; + char *tcmd, *p, *name; sigset_t set, oset; /* XXX -- can be removed after changes to handle multiple coprocs */ #if !MULTIPLE_COPROCS - if (sh_coproc.c_pid != NO_PID) + if (sh_coproc.c_pid != NO_PID && (sh_coproc.c_rfd >= 0 || sh_coproc.c_wfd >= 0)) internal_warning (_("execute_coproc: coproc [%d:%s] still exists"), sh_coproc.c_pid, sh_coproc.c_name); coproc_init (&sh_coproc); #endif invert = (command->flags & CMD_INVERT_RETURN) != 0; + + /* XXX - expand coproc name without splitting -- bash-5.0 */ + /* could make this dependent on a shopt option */ + name = expand_string_unsplit_to_string (command->value.Coproc->name, 0); + /* Optional check -- bash-5.0. */ + if (legal_identifier (name) == 0) + { + internal_error (_("`%s': not a valid identifier"), name); + return (invert ? EXECUTION_SUCCESS : EXECUTION_FAILURE); + } + else + { + free (command->value.Coproc->name); + command->value.Coproc->name = name; + } + command_string_index = 0; tcmd = make_command_string (command); @@ -2289,13 +2366,17 @@ execute_coproc (command, pipe_in, pipe_out, fds_to_close) BLOCK_SIGNAL (SIGCHLD, set, oset); - coproc_pid = make_child (savestring (tcmd), 1); + coproc_pid = make_child (p = savestring (tcmd), 1); if (coproc_pid == 0) { close (rpipe[0]); close (wpipe[1]); +#if defined (JOB_CONTROL) + FREE (p); +#endif + UNBLOCK_SIGNAL (oset); estat = execute_in_subshell (command, 1, wpipe[0], rpipe[1], fds_to_close); @@ -2308,11 +2389,13 @@ execute_coproc (command, pipe_in, pipe_out, fds_to_close) close (rpipe[1]); close (wpipe[0]); - /* XXX - possibly run Coproc->name through word expansion? */ + /* XXX - run Coproc->name through word expansion above -- bash-5.0 */ cp = coproc_alloc (command->value.Coproc->name, coproc_pid); cp->c_rfd = rpipe[0]; cp->c_wfd = wpipe[1]; + cp->c_flags |= COPROC_RUNNING; + SET_CLOSE_ON_EXEC (cp->c_rfd); SET_CLOSE_ON_EXEC (cp->c_wfd); @@ -2321,7 +2404,7 @@ execute_coproc (command, pipe_in, pipe_out, fds_to_close) UNBLOCK_SIGNAL (oset); #if 0 - itrace ("execute_coproc: [%d] %s", coproc_pid, the_printed_command); + itrace ("execute_coproc (%s): [%d] %s", command->value.Coproc->name, coproc_pid, the_printed_command); #endif close_pipes (pipe_in, pipe_out); @@ -2779,8 +2862,10 @@ execute_for_command (for_command) sh_invalidid (list->word->word); v = 0; } + else if (readonly_p (v)) + err_readonly (name_cell (v)); else - v = bind_variable_value (v, list->word->word, 0); + v = bind_variable_value (v, list->word->word, ASS_NAMEREF); } else v = bind_variable (identifier, list->word->word, 0); @@ -2835,7 +2920,7 @@ execute_for_command (for_command) { SHELL_VAR *new_value; - new_value = bind_variable (identifier, value_cell(old_value), 0); + new_value = bind_variable (identifier, value_cell (old_value), 0); new_value->attributes = old_value->attributes; dispose_variable (old_value); } @@ -2892,7 +2977,7 @@ eval_arith_for_expr (l, okp) skip the command. */ #if defined (DEBUGGER) if (debugging_mode == 0 || r == EXECUTION_SUCCESS) - expresult = evalexp (new->word->word, okp); + expresult = evalexp (new->word->word, EXP_EXPANDED, okp); else { expresult = 0; @@ -2900,7 +2985,7 @@ eval_arith_for_expr (l, okp) *okp = 1; } #else - expresult = evalexp (new->word->word, okp); + expresult = evalexp (new->word->word, EXP_EXPANDED, okp); #endif dispose_words (new); } @@ -2933,11 +3018,12 @@ execute_arith_for_command (arith_for_command) line_number before executing each expression -- for $LINENO and the DEBUG trap. */ line_number = arith_lineno = arith_for_command->line; - if (variable_context && interactive_shell) + if (variable_context && interactive_shell && sourcelevel == 0) { - line_number -= function_line_number; - if (line_number < 0) - line_number = 0; + /* line numbers in a function start at 1 */ + line_number -= function_line_number - 1; + if (line_number <= 0) + line_number = 1; } /* Evaluate the initialization expression. */ @@ -3142,12 +3228,7 @@ select_query (list, list_len, prompt, print_menu) WORD_LIST *l; char *repl_string, *t; -#if 0 - t = get_string_value ("LINES"); - LINES = (t && *t) ? atoi (t) : 24; -#endif - t = get_string_value ("COLUMNS"); - COLS = (t && *t) ? atoi (t) : 80; + COLS = default_columns (); #if 0 t = get_string_value ("TABSIZE"); @@ -3350,7 +3431,7 @@ execute_case_command (case_command) WORD_LIST *wlist, *es; PATTERN_LIST *clauses; char *word, *pattern; - int retval, match, ignore_return, save_line_number; + int retval, match, ignore_return, save_line_number, qflags; save_line_number = line_number; line_number = case_command->line; @@ -3402,7 +3483,18 @@ 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, QGLOB_CVTNULL); + { + /* Convert quoted null strings into empty strings. */ + qflags = QGLOB_CVTNULL; + + /* We left CTLESC in place quoting CTLESC and CTLNUL after the + call to expand_word_leave_quoted; tell quote_string_for_globbing + to remove those here. This works for both unquoted portions of + the word (which call quote_escapes) and quoted portions + (which call quote_string). */ + qflags |= QGLOB_CTLESC; + pattern = quote_string_for_globbing (es->word->word, qflags); + } else { pattern = (char *)xmalloc (1); @@ -3581,13 +3673,14 @@ execute_arith_command (arith_command) save_line_number = line_number; this_command_name = "(("; /* )) */ - line_number = arith_command->line; + line_number_for_err_trap = line_number = arith_command->line; /* If we're in a function, update the line number information. */ - if (variable_context && interactive_shell) + if (variable_context && interactive_shell && sourcelevel == 0) { - line_number -= function_line_number; - if (line_number < 0) - line_number = 0; + /* line numbers in a function start at 1 */ + line_number -= function_line_number - 1; + if (line_number <= 0) + line_number = 1; } command_string_index = 0; @@ -3623,7 +3716,7 @@ execute_arith_command (arith_command) if (new) { exp = new->next ? string_list (new) : new->word->word; - expresult = evalexp (exp, &expok); + expresult = evalexp (exp, EXP_EXPANDED, &expok); line_number = save_line_number; if (exp != new->word->word) free (exp); @@ -3779,17 +3872,17 @@ execute_cond_command (cond_command) { int retval, save_line_number; - retval = EXECUTION_SUCCESS; save_line_number = line_number; this_command_name = "[["; - line_number = cond_command->line; + line_number_for_err_trap = line_number = cond_command->line; /* If we're in a function, update the line number information. */ - if (variable_context && interactive_shell) + if (variable_context && interactive_shell && sourcelevel == 0) { - line_number -= function_line_number; - if (line_number < 0) - line_number = 0; + /* line numbers in a function start at 1 */ + line_number -= function_line_number - 1; + if (line_number <= 0) + line_number = 1; } command_string_index = 0; print_cond_command (cond_command); @@ -3849,7 +3942,11 @@ execute_null_command (redirects, pipe_in, pipe_out, async) REDIRECT *rd; for (forcefork = 0, rd = redirects; rd; rd = rd->next) - forcefork += rd->rflags & REDIR_VARASSIGN; + { + forcefork += rd->rflags & REDIR_VARASSIGN; + /* Safety */ + forcefork += (rd->redirector.dest == 0 || fd_is_bash_input (rd->redirector.dest)) && (INPUT_REDIRECT (rd->instruction) || TRANSLATE_REDIRECT (rd->instruction) || rd->instruction == r_close_this); + } if (forcefork || pipe_in != NO_PIPE || pipe_out != NO_PIPE || async) { @@ -3866,6 +3963,8 @@ execute_null_command (redirects, pipe_in, pipe_out, async) coproc_closeall (); #endif + interactive = 0; /* XXX */ + subshell_environment = 0; if (async) subshell_environment |= SUBSHELL_ASYNC; @@ -3962,9 +4061,10 @@ fix_assignment_words (words) /* If we have an assignment builtin that does not create local variables, make sure we create global variables even if we internally call - `declare' */ + `declare'. The CHKLOCAL flag means to set attributes or values on + an existing local variable */ if (b && ((b->flags & (ASSIGNMENT_BUILTIN|LOCALVAR_BUILTIN)) == ASSIGNMENT_BUILTIN)) - w->word->flags |= W_ASSNGLOBAL; + w->word->flags |= W_ASSNGLOBAL|W_CHKLOCAL; } #if defined (ARRAY_VARS) /* Note that we saw an associative array option to a builtin that takes @@ -4032,9 +4132,10 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) /* If we're in a function, update the line number information. */ if (variable_context && interactive_shell && sourcelevel == 0) { - line_number -= function_line_number; - if (line_number < 0) - line_number = 0; + /* line numbers in a function start at 1 */ + line_number -= function_line_number - 1; + if (line_number <= 0) + line_number = 1; } /* Remember what this command line looks like at invocation. */ @@ -4067,7 +4168,7 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) last_command_subst_pid = NO_PID; old_last_async_pid = last_asynchronous_pid; - already_forked = dofork = 0; + already_forked = 0; /* If we're in a pipeline or run in the background, set DOFORK so we make the child early, before word expansion. This keeps assignment @@ -4085,13 +4186,15 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) if (dofork) { + char *p; + /* Do this now, because execute_disk_command will do it anyway in the vast majority of cases. */ maybe_make_export_env (); /* Don't let a DEBUG trap overwrite the command string to be saved with the process/job associated with this child. */ - if (make_child (savestring (the_printed_command_except_trap), async) == 0) + if (make_child (p = savestring (the_printed_command_except_trap), async) == 0) { already_forked = 1; simple_command->flags |= CMD_NO_FORK; @@ -4124,6 +4227,10 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) if (async) subshell_level++; /* not for pipes yet */ + +#if defined (JOB_CONTROL) + FREE (p); /* child doesn't use pointer */ +#endif } else { @@ -4133,12 +4240,14 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) result = last_command_exit_value; close_pipes (pipe_in, pipe_out); #if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD) +#if 0 /* Close /dev/fd file descriptors in the parent after forking the last child in a (possibly one-element) pipeline. Defer this until any running shell function completes. */ if (pipe_out == NO_PIPE && variable_context == 0) /* XXX */ unlink_fifo_list (); /* XXX */ #endif +#endif command_line = (char *)NULL; /* don't free this. */ bind_lastarg ((char *)NULL); return (result); @@ -4345,10 +4454,18 @@ run_builtin: last_command_exit_value = EXECUTION_FAILURE; jump_to_top_level (ERREXIT); } + break; + case EX_DISKFALLBACK: + /* XXX - experimental */ + executing_builtin = old_builtin; + executing_command_builtin = old_command_builtin; + builtin = 0; + /* XXX - redirections will have to be performed again */ + goto execute_from_filesystem; } result = builtin_status (result); if (builtin_is_special) - special_builtin_failed = 1; + special_builtin_failed = 1; /* XXX - take command builtin into account? */ } /* In POSIX mode, if there are assignment statements preceding a special builtin, they persist after the builtin @@ -4379,6 +4496,7 @@ run_builtin: goto run_builtin; } +execute_from_filesystem: if (command_line == 0) command_line = savestring (the_printed_command_except_trap ? the_printed_command_except_trap : ""); @@ -4439,11 +4557,12 @@ execute_builtin (builtin, words, flags, subshell) WORD_LIST *words; int flags, subshell; { - int result, eval_unwind, ignexit_flag, old_e_flag; - int isbltinenv; + int result, eval_unwind, ignexit_flag; + int isbltinenv, should_keep; char *error_trap; error_trap = 0; + should_keep = 0; /* The eval builtin calls parse_and_execute, which does not know about the setting of flags, and always calls the execution functions with @@ -4479,7 +4598,18 @@ execute_builtin (builtin, words, flags, subshell) problem only with the `unset', `source' and `eval' builtins. `mapfile' is a special case because it uses evalstring (same as eval or source) to run its callbacks. */ + /* SHOULD_KEEP is for the pop_scope call below; it only matters when + posixly_correct is set, but we should propagate the temporary environment + to the enclosing environment only for special builtins. */ isbltinenv = (builtin == source_builtin || builtin == eval_builtin || builtin == unset_builtin || builtin == mapfile_builtin); + should_keep = isbltinenv && builtin != mapfile_builtin; +#if defined (HISTORY) && defined (READLINE) + if (builtin == fc_builtin || builtin == read_builtin) + { + isbltinenv = 1; + should_keep = 0; + } +#endif if (isbltinenv) { @@ -4489,8 +4619,10 @@ execute_builtin (builtin, words, flags, subshell) if (temporary_env) { push_scope (VC_BLTNENV, temporary_env); + if (flags & CMD_COMMAND_BUILTIN) + should_keep = 0; if (subshell == 0) - add_unwind_protect (pop_scope, (flags & CMD_COMMAND_BUILTIN) ? 0 : "1"); + add_unwind_protect (pop_scope, should_keep ? "1" : 0); temporary_env = (HASH_TABLE *)NULL; } } @@ -4549,7 +4681,7 @@ execute_builtin (builtin, words, flags, subshell) if (error_trap) { set_error_trap (error_trap); - xfree (error_trap); + free (error_trap); } discard_unwind_frame ("eval_builtin"); } @@ -4561,8 +4693,6 @@ static void maybe_restore_getopt_state (gs) sh_getopt_state_t *gs; { - SHELL_VAR *v; - /* If we have a local copy of OPTIND and it's at the right (current) context, then we restore getopt's internal state. If not, we just let it go. We know there is a local OPTIND if gs->gs_flags & 1. @@ -4638,6 +4768,12 @@ execute_function (var, words, flags, fds_to_close, async, subshell) if (subshell == 0) { begin_unwind_frame ("function_calling"); + /* If the shell is in posix mode, this will push the variables in + the temporary environment to the "current shell environment" (the + global scope), and dispose the temporary env before setting it to + NULL later. This behavior has disappeared from the latest edition + of the standard, so I will eventually remove it from variables.c: + push_var_context. */ push_context (var->name, subshell, temporary_env); /* This has to be before the pop_context(), because the unwinding of local variables may cause the restore of a local declaration of @@ -4698,11 +4834,7 @@ execute_function (var, words, flags, fds_to_close, async, subshell) /* Shell functions inherit the RETURN trap if function tracing is on globally or on individually for this function. */ -#if 0 - if (return_trap && ((trace_p (var) == 0) && function_trace_mode == 0)) -#else if (return_trap && (signal_in_progress (DEBUG_TRAP) || ((trace_p (var) == 0) && function_trace_mode == 0))) -#endif { if (subshell == 0) { @@ -4741,6 +4873,11 @@ execute_function (var, words, flags, fds_to_close, async, subshell) /* The temporary environment for a function is supposed to apply to all commands executed within the function body. */ + /* Initialize BASH_ARGC and BASH_ARGV before we blow away the positional + parameters */ + if (debugging_mode || shell_compatibility_level <= 44) + init_bash_argv (); + remember_args (words->next, 1); /* Update BASH_ARGV and BASH_ARGC */ @@ -4911,6 +5048,8 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var, without_job_control (); set_sigchld_handler (); +#else + without_job_control (); #endif /* JOB_CONTROL */ set_sigint_handler (); @@ -4947,6 +5086,15 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var, fflush (stdout); if (r == EX_USAGE) r = EX_BADUSAGE; + /* XXX - experimental */ + else if (r == EX_DISKFALLBACK) + { + char *command_line; + + command_line = savestring (the_printed_command_except_trap ? the_printed_command_except_trap : ""); + r = execute_disk_command (words, (REDIRECT *)0, command_line, + -1, -1, async, (struct fd_bitmap *)0, flags|CMD_NO_FORK); + } sh_exit (r); } } @@ -4995,8 +5143,7 @@ execute_builtin_or_function (words, builtin, var, redirects, if (do_redirections (redirects, RX_ACTIVE|RX_UNDOABLE) != 0) { - cleanup_redirects (redirection_undo_list); - redirection_undo_list = (REDIRECT *)NULL; + undo_partial_redirects (); dispose_exec_redirects (); #if defined (PROCESS_SUBSTITUTION) free (ofifo_list); @@ -5063,11 +5210,7 @@ execute_builtin_or_function (words, builtin, var, redirects, discard_unwind_frame ("saved-redirects"); } - if (redirection_undo_list) - { - cleanup_redirects (redirection_undo_list); - redirection_undo_list = (REDIRECT *)NULL; - } + undo_partial_redirects (); #if defined (PROCESS_SUBSTITUTION) /* Close any FIFOs created by this builtin or function. */ @@ -5139,7 +5282,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, struct fd_bitmap *fds_to_close; int cmdflags; { - char *pathname, *command, **args; + char *pathname, *command, **args, *p; int nofork, stdpath, result; pid_t pid; SHELL_VAR *hookf; @@ -5149,6 +5292,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, nofork = (cmdflags & CMD_NO_FORK); /* Don't fork, just exec, if no pipes */ pathname = words->word->word; + p = 0; result = EXECUTION_SUCCESS; #if defined (RESTRICTED_SHELL) command = (char *)NULL; @@ -5168,9 +5312,15 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, #endif /* RESTRICTED_SHELL */ command = search_for_command (pathname, CMDSRCH_HASH|(stdpath ? CMDSRCH_STDPATH : 0)); + QUIT; if (command) { + /* If we're optimizing out the fork (implicit `exec'), decrement the + shell level like `exec' would do. */ + if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE) + adjust_shell_level (-1); + maybe_make_export_env (); put_command_name_into_env (command); } @@ -5182,7 +5332,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE) pid = 0; else - pid = make_child (savestring (command_line), async); + pid = make_child (p = savestring (command_line), async); if (pid == 0) { @@ -5194,6 +5344,10 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, CHECK_SIGTERM; +#if defined (JOB_CONTROL) + FREE (p); +#endif + /* restore_original_signals may have undone the work done by make_child to ensure that SIGINT and SIGQUIT are ignored in asynchronous children. */ @@ -5246,9 +5400,13 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, exit (EX_NOTFOUND); /* Posix.2 says the exit status is 127 */ } + /* We don't want to manage process groups for processes we start + from here, so we turn off job control and don't attempt to + manipulate the terminal's process group. */ + without_job_control (); + #if defined (JOB_CONTROL) - /* May need to reinitialize more of the job control state here. */ - kill_current_pipeline (); + set_sigchld_handler (); #endif wl = make_word_list (make_word (NOTFOUND_HOOK), words); @@ -5271,9 +5429,11 @@ parent_return: /* Make sure that the pipes are closed in the parent. */ close_pipes (pipe_in, pipe_out); #if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD) +#if 0 if (variable_context == 0) unlink_fifo_list (); #endif +#endif FREE (command); return (result); } @@ -5619,6 +5779,7 @@ execute_intern_function (name, funcdef) FUNCTION_DEF *funcdef; { SHELL_VAR *var; + char *t; if (check_identifier (name, posixly_correct) == 0) { @@ -5630,6 +5791,13 @@ execute_intern_function (name, funcdef) return (EXECUTION_FAILURE); } + if (strchr (name->word, CTLESC)) /* WHY? */ + { + t = dequote_escapes (name->word); + free (name->word); + name->word = t; + } + /* Posix interpretation 383 */ if (posixly_correct && find_special_builtin (name->word)) { @@ -5647,7 +5815,7 @@ execute_intern_function (name, funcdef) } #if defined (DEBUGGER) - bind_function_def (name->word, funcdef); + bind_function_def (name->word, funcdef, 1); #endif bind_function (name->word, funcdef->command); |
