characters on the current line, and set _rl_last_c_pos accordingly
in both multibyte and singlebyte locales.
Fixes bug reported by Andreas Schwab <schwab@linux-m68k.org>
+
+ 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 <omega@funker.no>
+
+ 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)
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
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;
}
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)
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.
<DT><B>BASHPID</B>
<DT>${<I>parameter</I><B>/%</B><I>pattern</I><B>/</B><I>string</I>}<DD>
<B>Pattern substitution</B>.
-The <I>pattern</I> is expanded to produce a pattern just as in
-pathname expansion and matched against the expanded value of
-<I>parameter</I>
-using the rules described under
+The <I>pattern</I> is expanded to produce a pattern
+and matched against the expanded value of <I>parameter</I>
+as described under
<B>Pattern Matching</B>
below.
<B>Case modification</B>.
This expansion modifies the case of alphabetic characters in <I>parameter</I>.
-The <I>pattern</I> is expanded to produce a pattern just as in
-pathname expansion.
-Each character in the expanded value of <I>parameter</I> is tested against
-<I>pattern</I>, and, if it matches the pattern, its case is converted.
+First, the <I>pattern</I> is expanded to produce a pattern
+as described below under
+<FONT SIZE=-1><B>Pattern Matching</B>.
+
+</FONT>
+<B>Bash</B>
+
+then examines characters in the expanded value of <I>parameter</I>
+against <I>pattern</I> as described below.
+If a character matches the pattern, its case is converted.
The pattern should not attempt to match more than one character.
<DT><DD>
-The <B>^</B> operator converts lowercase letters matching <I>pattern</I>
-to uppercase; the <B>,</B> operator converts matching uppercase letters
-to lowercase.
-The <B>^^</B> and <B>,,</B> expansions convert each matched character in the
-expanded value; the <B>^</B> and <B>,</B> expansions match and convert only
-the first character in the expanded value.
+Using
+
+converts lowercase letters matching <I>pattern</I> to uppercase;
+
+converts matching uppercase letters to lowercase.
+The
+<B>^</B> and <B>,</B> variants
+examine the first character in the expanded value
+and convert its case if it matches <I>pattern</I>;
+the
+<B>^^</B> and <B>,,</B> variants
+examine all characters in the expanded value
+and convert each one that matches <I>pattern</I>.
If <I>pattern</I> is omitted, it is treated like a <B>?</B>, which matches
every character.
<DT><DD>
<P>
<B>Pattern Matching</B>
+
<P>
Any character that appears in a pattern, other than the special pattern
<DD>
<DT><B>compat44</B>
-<DD>
-<DT><B>compat50</B>
-
<DD>
These control aspects of the shell's compatibility mode
<DT>*<DD>
Importing function definitions from the shell environment at startup.
<DT>*<DD>
-Parsing the value of
+Parsing the values of
+<FONT SIZE=-1><B>BASHOPTS</B>
+
+</FONT>
+and
<FONT SIZE=-1><B>SHELLOPTS</B>
</FONT>
<DT><A HREF="#lbDJ">BUGS</A><DD>
</DL>
<HR>
-This document was created by man2html from /usr/local/src/bash/bash-20241227/doc/bash.1.<BR>
-Time: 08 January 2025 09:33:16 EST
+This document was created by man2html from /usr/local/src/bash/bash-20250122/doc/bash.1.<BR>
+Time: 28 January 2025 09:45:18 EST
</BODY>
</HTML>
<dt><code class="code">${<var class="var">parameter</var>//<var class="var">pattern</var>/<var class="var">string</var>}</code></dt>
<dt><code class="code">${<var class="var">parameter</var>/#<var class="var">pattern</var>/<var class="var">string</var>}</code></dt>
<dt><code class="code">${<var class="var">parameter</var>/%<var class="var">pattern</var>/<var class="var">string</var>}</code></dt>
-<dd><p>The <var class="var">pattern</var> is expanded to produce a pattern just as in
-filename expansion and matched
-against the expanded value of <var class="var">parameter</var>
-according to the rules
-described below (see <a class="pxref" href="#Pattern-Matching">Pattern Matching</a>).
+<dd><p>The <var class="var">pattern</var> is expanded to produce a pattern
+and matched against the expanded value of <var class="var">parameter</var>
+as described below (see <a class="pxref" href="#Pattern-Matching">Pattern Matching</a>).
The longest match of <var class="var">pattern</var>
in the expanded value is replaced with <var class="var">string</var>.
<var class="var">string</var> undergoes tilde expansion, parameter and variable expansion,
<dt><code class="code">${<var class="var">parameter</var>,<var class="var">pattern</var>}</code></dt>
<dt><code class="code">${<var class="var">parameter</var>,,<var class="var">pattern</var>}</code></dt>
<dd><p>This expansion modifies the case of alphabetic characters in <var class="var">parameter</var>.
-The <var class="var">pattern</var> is expanded to produce a pattern just as in
-filename expansion.
-Each character in the expanded value of <var class="var">parameter</var> is tested against
-<var class="var">pattern</var>, and, if it matches the pattern, its case is converted.
+First, the <var class="var">pattern</var> is expanded to produce a pattern
+as described below in <a class="ref" href="#Pattern-Matching">Pattern Matching</a>.
+</p>
+<p><code class="code">Bash</code>
+then examines characters in the expanded value of <var class="var">parameter</var>
+against <var class="var">pattern</var> as described below.
+If a character matches the pattern, its case is converted.
The pattern should not attempt to match more than one character.
</p>
-<p>The ‘<samp class="samp">^</samp>’ operator converts lowercase letters matching <var class="var">pattern</var>
-to uppercase; the ‘<samp class="samp">,</samp>’ operator converts matching uppercase letters
-to lowercase.
-The ‘<samp class="samp">^^</samp>’ and ‘<samp class="samp">,,</samp>’ expansions convert each matched character in the
-expanded value; the ‘<samp class="samp">^</samp>’ and ‘<samp class="samp">,</samp>’ expansions match and convert only
-the first character in the expanded value.
+<p>Using
+‘<samp class="samp">^</samp>’
+converts lowercase letters matching <var class="var">pattern</var> to uppercase;
+‘<samp class="samp">,</samp>’
+converts matching uppercase letters to lowercase.
+The
+‘<samp class="samp">^</samp>’ and ‘<samp class="samp">,</samp>’ variants
+examine the first character in the expanded value
+and convert its case if it matches <var class="var">pattern</var>;
+the
+‘<samp class="samp">^^</samp>’ and ‘<samp class="samp">,,</samp>’ variants
+examine all characters in the expanded value
+and convert each one that matches <var class="var">pattern</var>.
If <var class="var">pattern</var> is omitted, it is treated like a ‘<samp class="samp">?</samp>’, which matches
every character.
</p>
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.
</p>
</dd>
‘<samp class="samp">*</samp>’, ‘<samp class="samp">-</samp>’, or ‘<samp class="samp">%</samp>’.
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.
</p>
<p>For example,
</p>
</dd>
<dt><code class="code">!!:$</code></dt>
-<dd><p>designates the last argument of the preceding command.
+<dd><p>designates the last word of the preceding command.
This may be shortened to <code class="code">!$</code>.
</p>
</dd>
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;
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)
{
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
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)
/* 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;
#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 */
}
# 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 */
/* Private variables. */
int volatile _rl_caught_signal = 0; /* should be sig_atomic_t, but that requires including <signal.h> 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). */
if (sig == SIGWINCH)
{
RL_SETSTATE(RL_STATE_SIGHANDLER);
+ _rl_handling_signal = SIGWINCH;
rl_resize_terminal ();
/* XXX - experimental for now */
if (rl_signal_event_hook)
(*rl_signal_event_hook) ();
+ _rl_handling_signal = 0;
RL_UNSETSTATE(RL_STATE_SIGHANDLER);
}
else
#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
rl_reset_after_signal ();
}
+ _rl_handling_signal = 0;
RL_UNSETSTATE(RL_STATE_SIGHANDLER);
SIGHANDLER_RETURN;
}
}
#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
_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);
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
--- /dev/null
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+# 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
# 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