From: Chet Ramey Date: Mon, 18 Mar 2019 14:48:08 +0000 (-0400) Subject: commit bash-20190313 snapshot X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=15a23d44745c7ab66fa0d5a3e1ff06f60b27b905;p=thirdparty%2Fbash.git commit bash-20190313 snapshot --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 1e92197c5..4526e344e 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -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 + + 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 + +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 diff --git a/MANIFEST b/MANIFEST index dff682475..d8627af8c 100644 --- 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 bca18c544..6491e13b7 100644 --- 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 0c456507b..a2cd45c8a 100644 --- 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)); diff --git a/arrayfunc.c b/arrayfunc.c index 7631e5ab6..7e429a15f 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -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; diff --git a/arrayfunc.h b/arrayfunc.h index cd51ee073..cad13ab42 100644 --- a/arrayfunc.h +++ b/arrayfunc.h @@ -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 673eccb22..9cdbf12f4 100644 --- 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 db8383b2c..de3bf6d68 100644 --- 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 13b29edaa..62a3b3755 100644 --- 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, "ed_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 34763222e..c0e474ab9 100644 --- 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. */ diff --git a/tests/array.right b/tests/array.right index 834e5684e..ce0a7d552 100644 --- a/tests/array.right +++ b/tests/array.right @@ -416,8 +416,9 @@ argv[1] = <> argv[2] = <> argv[3] = <> argv[1] = -argv[1] = <--> -argv[1] = <> +argv[1] = <-> +argv[2] = <-> +argv[1] = < > argv[1] = argv[1] = <-> argv[2] = <-> @@ -426,8 +427,9 @@ argv[1] = <> argv[2] = <> argv[3] = <> argv[1] = -argv[1] = <--> -argv[1] = <> +argv[1] = <-> +argv[2] = <-> +argv[1] = < > argv[1] = argv[1] = <-> argv[2] = <-> diff --git a/tests/dollar-at-star b/tests/dollar-at-star index 3d8791dd5..d48089891 100755 --- a/tests/dollar-at-star +++ b/tests/dollar-at-star @@ -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 index 000000000..f3962f72e --- /dev/null +++ b/tests/dollar-at-star9.sub @@ -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" diff --git a/tests/dollar.right b/tests/dollar.right index 746ce16b5..09910d7e3 100644 --- a/tests/dollar.right +++ b/tests/dollar.right @@ -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] = +argv[1] = < X> +argv[2] = +argv[1] = < X> +argv[2] = +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] = +argv[1] = +argv[1] = +argv[1] = +argv[2] = +argv[1] = +argv[1] = +argv[1] = +argv[2] = +argv[1] = +argv[2] = +argv[1] = +argv[2] = +argv[1] = +argv[2] = +argv[1] = < X > +argv[1] = < X > +argv[1] = < X > +argv[1] = < X > +argv[1] = +argv[2] = +argv[1] = +argv[2] = +argv[1] = +argv[2] = +argv[1] = +argv[2] = +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