From 15b199c0dd5d7616d5d1ff5bcb24ff2212efa2a9 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Tue, 11 Apr 2023 09:56:03 -0400 Subject: [PATCH] posix mode changes for interp 1150; fix for varassign redirection; fix for nameref in ${param=value} expansion --- CWRU/CWRU.chlog | 32 ++++++++++++++++++++++++++++++++ doc/bashref.texi | 7 +++++++ doc/version.texi | 6 +++--- execute_cmd.c | 2 +- parse.y | 4 +++- redir.c | 2 +- sig.c | 15 +++++++++++++++ sig.h | 4 ++++ subst.c | 32 ++++++++++++++++++++++++++++---- subst.h | 1 + tests/nameref.right | 4 ++++ tests/nameref4.sub | 14 ++++++++++++++ 12 files changed, 113 insertions(+), 10 deletions(-) diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 4c1b8201..02250cf4 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -5969,3 +5969,35 @@ lib/readline/vi_mode.c - _rl_domove_motion_cleanup: ditto for y/Y - rl_domove_motion_callback: if t/T/;/, fail (return non-zero without moving point), flag the motion command as having failed (MOVE_FAILED) + + 4/6 + --- +subst.c + - command_substitute: save the return status of the child process in + last_command_subst_status; don't assign to last_command_exit_value + in posix mode (posix interp 1150) + +execute_cmd.c + - execute_null_command: a simple command without a command word but + with command substitutions now returns last_command_subst_status. + It may or may not have already modified $? depending on posix mode + +redir.c + - redir_varvalue: legal_number validity check should be == 0, not < 0 + From a report by Grisha Levit + + 4/10 + ---- +subst.c + - parameter_brace_expand_rhs: check for namerefs in the variable name + part of the ${name=word} expansion so we can go back and implement + the POSIX semantics of returning "the final value of parameter." + From a report by Grisha Levit + +sig.c,sig.h + - sigpipe_handler: clean up, set $?, and throw to top level on receipt + of a SIGPIPE + - termsig_handler: if the variable builtin_catch_sigpipe is set (it's + not set anywhere yet), call sigpipe_handler instead of terminating + the shell. Still need to make sure a sighandler is installed for + SIGPIPE even if initialize_terminating_signals isn't called diff --git a/doc/bashref.texi b/doc/bashref.texi index e34a6717..e058c9b9 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -8561,6 +8561,13 @@ undergoes expansion. That means, for example, that a backslash preceding a double quote character will escape it and the backslash will be removed. +@item +Command substitutions don't set the @samp{?} special parameter. The exit +status of a simple command without a command word is still the exit status +of the last command substitution that occurred while evaluating the variable +assignments and redirections in that command, but that does not happen until +after all of the assignments and redirections. + @end enumerate There is other @sc{posix} behavior that Bash does not implement by diff --git a/doc/version.texi b/doc/version.texi index 6fc79710..928b9836 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -2,10 +2,10 @@ Copyright (C) 1988-2023 Free Software Foundation, Inc. @end ignore -@set LASTCHANGE Tue Mar 21 11:05:49 EDT 2023 +@set LASTCHANGE Thu Apr 6 11:58:41 EDT 2023 @set EDITION 5.2 @set VERSION 5.2 -@set UPDATED 21 March 2023 -@set UPDATED-MONTH March 2023 +@set UPDATED 6 April 2023 +@set UPDATED-MONTH April 2023 diff --git a/execute_cmd.c b/execute_cmd.c index e7bdcdc9..bfc76dea 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -4123,7 +4123,7 @@ execute_null_command (REDIRECT *redirects, int pipe_in, int pipe_out, int async) if (r != 0) return (EXECUTION_FAILURE); else if (last_command_subst_pid != NO_PID) - return (last_command_exit_value); + return (last_command_subst_status); else return (EXECUTION_SUCCESS); } diff --git a/parse.y b/parse.y index a4c4c07c..e70b340d 100644 --- a/parse.y +++ b/parse.y @@ -5891,7 +5891,7 @@ decode_prompt_string (char *string) WORD_LIST *list; char *result, *t, *orig_string; struct dstack save_dstack; - int last_exit_value, last_comsub_pid; + int last_exit_value, last_comsub_pid, last_comsub_status; #if defined (PROMPT_STRING_DECODE) size_t result_size; size_t result_index; @@ -6297,12 +6297,14 @@ not_escape: { last_exit_value = last_command_exit_value; last_comsub_pid = last_command_subst_pid; + last_comsub_status = last_command_subst_status; list = expand_prompt_string (result, Q_DOUBLE_QUOTES, 0); free (result); result = string_list (list); dispose_words (list); last_command_exit_value = last_exit_value; last_command_subst_pid = last_comsub_pid; + last_command_subst_status = last_comsub_status; } else { diff --git a/redir.c b/redir.c index 804d9b82..267a9fc8 100644 --- a/redir.c +++ b/redir.c @@ -1459,7 +1459,7 @@ redir_varvalue (REDIRECT *redir) if (val == 0 || *val == 0) return -1; - if (legal_number (val, &vmax) < 0) + if (legal_number (val, &vmax) == 0) return -1; i = vmax; /* integer truncation */ diff --git a/sig.c b/sig.c index b6ccaa19..fc0ae878 100644 --- a/sig.c +++ b/sig.c @@ -74,6 +74,8 @@ volatile sig_atomic_t sigterm_received = 0; /* Set to the value of any terminating signal received. */ volatile sig_atomic_t terminating_signal = 0; +volatile int builtin_catch_sigpipe = 0; + /* The environment at the top-level R-E loop. We use this in the case of error return. */ procenv_t top_level; @@ -591,6 +593,9 @@ termsig_handler (int sig) handling_termsig = terminating_signal; /* for termsig_sighandler */ terminating_signal = 0; /* keep macro from re-testing true. */ + if (builtin_catch_sigpipe) + sigpipe_handler (sig); + /* I don't believe this condition ever tests true. */ if (sig == SIGINT && signal_is_trapped (SIGINT)) run_interrupt_trap (0); @@ -714,6 +719,7 @@ sigint_sighandler (int sig) set_exit_status (128 + sig); throw_to_top_level (); } + #if defined (READLINE) /* Set the event hook so readline will call it after the signal handlers finish executing, so if this interrupted character input we can get @@ -762,6 +768,15 @@ sigterm_sighandler (int sig) SIGRETURN (0); } +void +sigpipe_handler (int sig) +{ + handling_termsig = 0; + builtin_catch_sigpipe = 0; + last_command_exit_value = 128 + sig; + throw_to_top_level (); +} + /* Signal functions used by the rest of the code. */ #if !defined (HAVE_POSIX_SIGNALS) diff --git a/sig.h b/sig.h index 720e7969..8d197be6 100644 --- a/sig.h +++ b/sig.h @@ -108,6 +108,8 @@ extern volatile sig_atomic_t sigterm_received; extern int interrupt_immediately; /* no longer used */ extern int terminate_immediately; +extern volatile int builtin_catch_sigpipe; /* not used yet */ + /* Functions from sig.c. */ extern sighandler termsig_sighandler (int); extern void termsig_handler (int); @@ -126,6 +128,8 @@ extern void unset_sigwinch_handler (void); extern sighandler sigterm_sighandler (int); +extern void sigpipe_handler (int); + /* Functions defined in trap.c. */ extern SigHandler *set_sigint_handler (void); extern SigHandler *trap_to_sighandler (int); diff --git a/subst.c b/subst.c index 555d18db..bdb5ba88 100644 --- a/subst.c +++ b/subst.c @@ -149,6 +149,7 @@ typedef WORD_LIST *EXPFUNC (const char *, int); /* Process ID of the last command executed within command substitution. */ pid_t last_command_subst_pid = NO_PID; pid_t current_command_subst_pid = NO_PID; +int last_command_subst_status = 0; /* Variables used to keep track of the characters in IFS. */ SHELL_VAR *ifs_var; @@ -6723,13 +6724,16 @@ command_substitute (char *string, int quoted, int flags) istring = optimize_cat_file (cmd->value.Simple->redirects, quoted, flags, &tflag); if (istring == &expand_param_error) { - last_command_exit_value = EXECUTION_FAILURE; + last_command_subst_status = EXECUTION_FAILURE; istring = 0; } else - last_command_exit_value = EXECUTION_SUCCESS; /* compat */ + last_command_subst_status = EXECUTION_SUCCESS; /* compat */ last_command_subst_pid = dollar_dollar_pid; + if (posixly_correct == 0) /* POSIX interp 1150 */ + last_command_exit_value = last_command_subst_status; /* XXX */ + dispose_command (cmd); ret = alloc_word_desc (); ret->word = istring; @@ -6974,10 +6978,13 @@ command_substitute (char *string, int quoted, int flags) UNBLOCK_SIGNAL (oset); current_command_subst_pid = pid; - last_command_exit_value = wait_for (pid, JWAIT_NOTERM); + last_command_subst_status = wait_for (pid, JWAIT_NOTERM); last_command_subst_pid = pid; last_made_pid = old_pid; + if (posixly_correct == 0) /* POSIX interp 1150 */ + last_command_exit_value = last_command_subst_status; /* XXX */ + #if defined (JOB_CONTROL) /* If last_command_exit_value > 128, then the substituted command was terminated by a signal. If that signal was SIGINT, then send @@ -7626,7 +7633,24 @@ parameter_brace_expand_rhs (char *name, char *value, return &expand_wdesc_error; } } - + /* We check for this here instead of letting bind_variable do it so we can + satisfy the POSIX semantics of returning the final value assigned to the + variable, even after assignment transformations (uppercase, lowercase, etc.). + We need the final name to get the right value back. */ + else if ((v = find_variable_last_nameref (name, 0)) && nameref_p (v)) + { + temp = nameref_cell (v); + /* shouldn't happen at this point, but... */ + if (temp == 0 || *temp == 0) + { + report_error (_("%s: bad substitution"), name); + free (t1); + dispose_word (w); + return &expand_wdesc_error; + } + vname = savestring (temp); + } + arrayref = 0; #if defined (ARRAY_VARS) if (valid_array_reference (vname, 0)) diff --git a/subst.h b/subst.h index c8c0adde..a42d8c84 100644 --- a/subst.h +++ b/subst.h @@ -350,6 +350,7 @@ extern int expanding_redir; extern int inherit_errexit; extern pid_t last_command_subst_pid; +extern int last_command_subst_status; /* Evaluates to 1 if C is a character in $IFS. */ #define isifs(c) (ifs_cmap[(unsigned char)(c)] != 0) diff --git a/tests/nameref.right b/tests/nameref.right index a28c8845..419bbe14 100644 --- a/tests/nameref.right +++ b/tests/nameref.right @@ -92,6 +92,10 @@ expect one expect four +expect +X +expect +X errors = 0 1 2 diff --git a/tests/nameref4.sub b/tests/nameref4.sub index 6367d565..970a85e9 100644 --- a/tests/nameref4.sub +++ b/tests/nameref4.sub @@ -228,6 +228,20 @@ echo "expect " echo $qux ckval qux ${bar[3]} +bar=() +declare -n ref='bar[1]' + +echo "expect " +echo ${ref=X} +ckval ref ${bar[1]} + +unset -n ref +declare -n ref + +echo "expect " +echo ${ref=X} +ckval ref ${ref} + # Need to add code and tests for `for' loop nameref variables echo errors = $errors