]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
fix comsub inside parameter expansion in here-document; fix readline longjmp botch...
authorChet Ramey <chet.ramey@case.edu>
Mon, 19 Dec 2022 14:38:15 +0000 (09:38 -0500)
committerChet Ramey <chet.ramey@case.edu>
Mon, 19 Dec 2022 14:38:15 +0000 (09:38 -0500)
26 files changed:
CWRU/CWRU.chlog
MANIFEST
builtins/common.c
builtins/set.def
builtins/umask.def
doc/bashref.texi
execute_cmd.c
lib/readline/display.c
lib/readline/histexpand.c
lib/readline/mbutil.c
lib/readline/readline.c
lib/readline/rlmbutil.h
parse.y
patchlevel.h
shell.h
subst.c
subst.h
tests/builtins.right
tests/builtins.tests
tests/builtins8.sub [new file with mode: 0644]
tests/comsub.tests
tests/cond.right
tests/errors.right
tests/errors.tests
trap.c
trap.h

index f461ab356f2f39e4eb171d456060231fdb1b94bb..c59922f729b058c6da3388c5a43308239df47c37 100644 (file)
@@ -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 <nolange79@gmail.com>
+
+                                  12/14
+                                  -----
+execute_cmd.c
+       - execute_cond_node: if a regular expression fails to compile, print
+         an error message. Report from Emanuele Torre <torreemanuele6@gmail.com>
+         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 <myoga.murase@gmail.com>
+
+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 <torreemanuele6@gmail.com>
+
+                                  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
+
index 7518f4944155f2b3ff3a0b716c61a3e54d3ba647..4d735f66954d0741287e15cff0dfe61a1ffbec15 100644 (file)
--- 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
index b9705c02d6aaabcb31c51bd10c58e7726a5c408e..954eeabeac1ea3ece0523ad348a6f0f030d6216d 100644 (file)
@@ -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);
     }
index 44f17691966d31d1f713edbd60b693e9cfbc793d..4a7c517ec4f7eeb30edf44a54c4285e0424a5c0a 100644 (file)
@@ -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);
 }
index 8041d56b70832bc0f8cf91120e452423f274ea04..2a6ae40580b1529a6c02c3cd5d30e8b8b2949cf4 100644 (file)
@@ -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 == ',')
-       {
-         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. */
-           }
+      perm &= who;
 
-         if (*s == '\0')
-           break;
-         else
-           s++;        /* skip past ',' */
+      switch (op)
+       {
+       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);
index 772903b819362d76db3c374c136314d796629d5b..f5872b6b689368cf46c611a122a5a57d270006e1 100644 (file)
@@ -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
index 994c922966d5e1a87ba0bf3c2d5bbe1ca309d130..f9c86736948b573b2c0af7d38dbd170f6a5c93a6 100644 (file)
@@ -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:
index 2d3747f252367283e1e62a94de4043833e8bf5f8..02b5e5ccf3a80ebb7826fea4ba20c016b2b31c17 100644 (file)
@@ -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);
index 8ab68091e64cd750c58d785f2aa1a05a061a3d9b..35fe3fe7b62df68ede67eb466608c272743d169a 100644 (file)
@@ -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;
index 47e9100a68cc3eb525cd1a90f4ac1c05421ff074..32228bbf6367f5c5c6f46472b64192cff3f97484 100644 (file)
@@ -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);
index 6f538454391f82d59d501ed2691203f5a41cf4aa..bfdb4205cd53f3e76a0280e421217ad8c554f80e 100644 (file)
@@ -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))
index d9060572e5821f6811d1482b741b8490357c85cb..42d47c3d382f384af5c6ca588b9814b4ad7aec10 100644 (file)
 #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 cdba786cadb1bec7f96048cda26797337c4324f5..cad4d596484edf956e43bef8ce69ca379cbb1062 100644 (file)
--- 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);
 
index 91de6238439d9c4434bc4fa715d3f119eb3c6d59..31aaded1780979b265130f54923c5d9e0977f30e 100644 (file)
@@ -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 6e44bca6919284b792bcc718caf2382abace713f..152350768773c8d3ad78db4e389b0d07378ac524 100644 (file)
--- 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 0907a5d00da3891daaade98c5330e50985975036..59fc904078200aaea778c57825912aa8a4d485b4 100644 (file)
--- 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 28cc920316bcd2189091b8ade7c610211455e044..231b9e98f98a66602f0c57fc63b2127db3d00c4f 100644 (file)
--- 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 *));
index 4f51d436cb55f7a621e3d2740e06da286979a541..58d3b99cca82135c223c3190225f86fb1583ec0f 100644 (file)
@@ -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
index 8eee43e6e82a5ee82542aebc35fa231e60cf8496..50fdbaba4312924dd91d5269f0e02d98990e3360 100644 (file)
@@ -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 (file)
index 0000000..1c1b133
--- /dev/null
@@ -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
+
+
+
index 78f10791cb51397d60754a55480ea514aacb18a9..8b89ce44d286b484e2f63328237bc42f6778a205 100644 (file)
@@ -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
index 7bed9fd7b41f60c7c25d6da605e1cef13e75dd82..73f61fd35fc89d1d9ad423e063a75fc6b96d17bf 100644 (file)
@@ -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
index a3ee4f13211cd8f3f466b851a1be47b3eb154ef5..001ca22f6161efbbddf4e4a5c0ac70c70d57ca3e 100644 (file)
@@ -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
index 0880bb556bec6b190686268791b9ab3544fb01cd..8161383f05b1c27e42c4c6b1c6bdf5aaec94d6a1 100644 (file)
@@ -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 ddd539017d7fbf4ac624a90f5680c31a16b6fd04..a642052f7bf2ca37a21e6113c0bf6799f13b8c34 100644 (file)
--- 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 518113c990bbfa941519aae8a2bfda609710c31e..b2ceca9fa05ecdba4e13c158a97e88f2c86ed049 100644 (file)
--- 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;