directly, so we can print a message with the name of the sub-test
script into the output. This will help with identifying the script
that fails
+ Original suggestion from Martin D Kealey <martin@kurahaupo.gen.nz>
+
+ 10/16
+ -----
+tests/run-*
+ - highlight `warning' in warning messages
+
+ 10/17
+ -----
+lib/glob/sm_loop.c
+ - BRACKMATCH: fix range parsing to treat collating symbols and
+ character classes consistently, don't treat invalid collating
+ symbols or equivalence classes as 0xFF, fix issue with escaped
+ open brackets when parsing the end of a range expression
+ Report and patch from Grisha Levit <grishalevit@gmail.com>
+
+oslib.c
+ - bcopy: update function declaration to add `const'
-/* Copyright (C) 1991-2023 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2025 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
static CHAR *
BRACKMATCH (CHAR *p, U_CHAR test, int flags)
{
- register CHAR cstart, cend, c;
- register int not; /* Nonzero if the sense of the character class is inverted. */
- int forcecoll, isrange;
+ CHAR c;
+ INT cstart, cend;
+ int not; /* Nonzero if the sense of the character class is inverted. */
+ int forcecoll;
INT pc;
CHAR *savep;
CHAR *close;
if (not = (*p == L('!') || *p == L('^')))
++p;
- c = *p++;
for (;;)
{
- /* Initialize cstart and cend in case `-' is the last
- character of the pattern. */
- cstart = cend = c;
- forcecoll = 0;
+ c = *p++;
+
+ /* `]' ends the bracket expression, unless it is the first character. */
+ if (c == L(']') && (p > savep + not + 1))
+ break;
/* POSIX.2 equivalence class: [=c=]. See POSIX.2 2.8.3.2. Find
the end of the equivalence class, move the pattern pointer past
{
p++;
pc = COLLSYM (p, close - p);
- pc = FOLD (pc);
p = close + 2;
- if (COLLEQUIV (test, pc))
- {
-/*[*/ /* Move past the closing `]', since the first thing we do at
- the `matched:' label is back p up one. */
- p++;
- goto matched;
- }
+ if (pc != INVALID && COLLEQUIV (test, FOLD (pc)))
+ goto matched;
+
else
- {
- c = *p++;
- if (c == L('\0'))
- return ((test == L('[')) ? savep : (CHAR *)0); /*]*/
- else if (c == L('/') && (flags & FNM_PATHNAME))
- return ((test == L('[')) ? savep : (CHAR *)0); /*]*/
- else if (c == L(']'))
- break;
- c = FOLD (c);
- continue;
- }
+ /* Continue the loop here, since this expression can't be
+ the first part of a range expression. */
+ continue;
}
/* POSIX.2 character class expression. See POSIX.2 2.8.3.2. */
p = close + 2;
if (pc)
- {
-/*[*/ /* Move past the closing `]', since the first thing we do at
- the `matched:' label is back p up one. */
- p++;
- goto matched;
- }
+ goto matched;
else
- {
- /* continue the loop here, since this expression can't be
- the first part of a range expression. */
- c = *p++;
- if (c == L('\0'))
- return ((test == L('[')) ? savep : (CHAR *)0);
- else if (c == L('/') && (flags & FNM_PATHNAME))
- return ((test == L('[')) ? savep : (CHAR *)0); /*]*/
- else if (c == L(']'))
- break;
- c = FOLD (c);
- continue;
- }
+ /* Continue the loop here, since this expression can't be
+ the first part of a range expression. */
+ continue;
}
/* POSIX.2 collating symbols. See POSIX.2 2.8.3.2. Find the end of
p = close + 2;
forcecoll = 1;
}
-
- if (!(flags & FNM_NOESCAPE) && c == L('\\'))
+ else
{
- if (*p == '\0')
- return ((test == L('[')) ? savep : (CHAR *)0);
- else if (*p == L('/') && (flags & FNM_PATHNAME))
+ if ((flags & FNM_NOESCAPE) == 0 && c == L('\\'))
+ c = *p++;
+
+ /* POSIX.2 2.8.3.1.2 says: `An expression containing a `[' that
+ is not preceded by a backslash and is not part of a bracket
+ expression produces undefined results.' This implementation
+ treats the `[' as just a character to be matched if there is
+ not a closing `]'. */
+ if (c == L('\0'))
return ((test == L('[')) ? savep : (CHAR *)0);
- cstart = cend = *p++;
- }
- cstart = cend = FOLD (cstart);
- isrange = 0;
-
- /* POSIX.2 2.8.3.1.2 says: `An expression containing a `[' that
- is not preceded by a backslash and is not part of a bracket
- expression produces undefined results.' This implementation
- treats the `[' as just a character to be matched if there is
- not a closing `]'. */
- if (c == L('\0'))
- return ((test == L('[')) ? savep : (CHAR *)0);
-
- /* POSIX.2 2.13.3 says: `If a <slash> character is found following an
- unescaped <left-square-bracket> character before a corresponding
- <right-square-bracket> is found, the open bracket shall be treated
- as an ordinary character.' If we find a slash in a bracket
- expression and the flags indicate we're supposed to be treating the
- string like a pathname, we have to treat the `[' as just a character
- to be matched. */
- if (c == L('/') && (flags & FNM_PATHNAME))
- return ((test == L('[')) ? savep : (CHAR *)0);
-
- c = *p++;
- c = FOLD (c);
+ /* POSIX.2 2.13.3 says: `If a <slash> character is found following an
+ unescaped <left-square-bracket> character before a corresponding
+ <right-square-bracket> is found, the open bracket shall be treated
+ as an ordinary character.' If we find a slash in a bracket
+ expression and the flags indicate we're supposed to be treating the
+ string like a pathname, we have to treat the `[' as just a character
+ to be matched. */
+ if (c == L('/') && (flags & FNM_PATHNAME))
+ return ((test == L('[')) ? savep : (CHAR *)0);
- if (c == L('\0'))
- return ((test == L('[')) ? savep : (CHAR *)0);
- else if (c == L('/') && (flags & FNM_PATHNAME))
- return ((test == L('[')) ? savep : (CHAR *)0);
+ cstart = c;
+ forcecoll = 0;
+ }
- /* This introduces a range, unless the `-' is the last
- character of the class. Find the end of the range
- and move past it. */
- if (c == L('-') && *p != L(']'))
+ /* Range expression, unless `-' is followed by a `]' or an equivalence
+ class or a character class. POSIX.1-2024 9.3.5.7 says: ``The starting
+ range point and the ending range point shall be a collating element or
+ collating symbol. An equivalence class expression used as a starting
+ or ending point of a range expression produces unspecified results.''
+ We treat the `-' in `a-[=c=]' as matching itself to be consistent with
+ how we handled `[=c=]' above. */
+ if (p[0] == L('-') && p[1] != L(']') &&
+ !(p[1] == L('[') && (p[2] == L('=') || p[2] == L(':')) && PARSE_SUBBRACKET (p + 2, flags) != NULL))
{
- cend = *p++;
- if (!(flags & FNM_NOESCAPE) && cend == L('\\'))
- cend = *p++;
- if (cend == L('\0'))
- return ((test == L('[')) ? savep : (CHAR *)0);
- else if (cend == L('/') && (flags & FNM_PATHNAME))
- return ((test == L('[')) ? savep : (CHAR *)0);
- if (cend == L('[') && *p == L('.') && (close = PARSE_SUBBRACKET (p, flags)) != NULL)
+ p++; /* step over `-' */
+ c = *p++;
+ if (c == L('[') && *p == L('.') && (close = PARSE_SUBBRACKET (p, flags)) != NULL)
{
p++;
cend = COLLSYM (p, close - p);
p = close + 2;
forcecoll = 1;
}
- cend = FOLD (cend);
+ else
+ {
+ if (!(flags & FNM_NOESCAPE) && c == L('\\'))
+ c = *p++;
+ if (c == L('\0'))
+ return ((test == L('[')) ? savep : (CHAR *)0);
+ else if (c == L('/') && (flags & FNM_PATHNAME))
+ return ((test == L('[')) ? savep : (CHAR *)0);
+ cend = c;
+ }
- c = *p++;
+ /* A range with an invalid start or end matches nothing. */
+ if (cstart == INVALID || cend == INVALID)
+ continue;
+ cstart = FOLD (cstart);
+ cend = FOLD (cend);
/* POSIX.2 2.8.3.2: ``The ending range point shall collate
equal to or higher than the starting range point; otherwise
the expression shall be treated as invalid.'' Note that this
applies to only the range expression; the rest of the bracket
expression is still checked for matches. */
- if (RANGECMP (cstart, cend, forcecoll) > 0)
- {
- if (c == L(']'))
- break;
- c = FOLD (c);
- continue;
- }
- isrange = 1;
+ if (RANGECMP (cstart, cend, forcecoll) <= 0 &&
+ RANGECMP (test, cstart, forcecoll) >= 0 && RANGECMP (test, cend, forcecoll) <= 0)
+ goto matched;
+ }
+ else
+ {
+ /* Not a range. */
+ if (cstart != INVALID && test == FOLD (cstart))
+ goto matched;
}
-
- if (isrange == 0 && test == cstart)
- goto matched;
- if (isrange && RANGECMP (test, cstart, forcecoll) >= 0 && RANGECMP (test, cend, forcecoll) <= 0)
- goto matched;
-
- if (c == L(']'))
- break;
}
/* No match. */
return (!not ? (CHAR *)0 : p);
matched:
/* Skip the rest of the [...] that already matched. */
- c = *--p;
while (1)
{
- /* A `[' without a matching `]' is just another character to match. */
- if (c == L('\0'))
- return ((test == L('[')) ? savep : (CHAR *)0);
- else if (c == L('/') && (flags & FNM_PATHNAME))
- return ((test == L('[')) ? savep : (CHAR *)0);
-
c = *p++;
if (c == L('[') && (*p == L('=') || *p == L(':') || *p == L('.')))
{
the bracket expression. */
else if (c == L(']'))
break;
- else if (!(flags & FNM_NOESCAPE) && c == L('\\'))
+ else
{
- if (*p == '\0')
+ if (!(flags & FNM_NOESCAPE) && c == L('\\'))
+ c = *p++;
+ if (c == '\0')
return ((test == L('[')) ? savep : (CHAR *)0);
/* We don't allow backslash to quote slash if we're matching pathnames */
- else if (*p == L('/') && (flags & FNM_PATHNAME))
+ else if (c == L('/') && (flags & FNM_PATHNAME))
return ((test == L('[')) ? savep : (CHAR *)0);
/* Posix issue 8 leaves this unspecified for the shell. */
- ++p;
}
}
return (not ? (CHAR *)0 : p);
# undef bcopy
# endif
void
-bcopy (void *s, void *d, size_t n)
+bcopy (const void *s, void *d, size_t n)
{
FASTCOPY (s, d, n);
}
export BASH_TSTOUT=/tmp/xx
rm -f ${BASH_TSTOUT}
+# keep track of passed and failed tests and report them
+if [ -t 1 ]; then
+ # can't rely on having $'...' or printf understanding \e
+ # bright red background, white foreground text
+ export CSTART=$(printf '\033[01;101;37m') CEND=$(printf '\033[0m')
+else
+ export CSTART= CEND=
+fi
export TAB=' '
${THIS_SH} "$@"
case $intmax_max in
9223372036854775807) ;;
-*) echo "warning: your machine does not support 64-bit arithmetic using intmax_t" 2>&1 ;;
+*) echo "${CSTART}warning${CEND}: your machine does not support 64-bit arithmetic using intmax_t" 2>&1 ;;
esac
# these are actually the same
--- /dev/null
+for f ; do
+ echo $f
+ sed 's|warning:|${CSTART}warning${CEND}:|' < $f > zzz && mv zzz $f
+done
# this locale causes problems all over the place
if [ -z "$ZH_LOCALE" ]; then
- echo "glob2.sub: warning: you do not have the zh_TW.big5 locale installed;" >&2
- echo "glob2.sub: warning: that may cause some of these tests to fail." >&2
+ echo "${CSTART}glob2.sub: warning${CEND}: you do not have the zh_TW.big5 locale installed;" >&2
+ echo "${TAB}glob2.sub: that may cause some of these tests to fail." >&2
ZH_LOCALE=$ZH_DEFAULT
fi
if locale -a | grep -i '^de_DE\.UTF.*8' >/dev/null ; then
export LANG=de_DE.UTF-8
else
- echo "intl2.sub: warning: you do not have the de_DE.UTF-8 locale installed;" >&2
- echo "intl2.sub: that will cause some of these tests to fail." >&2
+ echo "${CSTART}intl2.sub: warning${CEND}: you do not have the de_DE.UTF-8 locale installed;" >&2
+ echo "${TAB}intl2.sub: that will cause some of these tests to fail." >&2
fi
printf '%.4f\n' 1
else
CSTART= CEND=
fi
+export CSTART CEND
passed=0
total=0
-echo "warning: all of these tests will fail if arrays have not" >&2
+echo "${CSTART}warning${CEND}: all of these tests will fail if arrays have not" >&2
echo "${TAB}been compiled into the shell" >&2
-echo "warning: the BASH_ARGC and BASH_ARGV tests will fail if debugging" >&2
+echo "${CSTART}warning${CEND}: the BASH_ARGC and BASH_ARGV tests will fail if debugging" >&2
echo "${TAB}support has not been compiled into the shell" >&2
${THIS_SH} ./array.tests > ${BASH_TSTOUT} 2>&1
diff ${BASH_TSTOUT} array.right && rm -f ${BASH_TSTOUT}
-echo "warning: all of these tests will fail if arrays have not" >&2
+echo "${CSTART}warning${CEND}: all of these tests will fail if arrays have not" >&2
echo "${TAB}been compiled into the shell" >&2
${THIS_SH} ./array-at-star > ${BASH_TSTOUT} 2>&1
diff ${BASH_TSTOUT} array2.right && rm -f ${BASH_TSTOUT}
-echo "warning: all of these tests will fail if arrays have not" >&2
+echo "${CSTART}warning${CEND}: all of these tests will fail if arrays have not" >&2
echo "${TAB}been compiled into the shell" >&2
${THIS_SH} ./assoc.tests > ${BASH_TSTOUT} 2>&1
diff ${BASH_TSTOUT} assoc.right && rm -f ${BASH_TSTOUT}
-echo "warning: some of these tests may fail if process substitution has not" >&2
+echo "${CSTART}warning${CEND}: some of these tests may fail if process substitution has not" >&2
echo "${TAB}been compiled into the shell or if the OS does not provide" >&2
echo "${TAB}/dev/fd." >&2
-echo "warning: all of these tests will fail if the conditional command has not" >&2
+echo "${CSTART}warning${CEND}: all of these tests will fail if the conditional command has not" >&2
echo "${TAB}been compiled into the shell" >&2
-echo "warning: some of these tests will fail if extended pattern matching has not" >&2
+echo "${CSTART}warning${CEND}: some of these tests will fail if extended pattern matching has not" >&2
echo "${TAB}been compiled into the shell" >&2
-echo "warning: the text of system error messages may vary between systems and" >&2
+echo "${CSTART}warning${CEND}: the text of system error messages may vary between systems and" >&2
echo "${TAB}produce diff output." >&2
${THIS_SH} ./cond.tests > ${BASH_TSTOUT} 2>&1
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-echo "warning: the text of a system error message may vary between systems and" >&2
+echo "${CSTART}warning${CEND}: the text of a system error message may vary between systems and" >&2
echo "${TAB}produce diff output." >&2
echo "${TAB}if the text of the error messages concerning \`notthere' or" >&2
echo "${TAB}\`/tmp/bash-notthere' not being found or \`/' being a directory" >&2
echo "${TAB}produce diff output, please do not consider this a test failure" >&2
-echo "warning: if diff output differing only in the location of the bash" >&2
+echo "${CSTART}warning${CEND}: if diff output differing only in the location of the bash" >&2
echo "${TAB}binary appears, please do not consider this a test failure" >&2
${THIS_SH} ./execscript > ${BASH_TSTOUT} 2>&1
diff ${BASH_TSTOUT} exec.right && rm -f ${BASH_TSTOUT}
-echo "warning: if you have exported functions defined in your environment," >&2
+echo "${CSTART}warning${CEND}: if you have exported functions defined in your environment," >&2
echo "${TAB}they may show up as diff output." >&2
echo "${TAB}If so, please do not consider this a test failure" >&2
${THIS_SH} ./func.tests > ${BASH_TSTOUT} 2>&1
-echo "warning: UNIX versions number signals and schedule processes differently." >&2
+echo "${CSTART}warning${CEND}: UNIX versions number signals and schedule processes differently." >&2
echo "${TAB}If output differing only in line numbers is produced, please" >&2
echo "${TAB}do not consider this a test failure." >&2
-echo "warning: all of these tests will fail if history has not been compiled" >&2
+echo "${CSTART}warning${CEND}: all of these tests will fail if history has not been compiled" >&2
echo "${TAB}into the shell" >&2
${THIS_SH} ./histexp.tests > ${BASH_TSTOUT} 2>&1
diff ${BASH_TSTOUT} histexp.right && rm -f ${BASH_TSTOUT}
-echo "warning: all of these tests will fail if history has not been compiled" >&2
+echo "${CSTART}warning${CEND}: all of these tests will fail if history has not been compiled" >&2
echo "${TAB}into the shell" >&2
${THIS_SH} ./history.tests > ${BASH_TSTOUT} 2>&1
diff ${BASH_TSTOUT} history.right && rm -f ${BASH_TSTOUT}
-echo "warning: some of these tests may fail if job control has not been compiled" >&2
+echo "${CSTART}warning${CEND}: some of these tests may fail if job control has not been compiled" >&2
echo "${TAB}into the shell" >&2
-echo "warning: there may be a message regarding a cat process dying due to a" >&2
+echo "${CSTART}warning${CEND}: there may be a message regarding a cat process dying due to a" >&2
echo "${TAB}SIGHUP. Please disregard." >&2
${THIS_SH} ./jobs.tests > ${BASH_TSTOUT} 2>&1
-echo "warning: some of these tests will fail if arrays have not" >&2
+echo "${CSTART}warning${CEND}: some of these tests will fail if arrays have not" >&2
echo "${TAB}been compiled into the shell" >&2
${THIS_SH} ./nameref.tests > ${BASH_TSTOUT} 2>&1
diff ${BASH_TSTOUT} nameref.right && rm -f ${BASH_TSTOUT}
-echo "warning: two of these tests will fail if your OS does not support" >&2
+echo "${CSTART}warning${CEND}: two of these tests will fail if your OS does not support" >&2
echo "${TAB}named pipes or the /dev/fd filesystem. If the tests of the" >&2
echo "${TAB}process substitution mechanism fail, please do not consider" >&2
echo "${TAB}this a test failure" >&2
-echo "warning: if you have exported variables beginning with the string _Q," >&2
+echo "${CSTART}warning${CEND}: if you have exported variables beginning with the string _Q," >&2
echo "${TAB}the tests may generate diff output. If so, please do not consider" >&2
echo "${TAB}this a test failure" >&2
-echo "warning: several of these tests will fail if arrays have not" >&2
+echo "${CSTART}warning${CEND}: several of these tests will fail if arrays have not" >&2
echo "${TAB}been compiled into the shell." >&2
${THIS_SH} ./nquote1.tests 2>&1 | grep -v '^expect' > ${BASH_TSTOUT}
diff ${BASH_TSTOUT} nquote1.right && rm -f ${BASH_TSTOUT}
-echo "warning: several of these tests will fail if arrays have not" >&2
+echo "${CSTART}warning${CEND}: several of these tests will fail if arrays have not" >&2
echo "${TAB}been compiled into the shell." >&2
${THIS_SH} ./nquote2.tests 2>&1 | grep -v '^expect' > ${BASH_TSTOUT}
diff ${BASH_TSTOUT} nquote2.right && rm -f ${BASH_TSTOUT}
-echo "warning: several of these tests will fail if arrays have not" >&2
+echo "${CSTART}warning${CEND}: several of these tests will fail if arrays have not" >&2
echo "${TAB}been compiled into the shell." >&2
${THIS_SH} ./nquote3.tests 2>&1 | grep -v '^expect' > ${BASH_TSTOUT}
diff ${BASH_TSTOUT} nquote3.right && rm -f ${BASH_TSTOUT}
# See whether or not we can use `diff -a'
( diff -a ./nquote4.right ./nquote4.right >/dev/null 2>&1 ) && AFLAG=-a
-echo "warning: some of these tests will fail if you do not have UTF-8" >&2
+echo "${CSTART}warning${CEND}: some of these tests will fail if you do not have UTF-8" >&2
echo "${TAB}locales installed on your system" >&2
${THIS_SH} ./nquote4.tests > ${BASH_TSTOUT} 2>&1
-echo "warning: all of these tests will fail if process substitution has not" >&2
+echo "${CSTART}warning${CEND}: all of these tests will fail if process substitution has not" >&2
echo "${TAB}been compiled into the shell or if the OS does not provide" >&2
echo "${TAB}FIFOs or /dev/fd. Some tests may fail if the OS does not" >&2
echo "${TAB}provide FIFOs." >&2
-echo "warning: please do not consider output differing only in the amount of" >&2
+echo "${CSTART}warning${CEND}: please do not consider output differing only in the amount of" >&2
echo "${TAB}white space to be an error." >&2
${THIS_SH} ./read.tests > ${BASH_TSTOUT} 2>&1
diff ${BASH_TSTOUT} read.right && rm -f ${BASH_TSTOUT}
-echo "warning: the text of a system error message may vary between systems and" >&2
+echo "${CSTART}warning${CEND}: the text of a system error message may vary between systems and" >&2
echo "${TAB}produce diff output." >&2
-echo "warning: if the text of an error message concerning \`redir1.*' not being" >&2
+echo "${CSTART}warning${CEND}: if the text of an error message concerning \`redir1.*' not being" >&2
echo "${TAB}found or messages concerning bad file descriptors produce diff" >&2
echo "${TAB}output, please do not consider it a test failure" >&2
${THIS_SH} ./redir.tests > ${BASH_TSTOUT} 2>&1
-echo "warning: UNIX versions number signals and schedule processes differently." >&2
+echo "${CSTART}warning${CEND}: UNIX versions number signals and schedule processes differently." >&2
echo "${TAB}If output differing only in line numbers is produced, please" >&2
echo "${TAB}do not consider this a test failure." >&2
-echo "warning: some of these tests will fail if arrays have not" >&2
+echo "${CSTART}warning${CEND}: some of these tests will fail if arrays have not" >&2
echo "${TAB}been compiled into the shell" >&2
${THIS_SH} ./varenv.tests 2>&1 | grep -v '^expect' > ${BASH_TSTOUT}
diff ${BASH_TSTOUT} varenv.right && rm -f ${BASH_TSTOUT}
-echo "warning: the text of a system error message may vary between systems and" >&2
+echo "${CSTART}warning${CEND}: the text of a system error message may vary between systems and" >&2
echo "${TAB}produce diff output." >&2
${THIS_SH} ./vredir.tests > ${BASH_TSTOUT} 2>&1
diff ${BASH_TSTOUT} vredir.right && rm -f ${BASH_TSTOUT}
localewarn()
{
- echo "unicode1.sub: warning: you do not have the $1 locale installed;" >&2
- echo "unicode1.sub: that will cause some of these tests to be skipped." >&2
+ echo "${CSTART}unicode1.sub: warning${CEND}: you do not have the $1 locale installed;" >&2
+ echo "${TAB}that will cause some of these tests to be skipped." >&2
}
function TestCodePage {