]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
commit bash-20190313 snapshot
authorChet Ramey <chet.ramey@case.edu>
Mon, 18 Mar 2019 14:48:08 +0000 (10:48 -0400)
committerChet Ramey <chet.ramey@case.edu>
Mon, 18 Mar 2019 14:48:08 +0000 (10:48 -0400)
14 files changed:
CWRU/CWRU.chlog
MANIFEST
array.c
array.h
arrayfunc.c
arrayfunc.h
assoc.c
assoc.h
subst.c
subst.h
tests/array.right
tests/dollar-at-star
tests/dollar-at-star9.sub [new file with mode: 0644]
tests/dollar.right

index 1e92197c5a92857420dc35edcf7bc5a7bf7bfe8f..4526e344ea7f8998631508cd33916113bfc72114 100644 (file)
@@ -5507,3 +5507,85 @@ subst.c
          so we don't quote each byte in a multibyte character sequence. Fixes
          bug uncovered by shquote changes from 3/8
 
+                                  3/11
+                                  ----
+subst.c
+       - expand_word_internal: if we are in a context where word splitting
+         will not occur, but one where we need to make sure we eventually
+         expand "$@" to multiple words, add a space as quoted so we can still
+         split on the space separator in $@. Fixes bug reported by
+         Grisha Levit <grishalevit@gmail.com>
+
+                                  3/13
+                                  ----
+subst.c
+       - parameter_brace_substring, parameter_brace_patsub: treat the case
+         where pflags includes PF_ASSIGNRHS the same as if IFS is null, since
+         word splitting will not take place
+
+{subst,array,assoc}.c,subst.h
+       - string_list_pos_params: add a fourth argument: pflags; change all
+         callers to initially pass 0 as fourth arg
+
+                                  3/14
+                                  ----
+subst.c
+       - expand_word_internal: split words with W_ASSIGNRHS flag set specially.
+         These and subsequent changes fix expansion bugs reported by
+         Grisha Levit <grishalevit@gmail.com>
+
+subst.c
+       - string_list_pos_params: if we are expanding `@', make sure we honor
+         PFLAGS including PF_ASSIGNRHS and use string_list_dollar_at to make
+         sure the positional parameters are separated by spaces
+       - string_list_pos_params: if we are expanding `*', make sure we honor
+         PFLAGS including PF_ASSIGNRHS and separate the positional parameters
+         with the first character of $IFS
+       - pos_params_pat_subst,pos_params_modcase: calculate appropriate value
+         for PFLAGS depending on match flags value, and pass right value to
+         string_list_pos_params (affects @, * expansion)
+       - pos_params: now takes PFLAGS as argument to pass to string_list_pos_params;
+         changed caller
+
+array.c
+       - array_pat_subst,array_modcase: calculate appropriate value for
+         PFLAGS depending on match flags value, and pass right value to
+         string_list_pos_params (affects @, * subscript expansion)
+
+array.[ch]
+       - array_subrange: now takes additional PFLAGS argument to pass to
+         string_list_pos_params
+
+assoc.c
+       - assoc_pat_subst,assoc_modcase: calculate appropriate value for
+         PFLAGS depending on match flags value, and pass right value to
+         string_list_pos_params (affects @, * subscript expansion)
+         STILL NEED TO DO SUBRANGE
+
+subst.c
+       - parameter_brace_substring: add PFLAGS argument to array_subrange
+
+arrayfunc.c
+       - array_keys: use string_list_pos_params instead of calling
+         string_list_dollar_{star,at} directly.
+
+arrayfunc.[ch]
+       - array_keys: now takes a PFLAGS argument, passes to string_list_pos_params
+
+subst.c
+       - parameter_brace_expand: add PFLAGS argument to call to array_keys
+
+subst.c
+       - parameter_brace_expand_indir: now takes a PFLAGS argument and uses it
+         in the call to parameter_brace_expand_word
+       - parameter_brace_expand: add PFLAGS argument to call to
+         parameter_brace_expand_indir
+
+                                  3/15
+                                  ----
+subst.c
+       - chk_atstar: now takes a PFLAGS parameter, changed callers. Will
+         eventually affect whether or not we saw $@
+       - chk_atstar: if we see "$*" don't note that we saw $@ unless
+         expand_no_split_dollar_star is unset. This is what param_expand
+         does
index dff682475a89ac5369a4617999c334f7ae04744c..d8627af8ce06b37bf9dcb39321b1f18d8192636d 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -993,6 +993,7 @@ tests/dollar-at-star5.sub   f
 tests/dollar-at-star6.sub      f
 tests/dollar-at-star7.sub      f
 tests/dollar-at-star8.sub      f
+tests/dollar-at-star9.sub      f
 tests/dollar-at1.sub   f
 tests/dollar-at2.sub   f
 tests/dollar-at3.sub   f
diff --git a/array.c b/array.c
index bca18c5441a11b7f1e4cc9094b3ae7f2361bbeaf..6491e13b75288e6b316cb053b0dbfe9bee8638e0 100644 (file)
--- a/array.c
+++ b/array.c
@@ -398,10 +398,10 @@ ARRAY     *array;
  * Since arrays are sparse, unset array elements are not counted.
  */
 char *
-array_subrange (a, start, nelem, starsub, quoted)
+array_subrange (a, start, nelem, starsub, quoted, pflags)
 ARRAY  *a;
 arrayind_t     start, nelem;
-int    starsub, quoted;
+int    starsub, quoted, pflags;
 {
        ARRAY           *a2;
        ARRAY_ELEMENT   *h, *p;
@@ -436,7 +436,7 @@ int starsub, quoted;
        array_dispose(a2);
        if (wl == 0)
                return (char *)NULL;
-       t = string_list_pos_params(starsub ? '*' : '@', wl, quoted);
+       t = string_list_pos_params(starsub ? '*' : '@', wl, quoted, pflags);    /* XXX */
        dispose_words(wl);
 
        return t;
@@ -449,7 +449,7 @@ char        *pat, *rep;
 int    mflags;
 {
        char    *t;
-       int     pchar, qflags;
+       int     pchar, qflags, pflags;
        WORD_LIST       *wl, *save;
 
        if (a == 0 || array_head(a) == 0 || array_empty(a))
@@ -467,8 +467,9 @@ int mflags;
 
        pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
        qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
+       pflags = (mflags & MATCH_ASSIGNRHS) ? PF_ASSIGNRHS : 0;
 
-       t = string_list_pos_params (pchar, save, qflags);
+       t = string_list_pos_params (pchar, save, qflags, pflags);
        dispose_words(save);
 
        return t;
@@ -482,7 +483,7 @@ int modop;
 int    mflags;
 {
        char    *t;
-       int     pchar, qflags;
+       int     pchar, qflags, pflags;
        WORD_LIST       *wl, *save;
 
        if (a == 0 || array_head(a) == 0 || array_empty(a))
@@ -500,8 +501,9 @@ int mflags;
 
        pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
        qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
+       pflags = (mflags & MATCH_ASSIGNRHS) ? PF_ASSIGNRHS : 0;
 
-       t = string_list_pos_params (pchar, save, qflags);
+       t = string_list_pos_params (pchar, save, qflags, pflags);
        dispose_words(save);
 
        return t;
diff --git a/array.h b/array.h
index 0c456507baa2ecaee40096822448b2cb5783e4d3..a2cd45c8a92a3666bdcdc9320fc14bb3f5e41082 100644 (file)
--- a/array.h
+++ b/array.h
@@ -27,7 +27,7 @@
 
 typedef intmax_t       arrayind_t;
 
-enum atype {array_indexed, array_assoc};
+enum atype {array_indexed, array_assoc};       /* only array_indexed used */
 
 typedef struct array {
        enum atype      type;
@@ -64,7 +64,7 @@ extern ARRAY  *array_dequote __P((ARRAY *));
 extern ARRAY   *array_dequote_escapes __P((ARRAY *));
 extern ARRAY   *array_remove_quoted_nulls __P((ARRAY *));
 
-extern char    *array_subrange __P((ARRAY *, arrayind_t, arrayind_t, int, int));
+extern char    *array_subrange __P((ARRAY *, arrayind_t, arrayind_t, int, int, int));
 extern char    *array_patsub __P((ARRAY *, char *, char *, int));
 extern char    *array_modcase __P((ARRAY *, char *, int, int));
 
index 7631e5ab6e65203fc0559bdf8bff198f07e916b6..7e429a15fdbbffb0f96ffd2f1afe5eb856966569 100644 (file)
@@ -1224,9 +1224,9 @@ get_array_value (s, flags, rtype, indp)
 }
 
 char *
-array_keys (s, quoted)
+array_keys (s, quoted, pflags)
      char *s;
-     int quoted;
+     int quoted, pflags;
 {
   int len;
   char *retval, *t, *temp;
@@ -1251,6 +1251,9 @@ array_keys (s, quoted)
   if (l == (WORD_LIST *)NULL)
     return ((char *) NULL);
 
+#if 1
+  retval = string_list_pos_params (t[0], l, quoted, pflags);
+#else
   if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
     {
       temp = string_list_dollar_star (l, quoted, 0);
@@ -1259,6 +1262,7 @@ array_keys (s, quoted)
     }
   else /* ${!name[@]} or unquoted ${!name[*]} */
     retval = string_list_dollar_at (l, quoted, 0);
+#endif
 
   dispose_words (l);
   return retval;
index cd51ee073c0cc59b44d9f8e98863efe11baeb3c8..cad13ab429331075663f69dfbe96c66ca8130754 100644 (file)
@@ -75,7 +75,7 @@ extern int valid_array_reference __P((const char *, int));
 extern char *array_value __P((const char *, int, int, int *, arrayind_t *));
 extern char *get_array_value __P((const char *, int, int *, arrayind_t *));
 
-extern char *array_keys __P((char *, int));
+extern char *array_keys __P((char *, int, int));
 
 extern char *array_variable_name __P((const char *, int, char **, int *));
 extern SHELL_VAR *array_variable_part __P((const char *, int, char **, int *));
diff --git a/assoc.c b/assoc.c
index 673eccb22c11c8c569ce41c31f82621a212edc8d..9cdbf12f4ed3f750371f8ffb3c715385eddb4a11 100644 (file)
--- a/assoc.c
+++ b/assoc.c
@@ -258,10 +258,10 @@ assoc_remove_quoted_nulls (h)
  * the STARTth element and spanning NELEM members.  Null elements are counted.
  */
 char *
-assoc_subrange (hash, start, nelem, starsub, quoted)
-HASH_TABLE *hash;
-arrayind_t start, nelem;
-int starsub, quoted;
+assoc_subrange (hash, start, nelem, starsub, quoted, pflags)
+     HASH_TABLE *hash;
+     arrayind_t start, nelem;
+     int starsub, quoted, pflags;
 {
   WORD_LIST *l, *save, *h, *t;
   int i, j;
@@ -289,7 +289,7 @@ int starsub, quoted;
 
   t->next = (WORD_LIST *)NULL;
 
-  ret = string_list_pos_params (starsub ? '*' : '@', h, quoted);
+  ret = string_list_pos_params (starsub ? '*' : '@', h, quoted, pflags);
 
   if (t != l)
     t->next = l;
@@ -306,7 +306,7 @@ assoc_patsub (h, pat, rep, mflags)
      int mflags;
 {
   char *t;
-  int pchar, qflags;
+  int pchar, qflags, pflags;
   WORD_LIST *wl, *save;
 
   if (h == 0 || assoc_empty (h))
@@ -325,8 +325,9 @@ assoc_patsub (h, pat, rep, mflags)
 
   pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
   qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
+  pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
 
-  t = string_list_pos_params (pchar, save, qflags);
+  t = string_list_pos_params (pchar, save, qflags, pflags);
   dispose_words (save);
 
   return t;
@@ -340,7 +341,7 @@ assoc_modcase (h, pat, modop, mflags)
      int mflags;
 {
   char *t;
-  int pchar, qflags;
+  int pchar, qflags, pflags;
   WORD_LIST *wl, *save;
 
   if (h == 0 || assoc_empty (h))
@@ -359,8 +360,9 @@ assoc_modcase (h, pat, modop, mflags)
 
   pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
   qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
+  pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
 
-  t = string_list_pos_params (pchar, save, qflags);
+  t = string_list_pos_params (pchar, save, qflags, pflags);
   dispose_words (save);
 
   return t;
diff --git a/assoc.h b/assoc.h
index db8383b2c9bc63f04b5e0c262ca640c6357d016a..de3bf6d68313308fcadc62edc248606a615841fe 100644 (file)
--- a/assoc.h
+++ b/assoc.h
@@ -43,7 +43,7 @@ extern void assoc_remove __P((HASH_TABLE *, char *));
 
 extern char *assoc_reference __P((HASH_TABLE *, char *));
 
-extern char *assoc_subrange __P((HASH_TABLE *, arrayind_t, arrayind_t, int, int));
+extern char *assoc_subrange __P((HASH_TABLE *, arrayind_t, arrayind_t, int, int, int));
 extern char *assoc_patsub __P((HASH_TABLE *, char *, char *, int));
 extern char *assoc_modcase __P((HASH_TABLE *, char *, int, int));
 
diff --git a/subst.c b/subst.c
index 13b29edaa03a596a0b800c38faba69c632b2f70e..62a3b37555ee626776c25dba5a72454262e49476 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -264,7 +264,7 @@ static char *extract_delimited_string __P((char *, int *, char *, char *, char *
 static char *extract_dollar_brace_string __P((char *, int *, int, int));
 static int skip_matched_pair __P((const char *, int, int, int, int));
 
-static char *pos_params __P((char *, int, int, int));
+static char *pos_params __P((char *, int, int, int, int));
 
 static unsigned char *mb_getcharlens __P((char *, int));
 
@@ -311,12 +311,12 @@ static arrayind_t array_length_reference __P((char *));
 #endif
 
 static int valid_brace_expansion_word __P((char *, int));
-static int chk_atstar __P((char *, int, int *, int *));
+static int chk_atstar __P((char *, int, int, int *, int *));
 static int chk_arithsub __P((const char *, int));
 
 static WORD_DESC *parameter_brace_expand_word __P((char *, int, int, int, arrayind_t *));
 static char *parameter_brace_find_indir __P((char *, int, int, int));
-static WORD_DESC *parameter_brace_expand_indir __P((char *, int, int, int *, int *));
+static WORD_DESC *parameter_brace_expand_indir __P((char *, int, int, int, int *, int *));
 static WORD_DESC *parameter_brace_expand_rhs __P((char *, char *, int, int, int, int *, int *));
 static void parameter_brace_expand_error __P((char *, char *, int));
 
@@ -2676,11 +2676,13 @@ string_list_dollar_at (list, quoted, flags)
    the various subtleties of using the first character of $IFS as the
    separator.  Calls string_list_dollar_at, string_list_dollar_star, and
    string_list as appropriate. */
+/* This needs to fully understand the additional contexts where word
+   splitting does not occur (W_ASSIGNRHS, etc.) */
 char *
-string_list_pos_params (pchar, list, quoted)
+string_list_pos_params (pchar, list, quoted, pflags)
      int pchar;
      WORD_LIST *list;
-     int quoted;
+     int quoted, pflags;
 {
   char *ret;
   WORD_LIST *tlist;
@@ -2699,6 +2701,8 @@ string_list_pos_params (pchar, list, quoted)
     }
   else if (pchar == '*' && quoted == 0 && ifs_is_null) /* XXX */
     ret = expand_no_split_dollar_star ? string_list_dollar_star (list, quoted, 0) : string_list_dollar_at (list, quoted, 0);   /* Posix interp 888 */
+  else if (pchar == '*' && quoted == 0 && (pflags & PF_ASSIGNRHS))     /* XXX */
+    ret = expand_no_split_dollar_star ? string_list_dollar_star (list, quoted, 0) : string_list_dollar_at (list, quoted, 0);   /* Posix interp 888 */
   else if (pchar == '*')
     {
       /* Even when unquoted, string_list_dollar_star does the right thing
@@ -2717,6 +2721,8 @@ string_list_pos_params (pchar, list, quoted)
     ret = string_list_dollar_at (list, quoted, 0);
   else if (pchar == '@' && quoted == 0 && ifs_is_null) /* XXX */
     ret = string_list_dollar_at (list, quoted, 0);     /* Posix interp 888 */
+  else if (pchar == '@' && quoted == 0 && (pflags & PF_ASSIGNRHS))
+    ret = string_list_dollar_at (list, quoted, pflags);        /* Posix interp 888 */
   else if (pchar == '@')
     ret = string_list_dollar_star (list, quoted, 0);
   else
@@ -3391,9 +3397,9 @@ string_rest_of_args (dollar_star)
    Q_HERE_DOCUMENT or Q_DOUBLE_QUOTES, this returns a quoted list, otherwise
    no quoting chars are added. */
 static char *
-pos_params (string, start, end, quoted)
+pos_params (string, start, end, quoted, pflags)
      char *string;
-     int start, end, quoted;
+     int start, end, quoted, pflags;
 {
   WORD_LIST *save, *params, *h, *t;
   char *ret;
@@ -3427,7 +3433,7 @@ pos_params (string, start, end, quoted)
     }
   t->next = (WORD_LIST *)NULL;
 
-  ret = string_list_pos_params (string[0], h, quoted);
+  ret = string_list_pos_params (string[0], h, quoted, pflags);
 
   if (t != params)
     t->next = params;
@@ -3755,7 +3761,7 @@ expand_string_unsplit (string, quoted)
     {
       if (value->word)
        {
-         remove_quoted_nulls (value->word->word);
+         remove_quoted_nulls (value->word->word);      /* XXX */
          value->word->flags &= ~W_HASQUOTEDNULL;
        }
       dequote_list (value);
@@ -3797,7 +3803,7 @@ expand_string_assignment (string, quoted)
     {
       if (value->word)
        {
-         remove_quoted_nulls (value->word->word);
+         remove_quoted_nulls (value->word->word);      /* XXX */
          value->word->flags &= ~W_HASQUOTEDNULL;
        }
       dequote_list (value);
@@ -3839,7 +3845,7 @@ expand_prompt_string (string, quoted, wflags)
     {
       if (value->word)
        {
-         remove_quoted_nulls (value->word->word);
+         remove_quoted_nulls (value->word->word);      /* XXX */
          value->word->flags &= ~W_HASQUOTEDNULL;
        }
       dequote_list (value);
@@ -3904,7 +3910,7 @@ expand_string_for_rhs (string, quoted, op, pflags, dollar_at_p, expanded_p)
      in Posix bug 1129 */
   old_nosplit = expand_no_split_dollar_star;
   expand_no_split_dollar_star = (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || op == '=' || ifs_is_null == 0; /* XXX - was 1 */
-  td.flags = W_EXPANDRHS;              /* expanding RHS of ${paramOPword */
+  td.flags = W_EXPANDRHS;              /* expanding RHS of ${paramOPword} */
   td.flags |= W_NOSPLIT2;              /* no splitting, remove "" and '' */
   if (pflags & PF_ASSIGNRHS)           /* pass through */
     td.flags |= W_ASSIGNRHS;
@@ -5166,7 +5172,7 @@ list_remove_pattern (list, pattern, patspec, itype, quoted)
     }
 
   l = REVERSE_LIST (new, WORD_LIST *);
-  tword = string_list_pos_params (itype, l, quoted);
+  tword = string_list_pos_params (itype, l, quoted, 0);
   dispose_words (l);
 
   return (tword);
@@ -6547,9 +6553,9 @@ valid_brace_expansion_word (name, var_is_special)
 }
 
 static int
-chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at)
+chk_atstar (name, quoted, pflags, quoted_dollar_atp, contains_dollar_at)
      char *name;
-     int quoted;
+     int quoted, pflags;
      int *quoted_dollar_atp, *contains_dollar_at;
 {
   char *temp1;
@@ -6574,7 +6580,9 @@ chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at)
     }
   else if (name[0] == '*' && name[1] == '\0' && quoted == 0)
     {
-      if (contains_dollar_at)
+      /* Need more checks here that parallel what string_list_pos_params and
+        param_expand do. Check expand_no_split_dollar_star and ??? */
+      if (contains_dollar_at && expand_no_split_dollar_star == 0)
        *contains_dollar_at = 1;
       return 1;
     }
@@ -6813,9 +6821,9 @@ parameter_brace_find_indir (name, var_is_special, quoted, find_nameref)
 /* Expand an indirect reference to a variable: ${!NAME} expands to the
    value of the variable whose name is the value of NAME. */
 static WORD_DESC *
-parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at)
+parameter_brace_expand_indir (name, var_is_special, quoted, pflags, quoted_dollar_atp, contains_dollar_at)
      char *name;
-     int var_is_special, quoted;
+     int var_is_special, quoted, pflags;
      int *quoted_dollar_atp, *contains_dollar_at;
 {
   char *t;
@@ -6853,7 +6861,7 @@ parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, c
       
   t = parameter_brace_find_indir (name, var_is_special, quoted, 0);
 
-  chk_atstar (t, quoted, quoted_dollar_atp, contains_dollar_at);
+  chk_atstar (t, quoted, pflags, quoted_dollar_atp, contains_dollar_at);
 
 #if defined (ARRAY_VARS)
   /* Array references to unset variables are also an error */
@@ -6886,7 +6894,7 @@ parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, c
       return (w);
     }
        
-  w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, 0, 0);
+  w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, pflags, 0);
   free (t);
 
   return w;
@@ -7649,7 +7657,7 @@ list_transform (xc, v, list, itype, quoted)
   if (itype == '*' && expand_no_split_dollar_star && ifs_is_null)
     qflags |= Q_DOUBLE_QUOTES;         /* Posix interp 888 */
 
-  tword = string_list_pos_params (itype, l, qflags);
+  tword = string_list_pos_params (itype, l, qflags, 0);
   dispose_words (l);
 
   return (tword);
@@ -7910,20 +7918,20 @@ parameter_brace_substring (varname, value, ind, substr, quoted, pflags, flags)
     case VT_POSPARMS:
     case VT_ARRAYVAR:
       if (vtype == VT_POSPARMS)
-       tt = pos_params (varname, e1, e2, quoted);
+       tt = pos_params (varname, e1, e2, quoted, pflags);
 #if defined (ARRAY_VARS)
         /* assoc_subrange and array_subrange both call string_list_pos_params,
           so we can treat this case just like VT_POSPARAMS. */
       else if (assoc_p (v))
        /* we convert to list and take first e2 elements starting at e1th
           element -- officially undefined for now */   
-       tt = assoc_subrange (assoc_cell (v), e1, e2, starsub, quoted);
+       tt = assoc_subrange (assoc_cell (v), e1, e2, starsub, quoted, pflags);
       else
        /* We want E2 to be the number of elements desired (arrays can be
           sparse, so verify_substring_values just returns the numbers
           specified and we rely on array_subrange to understand how to
           deal with them). */
-       tt = array_subrange (array_cell (v), e1, e2, starsub, quoted);
+       tt = array_subrange (array_cell (v), e1, e2, starsub, quoted, pflags);
 #endif
       /* We want to leave this alone in every case where pos_params/
         string_list_pos_params quotes the list members */
@@ -7931,6 +7939,10 @@ parameter_brace_substring (varname, value, ind, substr, quoted, pflags, flags)
        {
          temp = tt;    /* Posix interp 888 */
        }
+      else if (tt && quoted == 0 && (pflags & PF_ASSIGNRHS))
+       {
+         temp = tt;    /* Posix interp 888 */
+       }
       else if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0)
        {
          temp = tt ? quote_escapes (tt) : (char *)NULL;
@@ -8112,7 +8124,7 @@ pos_params_pat_subst (string, pat, rep, mflags)
   WORD_LIST *save, *params;
   WORD_DESC *w;
   char *ret;
-  int pchar, qflags;
+  int pchar, qflags, pflags;
 
   save = params = list_rest_of_args ();
   if (save == 0)
@@ -8129,13 +8141,14 @@ pos_params_pat_subst (string, pat, rep, mflags)
 
   pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
   qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
+  pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
 
   /* If we are expanding in a context where word splitting will not be
      performed, treat as quoted. This changes how $* will be expanded. */
   if (pchar == '*' && (mflags & MATCH_ASSIGNRHS) && expand_no_split_dollar_star && ifs_is_null)
     qflags |= Q_DOUBLE_QUOTES;         /* Posix interp 888 */
 
-  ret = string_list_pos_params (pchar, save, qflags);
+  ret = string_list_pos_params (pchar, save, qflags, pflags);
   dispose_words (save);
 
   return (ret);
@@ -8283,6 +8296,10 @@ parameter_brace_patsub (varname, value, ind, patsub, quoted, pflags, flags)
        {
          /* Posix interp 888 */
        }
+      else if (temp && quoted == 0 && (pflags & PF_ASSIGNRHS))
+       {
+         /* Posix interp 888 */
+       }
       else if (temp && (mflags & MATCH_QUOTED) == 0)
        {
          tt = quote_escapes (temp);
@@ -8344,7 +8361,7 @@ pos_params_modcase (string, pat, modop, mflags)
   WORD_LIST *save, *params;
   WORD_DESC *w;
   char *ret;
-  int pchar, qflags;
+  int pchar, qflags, pflags;
 
   save = params = list_rest_of_args ();
   if (save == 0)
@@ -8361,13 +8378,14 @@ pos_params_modcase (string, pat, modop, mflags)
 
   pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
   qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
+  pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
 
   /* If we are expanding in a context where word splitting will not be
      performed, treat as quoted.  This changes how $* will be expanded. */
   if (pchar == '*' && (mflags & MATCH_ASSIGNRHS) && ifs_is_null)
     qflags |= Q_DOUBLE_QUOTES;         /* Posix interp 888 */
 
-  ret = string_list_pos_params (pchar, save, qflags);
+  ret = string_list_pos_params (pchar, save, qflags, pflags);
   dispose_words (save);
 
   return (ret);
@@ -8802,7 +8820,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
       FREE (x);
       if (ALL_ELEMENT_SUB (x1[0]) && x1[1] == RBRACK)
        {
-         temp = array_keys (temp1, quoted);    /* handles assoc vars too */
+         temp = array_keys (temp1, quoted, pflags);    /* handles assoc vars too */
          if (x1[0] == '@')
            {
              if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
@@ -8837,7 +8855,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
 
   if (want_indir)
     {
-      tdesc = parameter_brace_expand_indir (name + 1, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at);
+      tdesc = parameter_brace_expand_indir (name + 1, var_is_special, quoted, pflags, quoted_dollar_atp, contains_dollar_at);
       if (tdesc == &expand_wdesc_error || tdesc == &expand_wdesc_fatal)
        {
          temp = (char *)NULL;
@@ -8894,7 +8912,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
          if (expand_no_split_dollar_star && t[1] == '*')       /* XXX */
            qflags |= Q_DOUBLE_QUOTES;
        }
-      chk_atstar (name, qflags, quoted_dollar_atp, contains_dollar_at);
+      chk_atstar (name, qflags, pflags, quoted_dollar_atp, contains_dollar_at);
     }
 #endif
 
@@ -8909,7 +8927,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
                   (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) &&
                   QUOTED_NULL (temp) &&
                   valid_array_reference (name, 0) &&
-                  chk_atstar (name, 0, (int *)0, (int *)0);
+                  chk_atstar (name, 0, 0, (int *)0, (int *)0);
 #endif
 
   /* Get the rest of the stuff inside the braces. */
@@ -8962,10 +8980,13 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
         "$@" to take a different code path. In fact, we make sure at the end
         of expand_word_internal that we're only looking at these flags if
         quoted_dollar_at == 0. */
-      if (temp1 && 
+      if (temp1 &&
           (quoted_dollar_atp == 0 || *quoted_dollar_atp == 0) &&
          QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
        ret->flags |= W_QUOTED|W_HASQUOTEDNULL;
+      else if (temp1 && (name[0] == '*' && name[1] == 0) && quoted == 0 &&
+               (pflags & PF_ASSIGNRHS))
+       ret->flags |= W_SPLITSPACE;     /* Posix interp 888 */  
       /* Special handling for $* when unquoted and $IFS is null. Posix interp 888 */
       else if (temp1 && (name[0] == '*' && name[1] == 0) && quoted == 0 && ifs_is_null)
        ret->flags |= W_SPLITSPACE;     /* Posix interp 888 */
@@ -9205,9 +9226,9 @@ param_expand (string, sindex, quoted, expanded_something,
   unsigned char c;
   intmax_t number;
   SHELL_VAR *var;
-  WORD_LIST *list;
+  WORD_LIST *list, *l;
   WORD_DESC *tdesc, *ret;
-  int tflag;
+  int tflag, nullarg;
 
 /*itrace("param_expand: `%s' pflags = %d", string+*sindex, pflags);*/
   zindex = *sindex;
@@ -9442,6 +9463,12 @@ param_expand (string, sindex, quoted, expanded_something,
        }
 #endif
 
+      for (nullarg = 0, l = list; l; l = l->next)
+       {
+         if (l->word && (l->word->word == 0 || l->word->word[0] == 0))
+           nullarg = 1;
+       }
+
       /* We want to flag the fact that we saw this.  We can't turn
         off quoting entirely, because other characters in the
         string might need it (consider "\"$@\""), but we need some
@@ -9460,12 +9487,18 @@ param_expand (string, sindex, quoted, expanded_something,
         parameters no matter what IFS is set to. */
       /* XXX - what to do when in a context where word splitting is not
         performed? Even when IFS is not the default, posix seems to imply
-        that we behave like unquoted $* ?  See below for how we use
-        PF_NOSPLIT2 here. */
+        that we have to expand $@ to all the positional parameters and
+        separate them with spaces, which are preserved because word splitting
+        doesn't take place.  See below for how we use PF_NOSPLIT2 here. */
 
       /* These are the cases where word splitting will not be performed. */
       if (pflags & PF_ASSIGNRHS)
-       temp = string_list_dollar_at (list, (quoted|Q_DOUBLE_QUOTES), pflags);
+       {
+         temp = string_list_dollar_at (list, (quoted|Q_DOUBLE_QUOTES), pflags);
+         if (nullarg)
+           tflag |= W_HASQUOTEDNULL;   /* we know quoting produces quoted nulls */
+       }
+
       /* This needs to match what expand_word_internal does with non-quoted $@
         does with separating with spaces.  Passing Q_DOUBLE_QUOTES means that
         the characters in LIST will be quoted, and PF_ASSIGNRHS ensures that
@@ -10059,6 +10092,7 @@ add_string:
            pflags |= PF_ASSIGNRHS;
          if (word->flags & W_COMPLETE)
            pflags |= PF_COMPLETE;
+
          tword = param_expand (string, &sindex, quoted, expanded_something,
                               &temp_has_dollar_at, &quoted_dollar_at,
                               &had_quoted_null, pflags);
@@ -10231,6 +10265,9 @@ add_twochars:
              if (word->flags & W_NOPROCSUB)
                tword->flags |= W_NOPROCSUB;
 
+if (word->flags & W_ASSIGNRHS)
+  tword->flags |= W_ASSIGNRHS;
+
              temp = (char *)NULL;
 
              temp_has_dollar_at = 0;   /* does this quoted (sub)string include $@? */
@@ -10436,13 +10473,30 @@ add_twochars:
 
          /* break; */
 
+       case ' ':
+         /* If we are in a context where the word is not going to be split, but
+            we need to account for $@ and $* producing one word for each
+            positional parameter, add quoted spaces so the spaces in the
+            expansion of "$@", if any, behave correctly. We still may need to
+            split if we are expanding the rhs of a word expansion. */
+         if (ifs_is_null || split_on_spaces || ((word->flags & (W_NOSPLIT|W_NOSPLIT2|W_ASSIGNRHS)) && (word->flags & W_EXPANDRHS) == 0))
+           {
+             if (string[sindex])
+               sindex++;
+             twochars[0] = CTLESC;
+             twochars[1] = c;
+             goto add_twochars;
+           }
+         /* FALLTHROUGH */
+         
        default:
          /* This is the fix for " $@ " */
-       add_ifs_character:
+add_ifs_character:
          if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (isexp == 0 && isifs (c) && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0))
            {
              if ((quoted&(Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0)
                has_quoted_ifs++;
+add_quoted_character:
              if (string[sindex])       /* from old goto dollar_add_string */
                sindex++;
              if (c == 0)
@@ -10474,7 +10528,7 @@ add_twochars:
 
          SADD_MBCHAR (temp, string, sindex, string_size);
 
-       add_character:
+add_character:
          RESIZE_MALLOCED_BUFFER (istring, istring_index, 1, istring_size,
                                  DEFAULT_ARRAY_SIZE);
          istring[istring_index++] = c;
@@ -10555,6 +10609,17 @@ finished_with_string:
        tword->flags |= W_QUOTED;
       list = make_word_list (tword, (WORD_LIST *)NULL);
     }
+  else if (word->flags & W_ASSIGNRHS)
+    {
+      list = list_string (istring, "", quoted);
+      tword = list->word;
+      if (had_quoted_null && QUOTED_NULL (istring))
+       tword->flags |= W_HASQUOTEDNULL;
+      free (list);
+      free (istring);
+      istring = 0;                     /* avoid later free() */
+      goto set_word_flags;
+    }
   else
     {
       char *ifs_chars;
diff --git a/subst.h b/subst.h
index 34763222e7678baefe67d9652f8a0c8d1270bdfb..c0e474ab92ae1a4996b9bedb3189126868d0e83e 100644 (file)
--- a/subst.h
+++ b/subst.h
@@ -115,7 +115,7 @@ extern char *string_list_dollar_at __P((WORD_LIST *, int, int));
    the various subtleties of using the first character of $IFS as the
    separator.  Calls string_list_dollar_at, string_list_dollar_star, and
    string_list as appropriate. */
-extern char *string_list_pos_params __P((int, WORD_LIST *, int));
+extern char *string_list_pos_params __P((int, WORD_LIST *, int, int));
 
 /* Perform quoted null character removal on each element of LIST.
    This modifies LIST. */
index 834e5684eb6725b000abdff97b3019113f2354ad..ce0a7d5522db52ba12f8267bd426f561ec5de373 100644 (file)
@@ -416,8 +416,9 @@ argv[1] = <>
 argv[2] = <>
 argv[3] = <>
 argv[1] = <bar>
-argv[1] = <-->
-argv[1] = <>
+argv[1] = <->
+argv[2] = <->
+argv[1] = <  >
 argv[1] = <qux>
 argv[1] = <->
 argv[2] = <->
@@ -426,8 +427,9 @@ argv[1] = <>
 argv[2] = <>
 argv[3] = <>
 argv[1] = <bar>
-argv[1] = <-->
-argv[1] = <>
+argv[1] = <->
+argv[2] = <->
+argv[1] = <  >
 argv[1] = <qux>
 argv[1] = <->
 argv[2] = <->
index 3d8791dd5bb40b9176bf3a4ca95cc3d07fdbe3b4..d48089891c4cc523afa7dd7b296c54fe18ab8794 100755 (executable)
@@ -242,6 +242,10 @@ ${THIS_SH} ./dollar-at-star7.sub
 # members separated by spaces
 ${THIS_SH} ./dollar-at-star8.sub
 
+# more tests of the expansions of $@ and $* (and their array equivalents)
+# with different values for IFS
+${THIS_SH} ./dollar-at-star9.sub
+
 # tests for special expansion of "$*" and "${array[*]}" when used with other
 # expansions -- bugs through bash-2.05b
 ${THIS_SH} ./dollar-star1.sub
diff --git a/tests/dollar-at-star9.sub b/tests/dollar-at-star9.sub
new file mode 100644 (file)
index 0000000..f3962f7
--- /dev/null
@@ -0,0 +1,265 @@
+IFS=$' \t\n'   # or any other IFS
+set -- ''
+recho ${v= "$*" }
+recho "$v"
+unset -v v
+
+IFS=''
+set -- '' ''
+recho ${v= "$*" }
+recho "$v"
+unset -v v
+
+IFS=$' \t\n'   # or any other IFS
+unset -v v
+
+set -- ''
+recho ${v= "$@" }
+recho "$v"
+unset v
+recho ${v= $@ }
+recho "$v"
+unset v
+recho ${v= $@"" }
+recho "$v"
+unset v
+recho ${v= ${@} }
+recho "$v"
+unset v
+recho ${v= ${@}"" }
+recho "$v"
+unset v
+
+set -- '' ''
+recho ${v= $@ }
+recho "$v"
+unset v
+recho ${v= "$@" }
+recho "$v"
+unset v
+recho "${v= $@}"
+recho "$v"
+unset v
+recho ${v= "$@"}
+recho "$v"
+unset v
+
+IFS=
+
+set -- X
+X=X
+
+recho ${0+ "$@" }
+recho ${0+ $@ }
+recho ${0+ $* }
+
+recho ${0+ "$X" }
+recho ${0+ $X }
+recho ${0+ $X }
+
+recho ${0+ "$@" }
+recho "$Y"
+unset Y
+recho ${0+ $@ }
+recho "$Y"
+unset Y
+recho ${0+ $* }
+recho "$Y"
+unset Y
+
+recho ${Y:= "$X" }
+recho "$Y"
+unset Y
+recho ${Y:= $X }
+recho "$Y"
+unset Y
+recho ${Y:= $X }
+recho "$Y"
+unset Y
+
+IFS=
+
+unset -v X Y
+
+set -- X Y
+X='X Y'
+
+recho ${0+ "$@" }
+recho ${0+ $@ }
+recho ${0+ $* }
+
+recho ${0+ "$X" }
+recho ${0+ $X }
+recho ${0+ $X }
+
+recho ${Y:= "$@" }
+recho "$Y"
+unset Y
+recho ${Y:= $@ }
+recho "$Y"
+unset Y
+recho ${Y:= $* }
+recho "$Y"
+unset Y
+
+recho ${Y:= "$X" }
+recho "$Y"
+unset Y
+recho ${Y:= $X }
+recho "$Y"
+unset Y
+recho ${Y:= $X }
+recho "$Y"
+unset Y
+
+IFS=''
+set -- ' X '
+
+unset x y
+
+x=$*
+y=${*:1}
+
+recho "$x"
+recho "$y"
+
+unset x y
+
+recho ${x=$*}
+recho ${y=${*:1}}
+
+set -- b a
+declare -A A=([b]= [a]=)
+
+x=$*
+y=${!A[*]}
+
+unset A
+
+recho "$x"
+recho "$y"
+
+unset x y
+
+recho ${x=$*}
+recho ${y=${!A[*]}}
+
+unset x y
+
+recho ${x-$*}
+recho ${y-${!A[*]}}    # this isn't right yet
+
+IFS=:
+set -- a b
+ind=*
+
+unset x y
+
+x=$*
+y=${!ind}
+
+recho "$x"
+recho "$y"
+
+unset x y
+
+recho ${x-$*}
+recho ${y-${!ind}}     # this isn't right yet
+
+unset x y
+
+recho ${x=$*}
+recho ${y=${!ind}}
+
+set -- ' X '
+IFS=$' \t\n'
+
+x=$*
+y=${!ind};
+
+recho "$x"
+recho "$y"
+
+IFS=''
+x=$*
+y=${!ind}
+
+recho "$x"
+recho "$y"
+
+IFS=:
+set -- a b
+ind=*
+
+unset x y
+
+recho ${x-$*}
+recho ${y-${!ind}}     # this isn't right yet
+
+unset x y
+
+recho ${x=$*}
+recho ${y=${!ind}}
+
+set -- ' X '
+IFS=$' \t\n'
+
+unset x y
+
+x=$*
+y=${!ind};
+
+recho "$x"
+recho "$y"
+
+IFS=''
+x=$*
+y=${!ind}
+
+recho "$x"
+recho "$y"
+
+IFS=''
+set -- $'\177'
+
+unset -v var
+
+recho "${*:1}"
+var=${*:1}
+recho "$var"
+
+unset var
+recho ${var=${*:1}}
+recho "$var"
+
+declare -a a=($'\177')
+
+unset var
+var=${a[*]:0}
+recho "$var"
+
+unset var
+recho ${var=${a[*]:0}}
+unset var
+
+set -- $'\177'
+ind='*'
+
+recho $*
+var=${!ind}
+recho "$var"
+
+unset var
+recho ${var=${!ind}}
+recho "$var"
+
+declare -A A=([0]=$'\177')
+
+unset var
+var=${A[*]:0}
+recho "$var"
+
+# this isn't really right yet
+unset var
+recho ${var=${A[*]:0}}
+recho "$var"
index 746ce16b5a2683fdf9d2a8bdc49957ba1ede93ef..09910d7e316b6da545c991598df2a4bbbb2d89e7 100644 (file)
@@ -302,6 +302,103 @@ a1=a b c a,b,c a b c a,b,c a b c a,b,c
 a2=a b c a,b,c a b c a,b,c a b c a,b,c
 a3=a b c a,b,c a b c a,b,c a b c a,b,c
 a4=a b c a,b,c a b c a,b,c a b c a,b,c
+argv[1] = <  >
+argv[1] = <  >
+argv[1] = <  >
+argv[1] = <  >
+argv[1] = <  >
+argv[1] = <  >
+argv[1] = <  >
+argv[1] = <  >
+argv[1] = <   >
+argv[1] = <   >
+argv[1] = <  >
+argv[1] = <  >
+argv[1] = <  >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = <>
+argv[1] = < X >
+argv[1] = <>
+argv[1] = < X >
+argv[1] = <>
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X>
+argv[2] = <Y >
+argv[1] = < X>
+argv[2] = <Y >
+argv[1] = < X>
+argv[2] = <Y >
+argv[1] = < X Y >
+argv[1] = < X Y >
+argv[1] = < X Y >
+argv[1] = < X Y >
+argv[1] = < X Y >
+argv[1] = < X Y >
+argv[1] = < X Y >
+argv[1] = < XY >
+argv[1] = < XY >
+argv[1] = < X Y >
+argv[1] = < X Y >
+argv[1] = < X Y >
+argv[1] = < X Y >
+argv[1] = < X Y >
+argv[1] = < X Y >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = <ba>
+argv[1] = <ba>
+argv[1] = <ba>
+argv[1] = <b>
+argv[2] = <a>
+argv[1] = <a:b>
+argv[1] = <a:b>
+argv[1] = <a>
+argv[2] = <b>
+argv[1] = <a>
+argv[2] = <b>
+argv[1] = <a>
+argv[2] = <b>
+argv[1] = <a>
+argv[2] = <b>
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = <a>
+argv[2] = <b>
+argv[1] = <a>
+argv[2] = <b>
+argv[1] = <a>
+argv[2] = <b>
+argv[1] = <a>
+argv[2] = <b>
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = < X >
+argv[1] = <^?>
+argv[1] = <>
+argv[1] = <>
+argv[1] = <>
+argv[1] = <^?>
+argv[1] = <^?>
+argv[1] = <^?>
+argv[1] = <^?>
+argv[1] = <>
+argv[1] = <>
 xa|xb|xc
 xa|xb|xc
 a|b|c