From: Chet Ramey Date: Tue, 28 Jan 2025 15:15:16 +0000 (-0500) Subject: posix change for undoing redirections after failed exec; change readline to set lines... X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0390b4354a9e5df517ef2d4f9d78a099063b22b4;p=thirdparty%2Fbash.git posix change for undoing redirections after failed exec; change readline to set lines and columns after SIGTSTP/SIGCONT --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 22738d7c..9824a366 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -10870,3 +10870,54 @@ lib/readline/display.c characters on the current line, and set _rl_last_c_pos accordingly in both multibyte and singlebyte locales. Fixes bug reported by Andreas Schwab + + 1/22 + ---- +execute_cmd.c + - execute_builtin_or_function: if we're executing the exec builtin, + and there are redirections, let exec_builtin handle disposing of + redirection_undo_list if there is no program name to execute, so + it's still there if exec returns and doesn't exit the shell. + Then we can manage disposing of exec_redirection_undo_list and + letting the redirections be undone as normal. + This is POSIX interp 1896, with modifications from + https://www.austingroupbugs.net/view.php?id=1896#c7037 + +builtins/exec.def + - exec_builtin: dispose of redirection_undo_list and set it to NULL + if there is no program name operand; let execute_builtin_or_function + clean it up + + 1/23 + ---- +lib/readline/terminal.c + - _rl_tcgetwinsize,_rl_tcsetwinsize: function wrappers for tcgetwinsize/ + tcsetwinsize with fallbacks to ioctl if those functions aren't + available + - _rl_get_screensize: use _rl_tcgetwinsize + +lib/readline/rlwinsize.h + - _rl_tcgetwinsize,_rl_tcsetwinsize: extern function declarations + +lib/readine/rltty.c + - set_winsize: use _rl_tcgetwinsize/_rl_tcsetwinsize + - set_winsize: if _rl_tcgetwinsize succeeds, use the values to set + readline's idea of the screen size via _rl_set_screen_size + Inspired by a discussion with Olav Mørkrid + + 1/24 + ---- +lib/readline/signals.c + - _rl_handling_signal: new private variable, set to the signal we're + currently handling in _rl_signal_handler (SIGWINCH) or + _rl_handle_signal (all others). Only valid if RL_STATE_SIGHANDLER + is set + +lib/readline/rlprivate.h + - _rl_handling_signal: new extern declaration + +lib/readline/rltty.c + - set_winsize: only set readline's idea of the screen size if we're + executing in a signal handler context and handling SIGTSTP and the + application has indicated that it doesn't want the environment + variables LINES and COLUMNS to have priority (rl_prefer_env_winsize) diff --git a/MANIFEST b/MANIFEST index 72d684e3..76917764 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1181,6 +1181,7 @@ tests/exec13.sub f tests/exec14.sub f tests/exec15.sub f tests/exec16.sub f +tests/exec17.sub f tests/exp.tests f tests/exp.right f tests/exp1.sub f diff --git a/arrayfunc.c b/arrayfunc.c index 72921a18..a5537488 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -916,7 +916,7 @@ assign_array_var_from_string (SHELL_VAR *var, char *value, int flags) return var; nlist = expand_compound_array_assignment (var, value, flags); - /* This is were we set ASS_NOEXPAND and ASS_ONEWORD if we need to, since + /* This is where we set ASS_NOEXPAND and ASS_ONEWORD if we need to, since expand_compound_array_assignment performs word expansions. Honors array_expand_once; allows @ and * as associative array keys. */ aflags = flags | (array_expand_once ? ASS_NOEXPAND : 0) | ASS_ALLOWALLSUB; diff --git a/builtins/exec.def b/builtins/exec.def index 461864b5..2582add7 100644 --- a/builtins/exec.def +++ b/builtins/exec.def @@ -128,12 +128,13 @@ exec_builtin (WORD_LIST *list) } list = loptend; - /* First, let the redirections remain. */ - dispose_redirects (redirection_undo_list); - redirection_undo_list = (REDIRECT *)NULL; - + /* First, let the redirections remain if exec is called without operands */ if (list == 0) - return (EXECUTION_SUCCESS); + { + dispose_redirects (redirection_undo_list); + redirection_undo_list = (REDIRECT *)NULL; + return (EXECUTION_SUCCESS); + } #if defined (RESTRICTED_SHELL) if (restricted) diff --git a/doc/bash.html b/doc/bash.html index 04b3b5ba..e4635097 100644 --- a/doc/bash.html +++ b/doc/bash.html @@ -2122,6 +2122,8 @@ If this variable is in the environment when starts up, the shell enables each option in the list before reading any startup files. +If this variable is exported, child shells will enable each option +in the list. This variable is read-only.
BASHPID @@ -4822,10 +4824,9 @@ ${parameter/pattern/string}
${parameter/%pattern/string}
Pattern substitution. -The pattern is expanded to produce a pattern just as in -pathname expansion and matched against the expanded value of -parameter -using the rules described under +The pattern is expanded to produce a pattern +and matched against the expanded value of parameter +as described under Pattern Matching below. @@ -4910,18 +4911,31 @@ ${parameter^pattern} Case modification. This expansion modifies the case of alphabetic characters in parameter. -The pattern is expanded to produce a pattern just as in -pathname expansion. -Each character in the expanded value of parameter is tested against -pattern, and, if it matches the pattern, its case is converted. +First, the pattern is expanded to produce a pattern +as described below under +Pattern Matching. + + +Bash + +then examines characters in the expanded value of parameter +against pattern as described below. +If a character matches the pattern, its case is converted. The pattern should not attempt to match more than one character.
-The ^ operator converts lowercase letters matching pattern -to uppercase; the , operator converts matching uppercase letters -to lowercase. -The ^^ and ,, expansions convert each matched character in the -expanded value; the ^ and , expansions match and convert only -the first character in the expanded value. +Using + +converts lowercase letters matching pattern to uppercase; + +converts matching uppercase letters to lowercase. +The +^ and , variants +examine the first character in the expanded value +and convert its case if it matches pattern; +the +^^ and ,, variants +examine all characters in the expanded value +and convert each one that matches pattern. If pattern is omitted, it is treated like a ?, which matches every character.
@@ -5524,6 +5538,7 @@ as described above under

Pattern Matching +

Any character that appears in a pattern, other than the special pattern @@ -14819,9 +14834,6 @@ under

compat44 -
-
compat50 -
These control aspects of the shell's compatibility mode @@ -16523,7 +16535,11 @@ builtin command.
*
Importing function definitions from the shell environment at startup.
*
-Parsing the value of +Parsing the values of +BASHOPTS + + +and SHELLOPTS @@ -16858,7 +16874,7 @@ Array variables may not (yet) be exported.
BUGS

-This document was created by man2html from /usr/local/src/bash/bash-20241227/doc/bash.1.
-Time: 08 January 2025 09:33:16 EST +This document was created by man2html from /usr/local/src/bash/bash-20250122/doc/bash.1.
+Time: 28 January 2025 09:45:18 EST diff --git a/doc/bashref.html b/doc/bashref.html index 3a627571..60a62c6f 100644 --- a/doc/bashref.html +++ b/doc/bashref.html @@ -2990,11 +2990,9 @@ array in turn, and the expansion is the resultant list.
${parameter//pattern/string}
${parameter/#pattern/string}
${parameter/%pattern/string}
-

The pattern is expanded to produce a pattern just as in -filename expansion and matched -against the expanded value of parameter -according to the rules -described below (see Pattern Matching). +

The pattern is expanded to produce a pattern +and matched against the expanded value of parameter +as described below (see Pattern Matching). The longest match of pattern in the expanded value is replaced with string. string undergoes tilde expansion, parameter and variable expansion, @@ -3098,18 +3096,28 @@ array in turn, and the expansion is the resultant list.

${parameter,pattern}
${parameter,,pattern}

This expansion modifies the case of alphabetic characters in parameter. -The pattern is expanded to produce a pattern just as in -filename expansion. -Each character in the expanded value of parameter is tested against -pattern, and, if it matches the pattern, its case is converted. +First, the pattern is expanded to produce a pattern +as described below in Pattern Matching. +

+

Bash +then examines characters in the expanded value of parameter +against pattern as described below. +If a character matches the pattern, its case is converted. The pattern should not attempt to match more than one character.

-

The ‘^’ operator converts lowercase letters matching pattern -to uppercase; the ‘,’ operator converts matching uppercase letters -to lowercase. -The ‘^^’ and ‘,,’ expansions convert each matched character in the -expanded value; the ‘^’ and ‘,’ expansions match and convert only -the first character in the expanded value. +

Using +‘^’ +converts lowercase letters matching pattern to uppercase; +‘,’ +converts matching uppercase letters to lowercase. +The +‘^’ and ‘,’ variants +examine the first character in the expanded value +and convert its case if it matches pattern; +the +‘^^’ and ‘,,’ variants +examine all characters in the expanded value +and convert each one that matches pattern. If pattern is omitted, it is treated like a ‘?’, which matches every character.

@@ -7466,6 +7474,8 @@ as ‘on’ by ‘shopt If this variable is in the environment when Bash starts up, the shell enables each option in the list before reading any startup files. +If this variable is exported, child shells will enable each option +in the list. This variable is readonly.

@@ -14603,6 +14613,8 @@ It may be omitted if the word designator begins with a ‘*’, ‘-’, or ‘%’. Words are numbered from the beginning of the line, with the first word being denoted by 0 (zero). +That first word is usually the command word, and the arguments begin +with the second word. Words are inserted into the current line separated by single spaces.

For example, @@ -14614,7 +14626,7 @@ When you type this, the preceding command is repeated in toto.

!!:$
-

designates the last argument of the preceding command. +

designates the last word of the preceding command. This may be shortened to !$.

diff --git a/execute_cmd.c b/execute_cmd.c index d4d0febd..50df6c10 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -5570,7 +5570,7 @@ execute_builtin_or_function (WORD_LIST *words, REDIRECT *redirects, struct fd_bitmap *fds_to_close, int flags) { - int result; + int result, has_exec_redirects; REDIRECT *saved_undo_list; #if defined (PROCESS_SUBSTITUTION) int ofifo, nfifo, osize; @@ -5597,17 +5597,25 @@ execute_builtin_or_function (WORD_LIST *words, return (EX_REDIRFAIL); /* was EXECUTION_FAILURE */ } + /* Is this the exec builtin with redirections? We want to undo them and + throw away the exec_redirection_undo_list if exec has a program name + argument, fails to execute it, and does not exit the shell */ + has_exec_redirects = (builtin == exec_builtin) && redirection_undo_list; + saved_undo_list = redirection_undo_list; /* Calling the "exec" builtin changes redirections forever. */ if (builtin == exec_builtin) { - dispose_redirects (saved_undo_list); + /* let exec_builtin handle disposing redirection_undo_list */ saved_undo_list = exec_redirection_undo_list; exec_redirection_undo_list = (REDIRECT *)NULL; } else - dispose_exec_redirects (); + { + dispose_exec_redirects (); + redirection_undo_list = (REDIRECT *)NULL; + } if (saved_undo_list) { @@ -5615,8 +5623,6 @@ execute_builtin_or_function (WORD_LIST *words, add_unwind_protect (uw_cleanup_redirects, (char *)saved_undo_list); } - redirection_undo_list = (REDIRECT *)NULL; - if (builtin) result = execute_builtin (builtin, words, flags, 0); else @@ -5628,26 +5634,38 @@ execute_builtin_or_function (WORD_LIST *words, if (ferror (stdout)) clearerr (stdout); - /* 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 (has_exec_redirects && redirection_undo_list) { - int discard; - - discard = 0; + /* We have returned from the exec builtin. If redirection_undo_list is + still non-null, we had an operand and failed to exit the shell for + some reason. We want to dispose of saved_undo_list, discard the frame, + and let the redirections be undone as usual. If redirection_undo_list + is NULL, then exec_builtin had no program name operand and disposed + of it. In that case, we should perform the redirections in + exec_redirection_undo_list (saved_undo_list) like usual. */ + if (saved_undo_list) + { + dispose_redirects (saved_undo_list); /* exec_redirection_undo_list */ + discard_unwind_frame ("saved-redirects"); + } + saved_undo_list = exec_redirection_undo_list = (REDIRECT *)NULL; + } + /* This code is no longer executed and remains only for explanatory reasons. */ + else if (builtin == command_builtin && this_shell_builtin == exec_builtin) + { + /* 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 (saved_undo_list) { - dispose_redirects (saved_undo_list); - discard = 1; + dispose_redirects (saved_undo_list); /* redirection_undo_list */ + discard_unwind_frame ("saved-redirects"); } redirection_undo_list = exec_redirection_undo_list; saved_undo_list = exec_redirection_undo_list = (REDIRECT *)NULL; - if (discard) - discard_unwind_frame ("saved-redirects"); } if (saved_undo_list) diff --git a/lib/readline/rlprivate.h b/lib/readline/rlprivate.h index 23cf8d31..ac1de28c 100644 --- a/lib/readline/rlprivate.h +++ b/lib/readline/rlprivate.h @@ -640,6 +640,7 @@ extern int _rl_history_search_pos; /* signals.c */ extern int volatile _rl_caught_signal; +extern int volatile _rl_handling_signal; extern _rl_sigcleanup_func_t *_rl_sigcleanup; extern void *_rl_sigcleanarg; diff --git a/lib/readline/rltty.c b/lib/readline/rltty.c index a4ad94c9..9681775a 100644 --- a/lib/readline/rltty.c +++ b/lib/readline/rltty.c @@ -80,15 +80,25 @@ static int ksrflow; #endif /* Dummy call to force a backgrounded readline to stop before it tries - to get the tty settings. */ + to get the tty settings. But we use the information to set our idea + of the screen size if we're in a signal handling context, since it + doesn't make sense to waste it. */ static void set_winsize (int tty) { -#if defined (TIOCGWINSZ) +#if defined (TIOCGWINSZ) || defined (HAVE_TCGETWINSIZE) struct winsize w; - - if (ioctl (tty, TIOCGWINSZ, &w) == 0) - (void) ioctl (tty, TIOCSWINSZ, &w); + + if (_rl_tcgetwinsize (tty, &w) == 0) + { + (void) _rl_tcsetwinsize (tty, &w); + /* We restrict this to the case where we're running a signal handler + and executing after a SIGTSTP. We can relax it later. */ +#if defined (SIGTSTP) + if (RL_ISSTATE (RL_STATE_SIGHANDLER) && _rl_handling_signal == SIGTSTP && rl_prefer_env_winsize == 0) + _rl_set_screen_size (w.ws_row, w.ws_col); /* don't waste the info */ +#endif + } #endif /* TIOCGWINSZ */ } diff --git a/lib/readline/rlwinsize.h b/lib/readline/rlwinsize.h index d198fcf8..3d1c97fc 100644 --- a/lib/readline/rlwinsize.h +++ b/lib/readline/rlwinsize.h @@ -55,4 +55,7 @@ # define tcflow(fd, action) ioctl(fd, TCXONC, action) #endif +extern int _rl_tcgetwinsize (int, struct winsize *); +extern void _rl_tcsetwinsize (int, struct winsize *); + #endif /* _RL_WINSIZE_H */ diff --git a/lib/readline/signals.c b/lib/readline/signals.c index 1cdb7629..8c4f3fad 100644 --- a/lib/readline/signals.c +++ b/lib/readline/signals.c @@ -88,6 +88,7 @@ int rl_catch_sigwinch = 0; /* for the readline state struct in readline.c */ /* Private variables. */ int volatile _rl_caught_signal = 0; /* should be sig_atomic_t, but that requires including everywhere */ +int volatile _rl_handling_signal = 0; /* If non-zero, print characters corresponding to received signals as long as the user has indicated his desire to do so (_rl_echo_control_chars). */ @@ -133,6 +134,7 @@ _rl_signal_handler (int sig) if (sig == SIGWINCH) { RL_SETSTATE(RL_STATE_SIGHANDLER); + _rl_handling_signal = SIGWINCH; rl_resize_terminal (); /* XXX - experimental for now */ @@ -142,6 +144,7 @@ _rl_signal_handler (int sig) if (rl_signal_event_hook) (*rl_signal_event_hook) (); + _rl_handling_signal = 0; RL_UNSETSTATE(RL_STATE_SIGHANDLER); } else @@ -177,6 +180,7 @@ _rl_handle_signal (int sig) #endif /* !HAVE_POSIX_SIGNALS */ RL_SETSTATE(RL_STATE_SIGHANDLER); + _rl_handling_signal = sig; #if !defined (HAVE_BSD_SIGNALS) && !defined (HAVE_POSIX_SIGNALS) /* Since the signal will not be blocked while we are in the signal @@ -301,6 +305,7 @@ _rl_handle_signal (int sig) rl_reset_after_signal (); } + _rl_handling_signal = 0; RL_UNSETSTATE(RL_STATE_SIGHANDLER); SIGHANDLER_RETURN; } diff --git a/lib/readline/terminal.c b/lib/readline/terminal.c index 7003d2a9..2c70553d 100644 --- a/lib/readline/terminal.c +++ b/lib/readline/terminal.c @@ -258,6 +258,30 @@ _win_get_screensize (int *swp, int *shp) } #endif +int +_rl_tcgetwinsize (int tty, struct winsize *wp) +{ +#if defined (HAVE_TCGETWINSIZE) + return (tcgetwinsize (tty, wp)); +#elif defined (TIOCGWINSZ) + return (ioctl (tty, TIOCGWINSZ, wp)); +#else + return -1; +#endif +} + +void +_rl_tcsetwinsize (int tty, struct winsize *wp) +{ +#if defined (HAVE_TCGETWINSIZE) + tcsetwinsize (tty, wp); +#elif defined (TIOCGWINSZ) + ioctl (tty, TIOCSWINSZ, wp); +#else + ; +#endif +} + /* Get readline's idea of the screen size. TTY is a file descriptor open to the terminal. If IGNORE_ENV is true, we do not pay attention to the values of $LINES and $COLUMNS. The tests for TERM_STRING_BUFFER being @@ -266,19 +290,19 @@ void _rl_get_screen_size (int tty, int ignore_env) { char *ss; -#if defined (TIOCGWINSZ) +#if defined (TIOCGWINSZ) || defined (HAVE_TCGETWINSIZE) struct winsize window_size; -#endif /* TIOCGWINSZ */ +#endif /* TIOCGWINSZ || HAVE_TCGETWINSIZE */ int wr, wc; wr = wc = -1; -#if defined (TIOCGWINSZ) - if (ioctl (tty, TIOCGWINSZ, &window_size) == 0) +#if defined (TIOCGWINSZ) || defined (HAVE_TCGETWINSIZE) + if (_rl_tcgetwinsize (tty, &window_size) == 0) { wc = (int) window_size.ws_col; wr = (int) window_size.ws_row; } -#endif /* TIOCGWINSZ */ +#endif /* TIOCGWINSZ || HAVE_TCGETWINSIZE */ #if defined (__EMX__) _emx_get_screensize (&wc, &wr); diff --git a/tests/exec.right b/tests/exec.right index d584d805..0116f202 100644 --- a/tests/exec.right +++ b/tests/exec.right @@ -247,3 +247,25 @@ reached AND-AND body reached OR-OR body reached AND-AND group reached OR-OR group +./exec17.sub: line 26: exec: notthere: not found +after failed exec: 127 +./exec17.sub: line 31: exec: notthere: not found +after failed exec with output redirection +./exec17.sub: line 36: exec: notthere: not found +./exec17.sub: line 37: 4: Bad file descriptor +./exec17.sub: line 40: .: Is a directory +after failed redir stdout +after failed redir stderr +./exec17.sub: line 44: exec: notthere: not found +after failed exec with input redirection +./exec17.sub: line 50: exec: notthere: not found +after failed exec: 127 +./exec17.sub: line 55: exec: notthere: not found +after failed exec with output redirection +./exec17.sub: line 60: exec: notthere: not found +./exec17.sub: line 61: 4: Bad file descriptor +./exec17.sub: line 64: .: Is a directory +after failed redir stdout +after failed redir stderr +./exec17.sub: line 68: exec: notthere: not found +after failed exec with input redirection diff --git a/tests/exec17.sub b/tests/exec17.sub new file mode 100644 index 00000000..55b05073 --- /dev/null +++ b/tests/exec17.sub @@ -0,0 +1,69 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# test behavior of redirections when the shell doesn't exit and exec gets a +# program name to execute as an operand + +# set up a fixed path so we know notthere will not be found +PATH=/usr/bin:/bin +export PATH + +FN=/tmp/output-$$ +shopt -s execfail + +# make sure execfail works right, otherwise none of the rest matters +exec notthere +echo after failed exec: $? + +# basic redirection undoing if exec fails +rm -f $FN +exec notthere >$FN +echo after failed exec with output redirection +rm -f $FN + +# undo successful redirections if exec fails but does not exit the shell +exec notthere 4>&2 +echo foo >&4 + +# we should undo successful redirects if one of the redirections fails +exec >$FN 2>. +echo after failed redir stdout +echo after failed redir stderr >&2 + +exec notthere <<<'foo bar baz' +echo after failed exec with input redirection + +# the command builtin should not make a difference here + +# make sure execfail works right, otherwise none of the rest matters +command exec notthere +echo after failed exec: $? + +# basic redirection undoing if exec fails +rm -f $FN +command exec notthere >$FN +echo after failed exec with output redirection +rm -f $FN + +# undo successful redirections if exec fails but does not exit the shell +command exec notthere 4>&2 +echo foo >&4 + +# we should undo successful redirects if one of the redirections fails +command exec >$FN 2>. +echo after failed redir stdout +echo after failed redir stderr >&2 + +command exec notthere <<<'foo bar baz' +echo after failed exec with input redirection diff --git a/tests/execscript b/tests/execscript index 6dad13c9..aad37be4 100644 --- a/tests/execscript +++ b/tests/execscript @@ -204,3 +204,6 @@ ${THIS_SH} ./exec15.sub # problems with set -e and inverting commands' return status ${THIS_SH} ./exec16.sub + +# test behavior of redirections when exec fails and does not exit the shell +${THIS_SH} ./exec17.sub