From 407d9afca046256d664becb70fd85b948d6c3450 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Fri, 18 Nov 2022 11:01:00 -0500 Subject: [PATCH] fixes for bracket expressions in pathname expansion; changes for select/pselect; jobs builtin now removes terminated jobs from the jobs table after notifying --- CWRU/CWRU.chlog | 59 ++++++++++++++++++++++++++++++++++++++++++ bashline.c | 6 +++++ builtins/jobs.def | 7 +++++ lib/glob/sm_loop.c | 55 ++++++++++++++++++++++++++++++++------- lib/readline/display.c | 6 ++--- lib/readline/input.c | 6 ++--- lib/readline/rltty.c | 2 +- lib/sh/input_avail.c | 12 +++------ lib/sh/timers.c | 6 ++++- lib/sh/ufuncs.c | 5 ++-- test.c | 6 ++++- trap.c | 15 ++++++++--- 12 files changed, 153 insertions(+), 32 deletions(-) diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 12ce5eda..94ed6c46 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -4407,3 +4407,62 @@ parse.y - [grammar]: changed check to decrement WORD_TOP to >= 0 since we start at -1 and we want to decrement back to -1 when all loops are closed + +builtins/jobs.def + - jobs_builtin: call notify_and_cleanup after displaying the status of + jobs to implement POSIX requirement that `jobs' remove terminated + jobs from the jobs list + https://pubs.opengroup.org/onlinepubs/9699919799/utilities/jobs.html#tag_20_62 + + 11/15 + ----- +lib/sh/input_avail.c + - include signal.h unconditionally, we need it for HAVE_SELECT and + HAVE_PSELECT + - nchars_avail: make sure we declare and use readfds and exceptfds if + we have pselect or select available + +lib/readline/input.c + - rl_gather_tyi,rl_getc: need to declare and use readfds and exceptfds + if HAVE_PSELECT or HAVE_SELECT is set. Report from + Henry Bent , fixes from + Koichi Murase + + 11/16 + ----- +lib/sh/ufuncs.c + - quit.h: include unconditionally for declaration of sigemptyset even + if HAVE_SELECT is not defined + +lib/sh/timers.c + - USEC_PER_SEC: make sure it's defined even if HAVE_SELECT is not + +lib/glob/sm_loop.c + - BRACKMATCH: if an equivalence class does not match, and the next + character following the class is a `]', treat that as the end of + the bracket expression. + Report and fix from Koichi Murase + - GMATCH: if the current character in the string is a `/' and the + current element in the pattern is a bracket expresion, and the FLAGS + include FNM_PATHNAME, return FNM_NOMATCH immediately. A bracket + expression can never match a slash. + Report and fix from Koichi Murase + - BRACKMATCH: if we encounter a in a bracket expression, either + individually or as part of an equivalence class, nullify the bracket + expression and force the `[' to be matched as an ordinary + character + + 11/17 + ----- +lib/glob/sm_loop.c + - BRACKMATCH: if a slash character appears as the first character + after a non-matching character class or equivalence class, treat + the bracket as an ordinary character that must be matched literally + - BRACKMATCH: if a slash character appears as the second character + of a range expression, treat the bracket as an ordinary character + - BRACKMATCH: if a slash character appears in the portion of a + bracket expression that already matched, treat the bracket as an + ordinary character + Updates from Koichi Murase + - BRACKMATCH: if a range expression is incomplete (no end char), + treat the bracket as an ordinary character diff --git a/bashline.c b/bashline.c index 2eaeb72a..5660283c 100644 --- a/bashline.c +++ b/bashline.c @@ -1877,7 +1877,13 @@ bash_default_completion (text, start, end, qc, compflags) rl_completion_suppress_append = 1; rl_filename_completion_desired = 0; } +#if 0 + /* TAG:bash-5.3 jidanni@jidanni.org 11/11/2022 */ + else if (matches[0] && matches[1] && STREQ (matches[0], matches[1]) && + matches[2] && STREQ (matches[1], matches[2]) && CMD_IS_DIR (matches[0])) +#else else if (matches[0] && matches[1] && STREQ (matches[0], matches[1]) && CMD_IS_DIR (matches[0])) +#endif /* There are multiple instances of the same match (duplicate completions haven't yet been removed). In this case, all of the matches will be the same, and the duplicate removal code diff --git a/builtins/jobs.def b/builtins/jobs.def index 1ce098d0..e2f2ff79 100644 --- a/builtins/jobs.def +++ b/builtins/jobs.def @@ -136,6 +136,9 @@ jobs_builtin (list) { case JSTATE_ANY: list_all_jobs (form); + /* POSIX says to remove terminated jobs from the list after the jobs + builtin reports their status. */ + notify_and_cleanup (); /* the notify part will be a no-op */ break; case JSTATE_RUNNING: list_running_jobs (form); @@ -163,6 +166,10 @@ jobs_builtin (list) UNBLOCK_CHILD (oset); list = list->next; } + /* POSIX says to remove terminated jobs from the list after the jobs + builtin reports their status. */ + notify_and_cleanup (); + return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); } diff --git a/lib/glob/sm_loop.c b/lib/glob/sm_loop.c index 592a78db..fa350daa 100644 --- a/lib/glob/sm_loop.c +++ b/lib/glob/sm_loop.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991-2021 Free Software Foundation, Inc. +/* Copyright (C) 1991-2022 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -343,6 +343,11 @@ fprintf(stderr, "gmatch: pattern = %s; pe = %s\n", pattern, pe); (n == string || ((flags & FNM_PATHNAME) && n[-1] == L('/')))) return (FNM_NOMATCH); + /* If we are matching pathnames, we can't match a slash with a + bracket expression. */ + if (sc == L('/') && (flags & FNM_PATHNAME)) + return (FNM_NOMATCH); + /* `?' cannot match `.' or `..' if it is the first character of the string or if it is the first character following a slash and we are matching a pathname. */ @@ -403,6 +408,8 @@ PARSE_COLLSYM (p, vp) return (p + pc + 2); } +#define SLASH_PATHNAME(c) (c == L('/') && (flags & FNM_PATHNAME)) + /* Use prototype definition here because of type promotion. */ static CHAR * #if defined (PROTOTYPES) @@ -451,6 +458,12 @@ BRACKMATCH (p, test, flags) { pc = FOLD (p[1]); p += 4; + + /* Finding a slash in a bracket expression means you have to + match the bracket as an ordinary character (see below). */ + if (pc == L('/') && (flags & FNM_PATHNAME)) + return ((test == L('[')) ? savep : (CHAR *)0); /*]*/ + if (COLLEQUIV (test, pc)) { /*[*/ /* Move past the closing `]', since the first thing we do at @@ -463,6 +476,10 @@ BRACKMATCH (p, test, flags) 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; } @@ -475,11 +492,11 @@ BRACKMATCH (p, test, flags) pc = 0; /* make sure invalid char classes don't match. */ /* Find end of character class name */ - for (close = p + 1; *close != '\0'; close++) + for (close = p + 1; *close != '\0' && SLASH_PATHNAME(*close) == 0; close++) if (*close == L(':') && *(close+1) == L(']')) break; - if (*close != L('\0')) + if (*close != L('\0') && SLASH_PATHNAME(*close) == 0) { ccname = (CHAR *)malloc ((close - p) * sizeof (CHAR)); if (ccname == 0) @@ -526,6 +543,8 @@ BRACKMATCH (p, test, flags) 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); @@ -565,15 +584,23 @@ BRACKMATCH (p, test, flags) if (c == L('\0')) return ((test == L('[')) ? savep : (CHAR *)0); + /* POSIX.2 2.13.3 says: `If a character is found following an + unescaped character before a corresponding + 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); if (c == L('\0')) return ((test == L('[')) ? savep : (CHAR *)0); - - if ((flags & FNM_PATHNAME) && c == L('/')) - /* [/] can never match when matching a pathname. */ - return (CHAR *)0; + else if (c == L('/') && (flags & FNM_PATHNAME)) + return ((test == L('[')) ? savep : (CHAR *)0); /* This introduces a range, unless the `-' is the last character of the class. Find the end of the range @@ -584,7 +611,9 @@ BRACKMATCH (p, test, flags) if (!(flags & FNM_NOESCAPE) && cend == L('\\')) cend = *p++; if (cend == L('\0')) - return (CHAR *)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('.')) { p = PARSE_COLLSYM (p, &pc); @@ -636,6 +665,8 @@ matched: /* 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); oc = c; c = *p++; @@ -643,7 +674,10 @@ matched: { brcnt++; brchrp = p++; /* skip over the char after the left bracket */ - if ((c = *p) == L('\0')) + 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); /* If *brchrp == ':' we should check that the rest of the characters form a valid character class name. We don't do that yet, but we @@ -666,6 +700,9 @@ matched: { if (*p == '\0') return (CHAR *)0; + /* We don't allow backslash to quote slash if we're matching pathnames */ + else if (*p == L('/') && (flags & FNM_PATHNAME)) + return ((test == L('[')) ? savep : (CHAR *)0); /* XXX 1003.2d11 is unclear if this is right. */ ++p; } diff --git a/lib/readline/display.c b/lib/readline/display.c index 09ecd864..d77cc683 100644 --- a/lib/readline/display.c +++ b/lib/readline/display.c @@ -616,10 +616,8 @@ rl_expand_prompt (char *prompt) prompt_visible_length = prompt_physical_chars = 0; if (local_prompt_invis_chars == 0) - { - local_prompt_invis_chars = (int *)xmalloc (sizeof (int)); - local_prompt_invis_chars[0] = 0; - } + local_prompt_invis_chars = (int *)xmalloc (sizeof (int)); + local_prompt_invis_chars[0] = 0; if (prompt == 0 || *prompt == 0) return (0); diff --git a/lib/readline/input.c b/lib/readline/input.c index da4da455..9118fed3 100644 --- a/lib/readline/input.c +++ b/lib/readline/input.c @@ -1,6 +1,6 @@ /* input.c -- character input functions for readline. */ -/* Copyright (C) 1994-2021 Free Software Foundation, Inc. +/* Copyright (C) 1994-2022 Free Software Foundation, Inc. This file is part of the GNU Readline Library (Readline), a library for reading lines of text with interactive input and history editing. @@ -250,7 +250,7 @@ rl_gather_tyi (void) register int tem, result; int chars_avail, k; char input; -#if defined(HAVE_SELECT) +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) fd_set readfds, exceptfds; struct timeval timeout; #endif @@ -807,7 +807,7 @@ rl_getc (FILE *stream) int result; unsigned char c; int fd; -#if defined (HAVE_PSELECT) +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) sigset_t empty_set; fd_set readfds; #endif diff --git a/lib/readline/rltty.c b/lib/readline/rltty.c index 882a3d46..d9b0cd1c 100644 --- a/lib/readline/rltty.c +++ b/lib/readline/rltty.c @@ -729,7 +729,7 @@ rl_tty_set_echoing (int u) _rl_echoing_p = u; return o; } - + /* **************************************************************** */ /* */ /* Bogus Flow Control */ diff --git a/lib/sh/input_avail.c b/lib/sh/input_avail.c index 2ac44616..262bc749 100644 --- a/lib/sh/input_avail.c +++ b/lib/sh/input_avail.c @@ -1,7 +1,7 @@ /* input_avail.c -- check whether or not data is available for reading on a specified file descriptor. */ -/* Copyright (C) 2008,2009-2019 Free Software Foundation, Inc. +/* Copyright (C) 2008,2009-2019,2022 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -33,9 +33,7 @@ # include #endif /* HAVE_SYS_FILE_H */ -#if defined (HAVE_PSELECT) -# include -#endif +#include #if defined (HAVE_UNISTD_H) # include @@ -107,10 +105,8 @@ nchars_avail (fd, nchars) int nchars; { int result, chars_avail; -#if defined(HAVE_SELECT) - fd_set readfds, exceptfds; -#endif #if defined (HAVE_PSELECT) || defined (HAVE_SELECT) + fd_set readfds, exceptfds; sigset_t set, oset; #endif @@ -121,7 +117,7 @@ nchars_avail (fd, nchars) chars_avail = 0; -#if defined (HAVE_SELECT) +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) FD_ZERO (&readfds); FD_ZERO (&exceptfds); FD_SET (fd, &readfds); diff --git a/lib/sh/timers.c b/lib/sh/timers.c index 69b754c9..f04af8bd 100644 --- a/lib/sh/timers.c +++ b/lib/sh/timers.c @@ -1,6 +1,6 @@ /* timers - functions to manage shell timers */ -/* Copyright (C) 2021 Free Software Foundation, Inc. +/* Copyright (C) 2021,2022 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -47,6 +47,10 @@ extern int errno; #define FREE(s) do { if (s) free (s); } while (0) #endif +#ifndef USEC_PER_SEC +# define USEC_PER_SEC 1000000 +#endif + extern unsigned int falarm (unsigned int, unsigned int); static void shtimer_zero (sh_timer *); diff --git a/lib/sh/ufuncs.c b/lib/sh/ufuncs.c index 4dc4853b..03243918 100644 --- a/lib/sh/ufuncs.c +++ b/lib/sh/ufuncs.c @@ -1,6 +1,6 @@ /* ufuncs - sleep and alarm functions that understand fractional values */ -/* Copyright (C) 2008,2009-2020 Free Software Foundation, Inc. +/* Copyright (C) 2008,2009-2020,2022 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -33,9 +33,10 @@ extern int errno; #endif /* !errno */ +#include "quit.h" + #if defined (HAVE_SELECT) # include "posixselect.h" -# include "quit.h" # include "trap.h" # include "stat-time.h" #endif diff --git a/test.c b/test.c index 6e016e36..fadc33d2 100644 --- a/test.c +++ b/test.c @@ -397,7 +397,7 @@ binary_test (op, arg1, arg2, flags) else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0') { #if defined (HAVE_STRCOLL) - if (shell_compatibility_level > 40 && flags & TEST_LOCALE) + if (shell_compatibility_level > 40 && (flags & TEST_LOCALE)) return ((op[0] == '>') ? (strcoll (arg1, arg2) > 0) : (strcoll (arg1, arg2) < 0)); else #endif @@ -450,7 +450,11 @@ binary_operator () ((w[0] == '>' || w[0] == '<') && w[1] == '\0') || /* <, > */ (w[0] == '!' && w[1] == '=' && w[2] == '\0')) /* != */ { +#if 0 /* TAG: bash-5.3 POSIX interp 375 11/9/2022 */ + value = binary_test (w, argv[pos], argv[pos + 2], (posixly_correct ? TEST_LOCALE : 0)); +#else value = binary_test (w, argv[pos], argv[pos + 2], 0); +#endif pos += 3; return (value); } diff --git a/trap.c b/trap.c index 9cedf7eb..8d94dcd3 100644 --- a/trap.c +++ b/trap.c @@ -308,6 +308,7 @@ run_pending_traps () sh_parser_state_t pstate; volatile int save_return_catch_flag, function_code; procenv_t save_return_catch; + char *trap_command, *old_trap; #if defined (ARRAY_VARS) ARRAY *ps; #endif @@ -425,6 +426,10 @@ run_pending_traps () } else { + /* XXX - why not set SIG_INPROGRESS, clear SIG_CHANGED here? */ + old_trap = trap_list[sig]; + trap_command = savestring (old_trap); + save_parser_state (&pstate); save_subst_varlist = subst_assign_varlist; subst_assign_varlist = 0; @@ -447,7 +452,8 @@ run_pending_traps () } if (function_code == 0) - x = parse_and_execute (savestring (trap_list[sig]), "trap", SEVAL_NONINT|SEVAL_NOHIST|SEVAL_RESETLINE); + /* XXX is x always last_command_exit_value? */ + x = parse_and_execute (trap_command, "trap", SEVAL_NONINT|SEVAL_NOHIST|SEVAL_RESETLINE); else { parse_and_execute_cleanup (sig + 1); /* XXX - could use -1 */ @@ -1068,7 +1074,7 @@ _run_trap_internal (sig, tag) old_modes = old_running = old_context = -1; - trap_exit_value = function_code = 0; + trap_exit_value = 0; trap_saved_exit_value = last_command_exit_value; /* Run the trap only if SIG is trapped and not ignored, and we are not currently executing in the trap handler. */ @@ -1112,7 +1118,11 @@ _run_trap_internal (sig, tag) save_pipeline (1); /* XXX only provides one save level */ #endif + /* XXX - set pending_traps[sig] = 0 here? */ + evalnest++; + /* If we're in a function, make sure return longjmps come here, too. */ + function_code = 0; save_return_catch_flag = return_catch_flag; if (return_catch_flag) { @@ -1123,7 +1133,6 @@ _run_trap_internal (sig, tag) flags = SEVAL_NONINT|SEVAL_NOHIST; if (sig != DEBUG_TRAP && sig != RETURN_TRAP && sig != ERROR_TRAP) flags |= SEVAL_RESETLINE; - evalnest++; if (function_code == 0) { parse_and_execute (trap_command, tag, flags);