]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
allow locale's decimal point as radix character when parsing fixed-point decimal...
authorChet Ramey <chet.ramey@case.edu>
Wed, 17 Dec 2025 14:59:56 +0000 (09:59 -0500)
committerChet Ramey <chet.ramey@case.edu>
Wed, 17 Dec 2025 14:59:56 +0000 (09:59 -0500)
12 files changed:
.gitignore
CWRU/CWRU.chlog
MANIFEST
builtins/shopt.def
error.h
input.c
lib/readline/bind.c
lib/sh/uconvert.c
subst.c
tests/new-exp.right
tests/new-exp.tests
tests/new-exp17.sub [new file with mode: 0644]

index f334b7950068ae42cc73cd2b2dbeff549aea4f50..f357e17f91b12a37f1f84312b48c99f907b32e27 100644 (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
index ab59fcd180aab9c58ce2d37f791b69bc185a2040..509288adae618332cb3141df8a70f1cafb5b1aba 100644 (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>
index f9af056b5e90c50686e374aad3437934db165cec..1e00729f8aacf2cd6c6486fa734eedfed4c0759c 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -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
index 6ecc087f8272491783d91e1b8d944810b72a381d..ed057e6342682e03b476ec15fc3644002c655565 100644 (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;
 }
diff --git a/error.h b/error.h
index 6254fee1a318fda0d3a85cc19d255cf1346a9825..29db88f70c5ae14b260eefa41c4cea459d96a042 100644 (file)
--- a/error.h
+++ b/error.h
@@ -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)));
diff --git a/input.c b/input.c
index 180b7e12e313f3b789232221543a4485d59f04b1..5de6b7ca748bef5384ce1264acf74e2653da9e83 100644 (file)
--- a/input.c
+++ b/input.c
@@ -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;
index 6df5304593867183f13378b566b450c295138345..c663b63ce4fb40391e1d0ada900abf845941fd3f 100644 (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 },
index 39790f6bee1a7b363b7322f15aa7f005c284b391..a9a00bb62534dd1430771748b6c6cb623f687d42 100644 (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.
 
 #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. */
diff --git a/subst.c b/subst.c
index bdbafac8f5ab06dfb26da98437daa9f1313892ff..eabea2aaec7a4b15802abbd0820b2eaacf10a576 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -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 `&'
index ac720ac3bcc9303e6b3320421621578924d5cf92..6e0596189b32eeefdf4f9578547c3fe835b76ef3 100644 (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
index 05959ac283a067c9afd4f038e2d89a8871910a7e..d6126817719e2999cd94ae627b894b2095a978ef 100644 (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
diff --git a/tests/new-exp17.sub b/tests/new-exp17.sub
new file mode 100644 (file)
index 0000000..ffee9a6
--- /dev/null
@@ -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