From c4a2e37470e223c171f9dcf70d353d0e1eab1ea9 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Mon, 19 Dec 2022 09:38:15 -0500 Subject: [PATCH] fix comsub inside parameter expansion in here-document; fix readline longjmp botch; posix change for unset non-identifier; posix change for double quotes inside backquote comsub in here-document; add missing posix pieces to umask builtin --- CWRU/CWRU.chlog | 78 +++++++++++++++++++++ MANIFEST | 1 + builtins/common.c | 2 +- builtins/set.def | 15 +++-- builtins/umask.def | 138 ++++++++++++++++++++++++++++---------- doc/bashref.texi | 13 ++++ execute_cmd.c | 3 + lib/readline/display.c | 1 - lib/readline/histexpand.c | 16 ++--- lib/readline/mbutil.c | 24 +++---- lib/readline/readline.c | 3 +- lib/readline/rlmbutil.h | 14 ++-- parse.y | 5 -- patchlevel.h | 2 +- shell.h | 1 + subst.c | 12 ++-- subst.h | 2 +- tests/builtins.right | 15 ++++- tests/builtins.tests | 5 +- tests/builtins8.sub | 54 +++++++++++++++ tests/comsub.tests | 5 ++ tests/cond.right | 1 + tests/errors.right | 9 ++- tests/errors.tests | 13 +++- trap.c | 26 +++---- trap.h | 2 +- 26 files changed, 360 insertions(+), 100 deletions(-) create mode 100644 tests/builtins8.sub diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index f461ab35..c59922f7 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -4667,3 +4667,81 @@ subst.c - parameter_brace_expand_length: handle namerefs with values that are valid length expansion expressions but invalid identifiers. From ed7-aspire4925@hotmail.com via https://savannah.gnu.org/support/?110799 + + 12/13 + ----- +subst.c + - extract_heredoc_dolbrace_string: fix off-by-one error after calling + extract_command_subst and extract_process_subst that caused it to + copy one too many parsed characters. Fix for bug reported by + Norbert Lange + + 12/14 + ----- +execute_cmd.c + - execute_cond_node: if a regular expression fails to compile, print + an error message. Report from Emanuele Torre + back on 6/15/2022 + +trap.c + - trap_variable_context -> trap_return_context, initialize from + funcnest + sourcenest instead of variable_context so we handle + shell function execution and `./source', both of which can use + `return'. Idea from Koichi Murase + +builtins/common.c + - get_exitstat: compare trap_return_context against funcnest+sourcenest, + since that's how it's initialized now + +lib/readline/readline.c + - readline_internal_charloop: if we're not using the callback interface, + don't restore _rl_top_level from olevel, since we will just be going + around the loop again and will potentially need to use it multiple + times. Report from Emanuele Torre + + 12/15 + ----- +shell.h + - EX_UTILERROR: new generic special builtin return status to indicate a + POSIX utility error that should cause a non-interactive shell to abort + +execute_cmd.c + - builtin_status: translate EX_UTILERROR to EXECUTION_FAILURE + +builtins/set.def + - unset_builtin: return EX_UTILERROR if posix_utility_error is set; + set it when trying to unset a non-identifier (variable) or a + non-unsettable or readonly variable + + 12/16 + ----- +subst.c + - de_backslash: now takes a second argument with the current quoting + flags + - de_backslash: if the quoting flags include Q_HERE_DOCUMENT and the + shell is in posix mode, remove backslashes quoting double quotes + +subst.h + - de_backslash: update extern declaration + +lib/readline/histexpand.c + - history_expand_internal,get_history_word_specifier,get_subst_pattern, + hist_error,history_find_word,hist_string_extract_single_quoted: + now take const char * string arguments + +lib/readline/mbutil.c + - _rl_get_char_len,_rl_adjust_point,_rl_find_next_mbchar_internal, + _rl_find_next_mbchar,_rl_find_prev_mbchar,_rl_find_prev_mbchar_internal, + _rl_test_nonzero,_rl_find_prev_utf8char,_rl_is_mbchar_matched, + _rl_compare_chars,_rl_char_value: take const char * string arguments + + 12/17 + ----- +builtins/umask.def + - parse_symbolic_umask: add missing POSIX pieces: + o `action' of ugo, meaning to copy portions of initial mask + o multiple `op' specs as part of the action string (`u=r-w') + (resets perm) + o missing perm characters Xst in action string + o default `who' equivalent to `a' instead of fixing up later + diff --git a/MANIFEST b/MANIFEST index 7518f494..4d735f66 100644 --- a/MANIFEST +++ b/MANIFEST @@ -978,6 +978,7 @@ tests/builtins4.sub f tests/builtins5.sub f tests/builtins6.sub f tests/builtins7.sub f +tests/builtins8.sub f tests/source1.sub f tests/source2.sub f tests/source3.sub f diff --git a/builtins/common.c b/builtins/common.c index b9705c02..954eeabe 100644 --- a/builtins/common.c +++ b/builtins/common.c @@ -573,7 +573,7 @@ get_exitstat (list) trap gets to change $?, though, since that is part of its reason for existing, and because the extended debug mode does things with the return value. */ - if (this_shell_builtin == return_builtin && running_trap > 0 && running_trap != DEBUG_TRAP+1 && variable_context == trap_variable_context) + if (this_shell_builtin == return_builtin && running_trap > 0 && running_trap != DEBUG_TRAP+1 && trap_return_context == funcnest + sourcenest) return (trap_saved_exit_value); return (last_command_exit_value); } diff --git a/builtins/set.def b/builtins/set.def index 44f17691..4a7c517e 100644 --- a/builtins/set.def +++ b/builtins/set.def @@ -836,9 +836,11 @@ unset_builtin (list) { int unset_function, unset_variable, unset_array, opt, nameref, any_failed; int global_unset_func, global_unset_var, vflags, base_vflags, valid_id; + int posix_utility_error; char *name, *tname; - unset_function = unset_variable = unset_array = nameref = any_failed = 0; + unset_function = unset_variable = unset_array = nameref = 0; + posix_utility_error = any_failed = 0; global_unset_func = global_unset_var = 0; reset_internal_getopt (); @@ -918,6 +920,7 @@ unset_builtin (list) if (unset_function == 0 && valid_id == 0) { sh_invalidid (name); + posix_utility_error++; NEXT_VARIABLE (); } @@ -930,6 +933,7 @@ unset_builtin (list) if (var && unset_function == 0 && non_unsettable_p (var)) { builtin_error (_("%s: cannot unset"), name); + posix_utility_error++; NEXT_VARIABLE (); } @@ -952,6 +956,7 @@ unset_builtin (list) { builtin_error (_("%s: cannot unset: readonly %s"), var->name, unset_function ? "function" : "variable"); + posix_utility_error++; NEXT_VARIABLE (); } @@ -1008,8 +1013,10 @@ unset_builtin (list) /* This is what Posix.2 says: ``If neither -f nor -v is specified, the name refers to a variable; if a variable by - that name does not exist, a function by that name, if any, - shall be unset.'' */ + that name does not exist, it is unspecified whether a function + by that name, if any, shall be unset.'' The unspecified part is a + recent addition, so we continue to try to unset a shell function if + we don't find a variable named NAME. */ if (tem == -1 && nameref == 0 && unset_function == 0 && unset_variable == 0) tem = unbind_func (name); @@ -1024,5 +1031,5 @@ unset_builtin (list) list = list->next; } - return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); + return (any_failed ? (posix_utility_error ? EX_UTILERROR : EXECUTION_FAILURE) : EXECUTION_SUCCESS); } diff --git a/builtins/umask.def b/builtins/umask.def index 8041d56b..2a6ae405 100644 --- a/builtins/umask.def +++ b/builtins/umask.def @@ -1,7 +1,7 @@ This file is umask.def, from which is created umask.c. It implements the builtin "umask" in Bash. -Copyright (C) 1987-2020 Free Software Foundation, Inc. +Copyright (C) 1987-2022 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -186,17 +186,46 @@ print_symbolic_umask (um) printf ("u=%s,g=%s,o=%s\n", ubits, gbits, obits); } +static inline mode_t +copyuser (mask) + mode_t mask; +{ + return ((mask & S_IRUSR) ? S_IRUGO : 0) | + ((mask & S_IWUSR) ? S_IWUGO : 0) | + ((mask & S_IXUSR) ? S_IXUGO : 0); +} + +static inline mode_t +copygroup (mask) + mode_t mask; +{ + return ((mask & S_IRGRP) ? S_IRUGO : 0) | + ((mask & S_IWGRP) ? S_IWUGO : 0) | + ((mask & S_IXGRP) ? S_IXUGO : 0); +} + +static inline mode_t +copyother (mask) + mode_t mask; +{ + return ((mask & S_IROTH) ? S_IRUGO : 0) | + ((mask & S_IWOTH) ? S_IWUGO : 0) | + ((mask & S_IXOTH) ? S_IXUGO : 0); +} + int parse_symbolic_mode (mode, initial_bits) char *mode; - int initial_bits; + mode_t initial_bits; { - int who, op, perm, bits, c; + char op, c; + mode_t who, perm, bits; char *s; for (s = mode, bits = initial_bits;;) { - who = op = perm = 0; + who = 0; + op = 0; /* Parse the `who' portion of the symbolic mode clause. */ while (member (*s, "agou")) @@ -220,7 +249,13 @@ parse_symbolic_mode (mode, initial_bits) } } + /* default `who' is `a' */ + if (who == 0) + who = S_IRWXU | S_IRWXG | S_IRWXO; + /* The operation is now sitting in *s. */ +start_op: + perm = 0; op = *s++; switch (op) { @@ -233,57 +268,87 @@ parse_symbolic_mode (mode, initial_bits) return (-1); } - /* Parse out the `perm' section of the symbolic mode clause. */ - while (member (*s, "rwx")) + /* Parse out the `action' section of the symbolic mode clause. An + action can be a set of permissions (rwxXst), a copy specification + (ugo), or another op (+-=). */ + while (member (*s, "rwxXstugo")) { - c = *s++; - - switch (c) + switch (*s) { + /* First the copy specification */ + case 'u': + perm = copyuser (initial_bits); + break; + case 'g': + perm = copygroup (initial_bits); + break; + case 'o': + perm = copyother (initial_bits); + break; + + /* Then the permissions. */ case 'r': perm |= S_IRUGO; break; case 'w': perm |= S_IWUGO; break; + case 'X': + /* for chmod, this includes S_ISDIR but that doesn't make sense here */ + if ((initial_bits & S_IXUGO) == 0) + break; /* no-op if original mask doesn't include execute bits */ + /* FALLTHROUGH */ case 'x': perm |= S_IXUGO; break; + case 's': +#ifdef S_ISUID + perm |= S_ISUID | S_ISGID; + break; +#else + goto spec_error; +#endif + case 't': +#ifdef S_ISVTX + perm |= S_ISVTX; + break; +#else + goto spec_error; +#endif } + s++; } /* Now perform the operation or return an error for a bad permission string. */ - if (!*s || *s == ',') + perm &= who; + + switch (op) { - if (who) - perm &= who; - - switch (op) - { - case '+': - bits |= perm; - break; - case '-': - bits &= ~perm; - break; - case '=': - if (who == 0) - who = S_IRWXU | S_IRWXG | S_IRWXO; - bits &= ~who; - bits |= perm; - break; - - /* No other values are possible. */ - } - - if (*s == '\0') - break; - else - s++; /* skip past ',' */ + case '+': + bits |= perm; + break; + case '-': + bits &= ~perm; + break; + case '=': + bits &= ~who; + bits |= perm; + break; } + + /* Break if the end of the action string, loop if we're going to parse + another `who', go back to parsing another op if we have an op spec + (+-=). Return an invalid mode character error for everything else. */ + if (*s == '\0') + break; + else if (*s == ',') + s++; /* skip past ',' */ + else if (*s == '+' || *s == '-' || *s == '=') + goto start_op; else { +spec_error: builtin_error (_("`%c': invalid symbolic mode character"), *s); return (-1); } @@ -299,7 +364,8 @@ static int symbolic_umask (list) WORD_LIST *list; { - int um, bits; + mode_t um; + int bits; /* Get the initial umask. Don't change it yet. */ um = umask (022); diff --git a/doc/bashref.texi b/doc/bashref.texi index 772903b8..f5872b6b 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -8248,6 +8248,12 @@ the @sc{posix} standard, and include things like passing incorrect options, redirection errors, variable assignment errors for assignments preceding the command name, and so on. +@item +The @code{unset} builtin with the @option{-v} option specified returns a +fatal error if it attempts to unset a @code{readonly} or @code{non-unsettable} +variable, or encounters a variable name argument that is an invalid identifier, +which causes a non-interactive shell to exit. + @item A non-interactive shell exits with an error status if a variable assignment error occurs when no command name follows the assignment @@ -8434,6 +8440,13 @@ arguments corresponding to floating point conversion specifiers, instead of Bash removes an exited background process's status from the list of such statuses after the @code{wait} builtin is used to obtain it. +@item +A double quote character (@samp{"}) is treated specially when it appears +in a backquoted command substitution in the body of a here-document that +undergoes expansion. +That means, for example, that a backslash preceding a double quote +character will escape it and the backslash will be removed. + @end enumerate There is other @sc{posix} behavior that Bash does not implement by diff --git a/execute_cmd.c b/execute_cmd.c index 994c9229..f9c86736 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -4014,6 +4014,8 @@ execute_cond_node (cond) #endif result = sh_regmatch (arg1, arg2, mflags); + if (result == 2) + builtin_error (_("invalid regular expression `%s'"), arg2); } else #endif /* COND_REGEXP */ @@ -4869,6 +4871,7 @@ builtin_status (result) case EX_REDIRFAIL: case EX_BADASSIGN: case EX_EXPFAIL: + case EX_UTILERROR: r = EXECUTION_FAILURE; break; default: diff --git a/lib/readline/display.c b/lib/readline/display.c index 2d3747f2..02b5e5cc 100644 --- a/lib/readline/display.c +++ b/lib/readline/display.c @@ -2732,7 +2732,6 @@ int rl_forced_update_display (void) { register char *temp; - register int tlen; if (visible_line) memset (visible_line, 0, line_size); diff --git a/lib/readline/histexpand.c b/lib/readline/histexpand.c index 8ab68091..35fe3fe7 100644 --- a/lib/readline/histexpand.c +++ b/lib/readline/histexpand.c @@ -70,12 +70,12 @@ static int subst_rhs_len; specifications from word designators. Static for now */ static char *history_event_delimiter_chars = HISTORY_EVENT_DELIMITERS; -static char *get_history_word_specifier (char *, char *, int *); +static char *get_history_word_specifier (const char *, char *, int *); static int history_tokenize_word (const char *, int); static char **history_tokenize_internal (const char *, int, int *); static char *history_substring (const char *, int, int); static void freewords (char **, int); -static char *history_find_word (char *, int); +static char *history_find_word (const char *, int); static char *quote_breaks (char *); @@ -319,7 +319,7 @@ get_history_event (const char *string, int *caller_index, int delimiting_quote) to the closing single quote. FLAGS currently used to allow backslash to escape a single quote (e.g., for bash $'...'). */ static void -hist_string_extract_single_quoted (char *string, int *sindex, int flags) +hist_string_extract_single_quoted (const char *string, int *sindex, int flags) { register int i; @@ -374,7 +374,7 @@ quote_breaks (char *s) } static char * -hist_error(char *s, int start, int current, int errtype) +hist_error(const char *s, int start, int current, int errtype) { char *temp; const char *emsg; @@ -434,7 +434,7 @@ hist_error(char *s, int start, int current, int errtype) subst_rhs is allowed to be set to the empty string. */ static char * -get_subst_pattern (char *str, int *iptr, int delimiter, int is_rhs, int *lenptr) +get_subst_pattern (const char *str, int *iptr, int delimiter, int is_rhs, int *lenptr) { register int si, i, j, k; char *s; @@ -527,7 +527,7 @@ postproc_subst_rhs (void) *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */ /* need current line for !# */ static int -history_expand_internal (char *string, int start, int qc, int *end_index_ptr, char **ret_string, char *current_line) +history_expand_internal (const char *string, int start, int qc, int *end_index_ptr, char **ret_string, char *current_line) { int i, n, starting_index; int substitute_globally, subst_bywords, want_quotes, print_only; @@ -1303,7 +1303,7 @@ history_expand (char *hstring, char **output) CALLER_INDEX is the offset in SPEC to start looking; it is updated to point to just after the last character parsed. */ static char * -get_history_word_specifier (char *spec, char *from, int *caller_index) +get_history_word_specifier (const char *spec, char *from, int *caller_index) { register int i = *caller_index; int first, last; @@ -1696,7 +1696,7 @@ freewords (char **words, int start) in the history line LINE. Used to save the word matched by the last history !?string? search. */ static char * -history_find_word (char *line, int ind) +history_find_word (const char *line, int ind) { char **words, *s; int i, wind; diff --git a/lib/readline/mbutil.c b/lib/readline/mbutil.c index 47e9100a..32228bbf 100644 --- a/lib/readline/mbutil.c +++ b/lib/readline/mbutil.c @@ -148,7 +148,7 @@ _rl_utf8_mblen (const char *s, size_t n) } static int -_rl_find_next_mbchar_internal (char *string, int seed, int count, int find_non_zero) +_rl_find_next_mbchar_internal (const char *string, int seed, int count, int find_non_zero) { size_t tmp, len; mbstate_t ps; @@ -228,7 +228,7 @@ _rl_find_next_mbchar_internal (char *string, int seed, int count, int find_non_z } static inline int -_rl_test_nonzero (char *string, int ind, int len) +_rl_test_nonzero (const char *string, int ind, int len) { size_t tmp; WCHAR_T wc; @@ -242,7 +242,7 @@ _rl_test_nonzero (char *string, int ind, int len) /* experimental -- needs to handle zero-width characters better */ static int -_rl_find_prev_utf8char (char *string, int seed, int find_non_zero) +_rl_find_prev_utf8char (const char *string, int seed, int find_non_zero) { char *s; unsigned char b; @@ -288,7 +288,7 @@ _rl_find_prev_utf8char (char *string, int seed, int find_non_zero) } /*static*/ int -_rl_find_prev_mbchar_internal (char *string, int seed, int find_non_zero) +_rl_find_prev_mbchar_internal (const char *string, int seed, int find_non_zero) { mbstate_t ps; int prev, non_zero_prev, point, length; @@ -356,7 +356,7 @@ _rl_find_prev_mbchar_internal (char *string, int seed, int find_non_zero) if an invalid multibyte sequence was encountered. It returns (size_t)(-2) if it couldn't parse a complete multibyte character. */ int -_rl_get_char_len (char *src, mbstate_t *ps) +_rl_get_char_len (const char *src, mbstate_t *ps) { size_t tmp, l; int mb_cur_max; @@ -368,7 +368,7 @@ _rl_get_char_len (char *src, mbstate_t *ps) else { mb_cur_max = MB_CUR_MAX; - tmp = mbrlen((const char *)src, (l < mb_cur_max) ? l : mb_cur_max, ps); + tmp = mbrlen(src, (l < mb_cur_max) ? l : mb_cur_max, ps); } if (tmp == (size_t)(-2)) { @@ -394,7 +394,7 @@ _rl_get_char_len (char *src, mbstate_t *ps) /* compare the specified two characters. If the characters matched, return 1. Otherwise return 0. */ int -_rl_compare_chars (char *buf1, int pos1, mbstate_t *ps1, char *buf2, int pos2, mbstate_t *ps2) +_rl_compare_chars (const char *buf1, int pos1, mbstate_t *ps1, const char *buf2, int pos2, mbstate_t *ps2) { int i, w1, w2; @@ -417,7 +417,7 @@ _rl_compare_chars (char *buf1, int pos1, mbstate_t *ps1, char *buf2, int pos2, m if point is invalid (point < 0 || more than string length), it returns -1 */ int -_rl_adjust_point (char *string, int point, mbstate_t *ps) +_rl_adjust_point (const char *string, int point, mbstate_t *ps) { size_t tmp; int length, pos; @@ -457,7 +457,7 @@ _rl_adjust_point (char *string, int point, mbstate_t *ps) } int -_rl_is_mbchar_matched (char *string, int seed, int end, char *mbchar, int length) +_rl_is_mbchar_matched (const char *string, int seed, int end, char *mbchar, int length) { int i; @@ -471,7 +471,7 @@ _rl_is_mbchar_matched (char *string, int seed, int end, char *mbchar, int length } WCHAR_T -_rl_char_value (char *buf, int ind) +_rl_char_value (const char *buf, int ind) { size_t tmp; WCHAR_T wc; @@ -500,7 +500,7 @@ _rl_char_value (char *buf, int ind) characters. */ #undef _rl_find_next_mbchar int -_rl_find_next_mbchar (char *string, int seed, int count, int flags) +_rl_find_next_mbchar (const char *string, int seed, int count, int flags) { #if defined (HANDLE_MULTIBYTE) return _rl_find_next_mbchar_internal (string, seed, count, flags); @@ -514,7 +514,7 @@ _rl_find_next_mbchar (char *string, int seed, int count, int flags) we look for non-zero-width multibyte characters. */ #undef _rl_find_prev_mbchar int -_rl_find_prev_mbchar (char *string, int seed, int flags) +_rl_find_prev_mbchar (const char *string, int seed, int flags) { #if defined (HANDLE_MULTIBYTE) return _rl_find_prev_mbchar_internal (string, seed, flags); diff --git a/lib/readline/readline.c b/lib/readline/readline.c index 6f538454..bfdb4205 100644 --- a/lib/readline/readline.c +++ b/lib/readline/readline.c @@ -590,7 +590,8 @@ readline_internal_charloop (void) { (*rl_redisplay_function) (); _rl_want_redisplay = 0; - memcpy ((void *)_rl_top_level, (void *)olevel, sizeof (procenv_t)); + if (RL_ISSTATE (RL_STATE_CALLBACK)) + memcpy ((void *)_rl_top_level, (void *)olevel, sizeof (procenv_t)); /* If we longjmped because of a timeout, handle it here. */ if (RL_ISSTATE (RL_STATE_TIMEOUT)) diff --git a/lib/readline/rlmbutil.h b/lib/readline/rlmbutil.h index d9060572..42d47c3d 100644 --- a/lib/readline/rlmbutil.h +++ b/lib/readline/rlmbutil.h @@ -104,21 +104,21 @@ #define MB_FIND_ANY 0x00 #define MB_FIND_NONZERO 0x01 -extern int _rl_find_prev_mbchar (char *, int, int); -extern int _rl_find_next_mbchar (char *, int, int, int); +extern int _rl_find_prev_mbchar (const char *, int, int); +extern int _rl_find_next_mbchar (const char *, int, int, int); #ifdef HANDLE_MULTIBYTE -extern int _rl_compare_chars (char *, int, mbstate_t *, char *, int, mbstate_t *); -extern int _rl_get_char_len (char *, mbstate_t *); -extern int _rl_adjust_point (char *, int, mbstate_t *); +extern int _rl_compare_chars (const char *, int, mbstate_t *, const char *, int, mbstate_t *); +extern int _rl_get_char_len (const char *, mbstate_t *); +extern int _rl_adjust_point (const char *, int, mbstate_t *); extern int _rl_read_mbchar (char *, int); extern int _rl_read_mbstring (int, char *, int); -extern int _rl_is_mbchar_matched (char *, int, int, char *, int); +extern int _rl_is_mbchar_matched (const char *, int, int, char *, int); -extern WCHAR_T _rl_char_value (char *, int); +extern WCHAR_T _rl_char_value (const char *, int); extern int _rl_walphabetic (WCHAR_T); #define _rl_to_wupper(wc) (iswlower (wc) ? towupper (wc) : (wc)) diff --git a/parse.y b/parse.y index cdba786c..cad4d596 100644 --- a/parse.y +++ b/parse.y @@ -3072,12 +3072,7 @@ alias_expand_token (tokstr) char *expanded; alias_t *ap; -#if 0 - if (((parser_state & PST_ALEXPNEXT) || command_token_position (last_read_token)) && - (parser_state & PST_CASEPAT) == 0) -#else if ((parser_state & PST_ALEXPNEXT) || assignment_acceptable (last_read_token)) -#endif { ap = find_alias (tokstr); diff --git a/patchlevel.h b/patchlevel.h index 91de6238..31aaded1 100644 --- a/patchlevel.h +++ b/patchlevel.h @@ -25,6 +25,6 @@ regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh looks for to find the patch level (for the sccs version string). */ -#define PATCHLEVEL 12 +#define PATCHLEVEL 15 #endif /* _PATCHLEVEL_H_ */ diff --git a/shell.h b/shell.h index 6e44bca6..15235076 100644 --- a/shell.h +++ b/shell.h @@ -73,6 +73,7 @@ extern int EOF_Reached; #define EX_BADASSIGN 260 /* variable assignment error */ #define EX_EXPFAIL 261 /* word expansion failed */ #define EX_DISKFALLBACK 262 /* fall back to disk command from builtin */ +#define EX_UTILERROR 263 /* Posix special builtin utility error */ /* Flag values that control parameter pattern substitution. */ #define MATCH_ANY 0x000 diff --git a/subst.c b/subst.c index 0907a5d0..59fc9040 100644 --- a/subst.c +++ b/subst.c @@ -85,7 +85,6 @@ extern int errno; #define VT_STARSUB 128 /* $* or ${array[*]} -- used to split */ - /* Flags for quoted_strchr */ #define ST_BACKSL 0x01 #define ST_CTLESC 0x02 @@ -1699,7 +1698,7 @@ extract_heredoc_dolbrace_string (string, sindex, quoted, flags) t = extract_command_subst (string, &si, flags); CHECK_STRING_OVERRUN (i, si, slen, c); - tlen = si - i - 1; + tlen = si - i - 2; RESIZE_MALLOCED_BUFFER (result, result_index, tlen + 4, result_size, 64); result[result_index++] = c; result[result_index++] = LPAREN; @@ -1719,7 +1718,7 @@ extract_heredoc_dolbrace_string (string, sindex, quoted, flags) t = extract_process_subst (string, (string[i] == '<' ? "<(" : ">)"), &si, flags); CHECK_STRING_OVERRUN (i, si, slen, c); - tlen = si - i - 1; + tlen = si - i - 2; RESIZE_MALLOCED_BUFFER (result, result_index, tlen + 4, result_size, 64); result[result_index++] = c; result[result_index++] = LPAREN; @@ -2002,8 +2001,9 @@ extract_dollar_brace_string (string, sindex, quoted, flags) /* Remove backslashes which are quoting backquotes from STRING. Modifies STRING, and returns a pointer to it. */ char * -de_backslash (string) +de_backslash (string, qflags) char *string; + int qflags; { register size_t slen; register int i, j, prev_i; @@ -2018,6 +2018,8 @@ de_backslash (string) if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' || string[i + 1] == '$')) i++; + else if (posixly_correct && (qflags & Q_HERE_DOCUMENT) && string[i] == '\\' && string[i + 1] == '"') + i++; prev_i = i; ADVANCE_CHAR (string, slen, i); if (j < prev_i) @@ -11327,7 +11329,7 @@ add_string: temp1 = substring (string, t_index, sindex + 1); else { - de_backslash (temp); + de_backslash (temp, quoted); tword = command_substitute (temp, quoted, PF_BACKQUOTE); temp1 = tword ? tword->word : (char *)NULL; if (tword) diff --git a/subst.h b/subst.h index 28cc9203..231b9e98 100644 --- a/subst.h +++ b/subst.h @@ -75,7 +75,7 @@ /* Remove backslashes which are quoting backquotes from STRING. Modifies STRING, and returns a pointer to it. */ -extern char * de_backslash PARAMS((char *)); +extern char * de_backslash PARAMS((char *, int)); /* Replace instances of \! in a string with !. */ extern void unquote_bang PARAMS((char *)); diff --git a/tests/builtins.right b/tests/builtins.right index 4f51d436..58d3b99c 100644 --- a/tests/builtins.right +++ b/tests/builtins.right @@ -278,4 +278,17 @@ type + command -p -- command -v type type + set +x -./builtins.tests: line 284: exit: status: numeric argument required +u=rw,g=rx,o=rx +u=r,g=rx,o=rx +u=rwx,g=rwx,o= +u=rw,g=wx,o=rx +u=rx,g=rx,o=rx +u=rwx,g=rx,o=rwx +u=rwx,g=rwx,o=rx +u=rx,g=rx,o=rx +u=rwx,g=rx,o=rx +u=rwx,g=rwx,o=rwx +u=rwx,g=rwx,o=rwx +u=rwx,g=rx,o=rx +u=rwx,g=rx,o=rx +./builtins.tests: line 287: exit: status: numeric argument required diff --git a/tests/builtins.tests b/tests/builtins.tests index 8eee43e6..50fdbaba 100644 --- a/tests/builtins.tests +++ b/tests/builtins.tests @@ -87,7 +87,7 @@ BVAR=xxx eval echo $AVAR unset -v AVAR BVAR -# test umask +# basic umask tests mask=$(umask) umask 022 umask @@ -280,6 +280,9 @@ ${THIS_SH} ./builtins6.sub # test behavior of command builtin after changing it to a pseudo-keyword ${THIS_SH} ./builtins7.sub +# POSIX complete symbolic umask tests +${THIS_SH} ./builtins8.sub + # this must be last -- it is a fatal error exit status diff --git a/tests/builtins8.sub b/tests/builtins8.sub new file mode 100644 index 00000000..1c1b133f --- /dev/null +++ b/tests/builtins8.sub @@ -0,0 +1,54 @@ +umask 022 +umask u=r+w +umask -S + +umask 022 +umask u=r-w +umask -S + +umask 022 +umask g+u,o+rwx-u +umask -S + +umask 022 +umask u=r+w,g=wx,o+xr +umask -S + +umask 022 +umask u+w=r+x +umask -S + +umask 022 +umask o=u +umask -S + +umask 022 +umask g=u +umask -S + +umask 022 +umask u=rwx,u-w +umask -S + +umask 022 +umask u=xwr +umask -S + +umask 022 +umask +xwr +umask -S + +umask 022 +umask a+xwr +umask -S + +umask 022 +umask +xr +umask -S + +umask 022 +umask a+xr +umask -S + + + diff --git a/tests/comsub.tests b/tests/comsub.tests index 78f10791..8b89ce44 100644 --- a/tests/comsub.tests +++ b/tests/comsub.tests @@ -76,6 +76,11 @@ echo $(echo $( echo nested ) ) ) +BUILDDIR=/builds/test +read << EOC +Dir: ${BUILDDIR#<(echo a)/} +EOC + ${THIS_SH} ./comsub1.sub ${THIS_SH} ./comsub2.sub ${THIS_SH} ./comsub3.sub diff --git a/tests/cond.right b/tests/cond.right index 7bed9fd7..73f61fd3 100644 --- a/tests/cond.right +++ b/tests/cond.right @@ -76,6 +76,7 @@ match control-a 2 match control-a 3 match control-a 4 match control-a 5 +./cond-regexp2.sub: line 18: [[: invalid regular expression `[\.' ok 1 ok 2 ok 3 diff --git a/tests/errors.right b/tests/errors.right index a3ee4f13..001ca22f 100644 --- a/tests/errors.right +++ b/tests/errors.right @@ -48,7 +48,7 @@ hash: usage: hash [-lr] [-p pathname] [-dt] [name ...] ./errors.tests: line 157: umask: `:': invalid symbolic mode operator ./errors.tests: line 160: umask: -i: invalid option umask: usage: umask [-p] [-S] [mode] -./errors.tests: line 164: umask: `u': invalid symbolic mode character +./errors.tests: line 164: umask: `p': invalid symbolic mode character ./errors.tests: line 173: VAR: readonly variable ./errors.tests: line 176: declare: VAR: readonly variable ./errors.tests: line 177: declare: VAR: readonly variable @@ -210,4 +210,9 @@ DEBUG bash: line 1: return: can only `return' from a function or sourced script after return bash: line 1: return: can only `return' from a function or sourced script -./errors.tests: line 305: `!!': not a valid identifier +sh: line 1: unset: a: cannot unset: readonly variable +sh: line 1: unset: `a-b': not a valid identifier +sh: line 1: /nosuchfile: No such file or directory +sh: line 1: trap: SIGNOSIG: invalid signal specification +after trap +./errors.tests: line 316: `!!': not a valid identifier diff --git a/tests/errors.tests b/tests/errors.tests index 0880bb55..8161383f 100644 --- a/tests/errors.tests +++ b/tests/errors.tests @@ -161,7 +161,7 @@ umask -i # bad assignments shouldn't change the umask mask=$(umask) -umask g=u +umask g=p mask2=$(umask) if [ "$mask" != "$mask2" ]; then echo "umask errors change process umask" @@ -297,6 +297,17 @@ ${THIS_SH} ./errors9.sub ${THIS_SH} -c 'return ; echo after return' bash ${THIS_SH} -o posix -c 'return ; echo after return' bash +# various posix-mode special builtin fatal (or not) errors + +# posix says unsetting readonly variables is a fatal error +${THIS_SH} -o posix -c 'readonly a=a ; unset -v a; echo after unset 1' sh +# the same with non-identifiers +${THIS_SH} -o posix -c 'unset -v a-b; echo after unset 2' sh +# and sourcing a non-existent file is fatal too +${THIS_SH} -o posix -c '. /nosuchfile ; echo after source' sh +# but trap specifying a bad signal nunber is non-fatal +${THIS_SH} -o posix -c 'trap "echo bad" SIGNOSIG; echo after trap' sh + # this must be last! # in posix mode, a function name must be a valid identifier # this can't go in posix2.tests, since it causes the shell to exit diff --git a/trap.c b/trap.c index ddd53901..a642052f 100644 --- a/trap.c +++ b/trap.c @@ -1,7 +1,7 @@ /* trap.c -- Not the trap command, but useful functions for manipulating those objects. The trap command is in builtins/trap.def. */ -/* Copyright (C) 1987-2021 Free Software Foundation, Inc. +/* Copyright (C) 1987-2022 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -113,9 +113,11 @@ int pending_traps[NSIG]; trap command (e.g., when `return' is executed in the trap command). */ int running_trap; -/* The variable context (function execution level) when we began running - this trap command. */ -int trap_variable_context; +/* The execution context (function/source execution level) when we began + running this trap command. This is used to determine whether we have + executed any shell functions or sourced files from the trap action, and + determines where `return' without arguments gets its return status. */ +int trap_return_context; /* Set to last_command_exit_value before running a trap. */ int trap_saved_exit_value; @@ -341,7 +343,7 @@ run_pending_traps () ps = save_pipestatus_array (); #endif old_running = running_trap; - old_context = trap_variable_context; + old_context = trap_return_context; for (sig = 1; sig < NSIG; sig++) { @@ -351,7 +353,7 @@ run_pending_traps () { /* XXX - set last_command_exit_value = trap_saved_exit_value here? */ running_trap = sig + 1; - trap_variable_context = variable_context; + trap_return_context = funcnest + sourcenest; if (sig == SIGINT) { @@ -477,7 +479,7 @@ run_pending_traps () if (function_code) { running_trap = old_running; /* XXX */ - trap_variable_context = old_context; + trap_return_context = old_context; /* caller will set last_command_exit_value */ sh_longjmp (return_catch, 1); } @@ -486,7 +488,7 @@ run_pending_traps () pending_traps[sig] = 0; /* XXX - move before evalstring? */ running_trap = old_running; - trap_variable_context = old_context; + trap_return_context = old_context; } } @@ -1006,7 +1008,7 @@ run_exit_trap () retval = trap_saved_exit_value; running_trap = 1; - trap_variable_context = variable_context; + trap_return_context = funcnest + sourcenest; code = setjmp_nosigs (top_level); @@ -1091,14 +1093,14 @@ _run_trap_internal (sig, tag) old_trap = trap_list[sig]; old_modes = sigmodes[sig]; old_running = running_trap; - old_context = trap_variable_context; + old_context = trap_return_context; sigmodes[sig] |= SIG_INPROGRESS; sigmodes[sig] &= ~SIG_CHANGED; /* just to be sure */ trap_command = savestring (old_trap); running_trap = sig + 1; - trap_variable_context = variable_context; + trap_return_context = funcnest + sourcenest; old_int = interrupt_state; /* temporarily suppress pending interrupts */ CLRINTERRUPT; @@ -1161,7 +1163,7 @@ _run_trap_internal (sig, tag) running_trap = old_running; interrupt_state = old_int; - trap_variable_context = old_context; + trap_return_context = old_context; if (sigmodes[sig] & SIG_CHANGED) { diff --git a/trap.h b/trap.h index 518113c9..b2ceca9f 100644 --- a/trap.h +++ b/trap.h @@ -63,7 +63,7 @@ extern char *trap_list[]; extern int trapped_signal_received; extern int wait_signal_received; extern int running_trap; -extern int trap_variable_context; +extern int trap_return_context; extern int trap_saved_exit_value; extern int suppress_debug_trap_verbose;