allow locale's decimal point as radix character when parsing fixed-point decimal into seconds and fractional seconds; handle case of shell script file descriptor being made non-blocking; fix smalkl memory leak in programmable completion of shell option names; fix for patsub_replacement behavior when BASH_COMPAT = 42 and the replacement string is quoted; fix for readine when changing show-mode-in-prompt variable

This commit is contained in:
Chet Ramey
2025-12-17 09:59:56 -05:00
parent f27bf94a79
commit 2cdb2f9b31
12 changed files with 241 additions and 38 deletions
+2
View File
@@ -164,12 +164,14 @@ examples/loadables/accept
examples/loadables/asort
examples/loadables/basename
examples/loadables/cat
examples/loadables/chmod
examples/loadables/csv
examples/loadables/cut
examples/loadables/dirname
examples/loadables/dsv
examples/loadables/fdflags
examples/loadables/finfo
examples/loadables/fltexpr
examples/loadables/getconf
examples/loadables/head
examples/loadables/hello
+46
View File
@@ -12327,3 +12327,49 @@ builtins/printf.def
discussed in thread starting with
https://lists.gnu.org/archive/html/bug-bash/2025-02/msg00151.html
12/5
----
lib/sh/uconvert.c
- uconvert: allow locale's decimal point character to serve as radix
character when parsing fixed-point number into seconds and
microseconds
12/8
----
input.c
- b_fill_buffer: handle unlikely case of file descriptor being made
non-blocking by retrying read on EAGAIN/EWOULDBLOCK
Report and patch from Keno Fischer <keno@juliahub.com>
12/10
-----
builtins/shopt.def
- get_shopt_options: don't allocate memory for each member of the
option name array, since programmable completion doesn't free the
list members
From https://savannah.gnu.org/patch/?10551
12/12
-----
subst.c
- expand_string_for_patsub42: new function, uses code from bash-4.2 to
expand the pattern replacement string if patsub_replacement is not
set; calls expand_string_for_patsub with the same value for QUOTING
to quote patsub_replacement strings appropriately if it is set.
This means that double-quoting the pattern substitution has the
effect of quoting any `&' in the replacement string, which is iffy
but closer to what bash-4.2 did
- parameter_brace_patsub: call expand_string_for_patsub42 if the
shell compatibility level is <= 42
- quote_string_for_repl: if the shell compatibility level is <= 42
and the replacement string is quoted, quote backslashes that quote
`&' or `\' so strcreplace() preserves them
Fixes most patsub_replacement compatibility issues with bash-4.2
most recently raised by <a.elata@icloud.com>
12/16
-----
lib/readline/bind.c
- show-mode-in-prompt: add V_SPECIAL flag so hack_special_boolean_var()
gets called and _rl_reset_prompt will be called when it's changed
Report and fix from Martin D Kealey <martin@kurahaupo.gen.nz>
+1
View File
@@ -1391,6 +1391,7 @@ tests/new-exp13.sub f
tests/new-exp14.sub f
tests/new-exp15.sub f
tests/new-exp16.sub f
tests/new-exp17.sub f
tests/new-exp.right f
tests/nquote.tests f
tests/nquote.right f
+1 -1
View File
@@ -777,7 +777,7 @@ get_shopt_options (void)
n = sizeof (shopt_vars) / sizeof (shopt_vars[0]);
ret = strvec_create (n + 1);
for (i = 0; shopt_vars[i].name; i++)
ret[i] = savestring (shopt_vars[i].name);
ret[i] = shopt_vars[i].name;
ret[i] = (char *)NULL;
return ret;
}
+2 -2
View File
@@ -1,6 +1,6 @@
/* error.h -- External declarations of functions appearing in error.c. */
/* Copyright (C) 1993-2022 Free Software Foundation, Inc.
/* Copyright (C) 1993-2025 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@@ -30,7 +30,7 @@ extern char *get_name_for_error (void);
extern void file_error (const char *);
/* Report a programmer's error, and abort. Pass REASON, and ARG1 ... ARG5. */
extern void programming_error (const char *, ...) __attribute__((__format__ (printf, 1, 2))) __attribute__((__noreturn__));;
extern void programming_error (const char *, ...) __attribute__((__format__ (printf, 1, 2))) __attribute__((__noreturn__));
/* General error reporting. Pass FORMAT and ARG1 ... ARG5. */
extern void report_error (const char *, ...) __attribute__((__format__ (printf, 1, 2)));
+39 -19
View File
@@ -510,33 +510,53 @@ b_fill_buffer (BUFFERED_STREAM *bp)
if (bp->b_flag & B_ERROR) /* try making read errors `sticky' */
return EOF;
/* In an environment where text and binary files are treated differently,
compensate for lseek() on text files returning an offset different from
the count of characters read() returns. Text-mode streams have to be
treated as unbuffered. */
if ((bp->b_flag & (B_TEXT | B_UNBUFF)) == B_TEXT)
while (1) /* loop to handle non-blocking fds */
{
o = lseek (bp->b_fd, 0, SEEK_CUR);
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
if (nr > 0 && nr < lseek (bp->b_fd, 0, SEEK_CUR) - o)
/* In an environment where text and binary files are treated differently,
compensate for lseek() on text files returning an offset different from
the count of characters read() returns. Text-mode streams have to be
treated as unbuffered. */
if ((bp->b_flag & (B_TEXT | B_UNBUFF)) == B_TEXT)
{
lseek (bp->b_fd, o, SEEK_SET);
bp->b_flag |= B_UNBUFF;
bp->b_size = 1;
o = lseek (bp->b_fd, 0, SEEK_CUR);
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
if (nr > 0 && nr < lseek (bp->b_fd, 0, SEEK_CUR) - o)
{
lseek (bp->b_fd, o, SEEK_SET);
bp->b_flag |= B_UNBUFF;
bp->b_size = 1;
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
}
}
}
else
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
if (nr <= 0)
{
else
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
if (nr > 0)
break;
bp->b_used = bp->b_inputp = 0;
bp->b_buffer[0] = 0;
if (nr == 0)
bp->b_flag |= B_EOF;
{
bp->b_flag |= B_EOF;
return (EOF);
}
else if (errno == X_EAGAIN || errno == X_EWOULDBLOCK)
{
if (sh_unset_nodelay_mode (bp->b_fd) < 0)
{
sys_error (_("cannot reset nodelay mode for fd %d"), bp->b_fd);
bp->b_flag |= B_ERROR;
return (EOF);
}
continue;
}
else
bp->b_flag |= B_ERROR;
return (EOF);
{
bp->b_flag |= B_ERROR;
return (EOF);
}
}
bp->b_used = nr;
+1 -1
View File
@@ -1906,7 +1906,7 @@ static const struct {
{ "search-ignore-case", &_rl_search_case_fold, 0 },
{ "show-all-if-ambiguous", &_rl_complete_show_all, 0 },
{ "show-all-if-unmodified", &_rl_complete_show_unmodified, 0 },
{ "show-mode-in-prompt", &_rl_show_mode_in_prompt, 0 },
{ "show-mode-in-prompt", &_rl_show_mode_in_prompt, V_SPECIAL },
{ "skip-completed-text", &_rl_skip_completed_text, 0 },
#if defined (VISIBLE_STATS)
{ "visible-stats", &rl_visible_stats, 0 },
+12 -4
View File
@@ -1,7 +1,7 @@
/* uconvert - convert string representations of decimal numbers into whole
number/fractional value pairs. */
/* Copyright (C) 2008,2009,2020,2022 Free Software Foundation, Inc.
/* Copyright (C) 2008,2009,2020-2025 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@@ -29,13 +29,21 @@
#include <unistd.h>
#endif
#include <bashintl.h>
#include <stdc.h>
#include <stdio.h>
#include "chartypes.h"
#include "shell.h"
#include "builtins.h"
#define DECIMAL '.' /* XXX - should use locale */
#ifndef locale_decpoint
extern int locale_decpoint (void);
#endif
#define DECIMAL '.'
#define ISRADIX(c) ((c) == DECIMAL || (c) == locale_decpoint())
#define RETURN(x) \
do { \
@@ -76,7 +84,7 @@ uconvert(const char *s, long *ip, long *up, char **ep)
for ( ; p && *p; p++)
{
if (*p == DECIMAL) /* decimal point */
if (ISRADIX (*p)) /* radix character */
break;
if (DIGIT(*p) == 0)
RETURN(0);
@@ -86,7 +94,7 @@ uconvert(const char *s, long *ip, long *up, char **ep)
if (p == 0 || *p == 0) /* callers ensure p can never be 0; this is to shut up clang */
RETURN(1);
if (*p == DECIMAL)
if (ISRADIX (*p))
p++;
/* Look for up to six digits past a decimal point. */
+43 -11
View File
@@ -353,6 +353,7 @@ static int shouldexp_replacement (const char *);
static char *pos_params_pat_subst (char *, char *, char *, int);
static char *expand_string_for_patsub (char *, int);
static char *expand_string_for_patsub42 (char *, int); /* BASH_COMPAT=42 version */
static char *parameter_brace_patsub (char *, char *, array_eltstate_t *, char *, int, int, int);
static char *pos_params_casemod (char *, char *, int, int);
@@ -3917,7 +3918,7 @@ expand_assignment_string_to_string (char *string, int quoted)
or a backslash into a backslash. The output of this function must eventually
be processed by strcreplace(). */
static char *
quote_string_for_repl (const char *string, int flags)
quote_string_for_repl (const char *string, int quoted)
{
size_t slen;
char *result, *t;
@@ -3951,12 +3952,24 @@ quote_string_for_repl (const char *string, int flags)
{
/* This function's result has to be processed by strcreplace() */
if (*s == CTLESC && (s[1] == '&' || s[1] == '\\'))
{
*t++ = '\\';
s++;
*t++ = *s++;
continue;
}
{
*t++ = '\\';
s++;
*t++ = *s++;
continue;
}
/* Bash-4.2 and earlier don't perform quote removal on double-quoted
pattern substitutions. */
if (shell_compatibility_level <= 42 &&
(quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) &&
*s == '\\' && (s[1] == '&' || s[1] == '\\'))
{
*t++ = '\\'; *t++ = '\\';
*t++ = '\\';
s++;
*t++ = *s++;
continue;
}
/* Dequote it */
if (*s == CTLESC)
{
@@ -4006,6 +4019,28 @@ expand_string_for_patsub (char *string, int quoted)
return (ret);
}
static char *
expand_string_for_patsub42 (char *string, int quoted)
{
char *ret, *t;
if (string == 0 || *string == '\0')
return (char *)NULL;
/* This is the bash-4.2 code from parameter_brace_patsub(). */
if (patsub_replacement == 0)
{
if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0)
ret = expand_string_if_necessary (string, quoted, expand_string_unsplit);
else
ret = expand_string_to_string_internal (string, quoted, expand_string_unsplit);
}
else
ret = expand_string_for_patsub (string, quoted);
return (ret);
}
char *
expand_arith_string (char *string, int quoted)
{
@@ -9501,11 +9536,8 @@ parameter_brace_patsub (char *varname, char *value, array_eltstate_t *estatep,
rep = expand_string_if_necessary (rep, quoted & ~(Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT), expand_string_unsplit);
else if (shell_compatibility_level > 42 && patsub_replacement)
rep = expand_string_for_patsub (rep, quoted & ~(Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT));
/* This is the bash-4.2 code. */
else if ((mflags & MATCH_QUOTED) == 0)
rep = expand_string_if_necessary (rep, quoted, expand_string_unsplit);
else
rep = expand_string_to_string_internal (rep, quoted, expand_string_unsplit);
rep = expand_string_for_patsub42 (rep, quoted);
/* Check whether or not to replace `&' in the replacement string after
expanding it, since we want to treat backslashes quoting the `&'
+25
View File
@@ -823,4 +823,29 @@ two
&two
otwone
&twone
new-exp17.sub
unquoted word expansion with quoted pattern and replacement
4.2: 4.2, a &lt; b
5.3: 4.2, a &lt; b
5.3: 5.2, a &lt; b
double-quoted word expansion with quoted pattern and replacement
4.2: 4.2, a '&lt;' b
5.3: 4.2, a '&lt;' b
5.3: 5.2, a &lt; b
unquoted word expansion with unquoted pattern and replacement
4.2: 4.2, a &lt; b
5.3: 4.2, a <lt; b
5.3: 5.2, a <lt; b
double-quoted word expansion with unquoted pattern and replacement
4.2: 4.2, a &lt; b
5.3: 4.2, a &lt; b
5.3: 5.2, a <lt; b
unquoted word expansion with backslash-quoted &
4.2: 4.2, a &lt; b
5.3: 4.2, a &lt; b
5.3: 5.2, a &lt; b
double-quoted word expansion with backslash-quoted &
4.2: 4.2, a \&lt; b
5.3: 4.2, a \&lt; b
5.3: 5.2, a &lt; b
./new-exp.tests: line 1: ABXD: parameter unset
+1
View File
@@ -668,6 +668,7 @@ test_runsub ./new-exp15.sub
# pattern substitution with `&' (quoted and unquoted) in the replacement string
test_runsub ./new-exp16.sub
test_runsub ./new-exp17.sub # test bash-4.2 compat
expect $0: 'ABXD: parameter unset'
${THIS_SH} -c 'recho ${ABXD:?"parameter unset"}' $0
+68
View File
@@ -0,0 +1,68 @@
# 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/>.
#
# address issues with patsub_replacement, quoting `&', and BASH_COMPAT <= 42
# unquoted pattern substitutions with unquoted pattern and replacement strings
# will still perform matching and replacement even if BASH_COMPAT == 42
s="a < b"
echo unquoted word expansion with quoted pattern and replacement
echo '4.2: 4.2, a &lt; b'
for ver in 4.2 5.2; do
BASH_COMPAT=$ver # ignored for bash version less than 5.0
t=${s//'<'/'&lt;'}
echo "${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}: $BASH_COMPAT, $t"
done
echo double-quoted word expansion with quoted pattern and replacement
echo "4.2: 4.2, a '&lt;' b"
for ver in 4.2 5.2; do
BASH_COMPAT=$ver # ignored for bash version less than 5.0
t="${s//'<'/'&lt;'}"
echo "${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}: $BASH_COMPAT, $t"
done
echo unquoted word expansion with unquoted pattern and replacement
echo '4.2: 4.2, a &lt; b'
for ver in 4.2 5.2; do
BASH_COMPAT=$ver # ignored for bash version less than 5.0
t=${s//</&lt;}
echo "${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}: $BASH_COMPAT, $t"
done
# XXX - this is iffy but difficult to distinguish internally
echo double-quoted word expansion with unquoted pattern and replacement
echo '4.2: 4.2, a &lt; b'
for ver in 4.2 5.2; do
BASH_COMPAT=$ver # ignored for bash version less than 5.0
t="${s//</&lt;}"
echo "${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}: $BASH_COMPAT, $t"
done
echo unquoted word expansion with backslash-quoted '&'
echo '4.2: 4.2, a &lt; b'
for ver in 4.2 5.2; do
BASH_COMPAT=$ver # ignored for bash version less than 5.0
t=${s//</\&lt;}
echo "${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}: $BASH_COMPAT, $t"
done
echo double-quoted word expansion with backslash-quoted '&'
echo '4.2: 4.2, a \&lt; b'
for ver in 4.2 5.2; do
BASH_COMPAT=$ver # ignored for bash version less than 5.0
t="${s//</\&lt;}"
echo "${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}: $BASH_COMPAT, $t"
done