diff options
author | Axel Beckert <abe@deuxchevaux.org> | 2022-04-11 00:17:48 +0200 |
---|---|---|
committer | Axel Beckert <abe@deuxchevaux.org> | 2022-04-11 00:17:48 +0200 |
commit | b09f4483416c54c1782824633dfabaf2ec0265b6 (patch) | |
tree | 304bc82642862525ae680c7fbaa249663b10ad57 /Src/subst.c | |
parent | 12eb3e5356f2fc3351eed58ef1cef1b8fb83b504 (diff) | |
parent | 6e55c920503071e917619b8cb1a188cd35d772db (diff) | |
download | zsh-b09f4483416c54c1782824633dfabaf2ec0265b6.tar.gz zsh-b09f4483416c54c1782824633dfabaf2ec0265b6.zip |
New upstream version 5.8.1.2-test
Diffstat (limited to 'Src/subst.c')
-rw-r--r-- | Src/subst.c | 115 |
1 files changed, 90 insertions, 25 deletions
diff --git a/Src/subst.c b/Src/subst.c index 79efc9ad2..0f98e6ea3 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -765,7 +765,7 @@ filesubstr(char **namptr, int assign) *namptr = dyncat(res, ptr2+1); return 1; } - if (isset(NOMATCH)) + if (isset(NOMATCH) && isset(EXECOPT)) zerr("no directory expansion: ~[%s]", tmp); return 0; } else if (!inblank(str[1]) && isend(*ptr) && @@ -796,7 +796,7 @@ filesubstr(char **namptr, int assign) *namptr = dyncat(hom, ptr); return 1; } - } else if (*str == Equals && isset(EQUALS) && str[1]) { /* =foo */ + } else if (*str == Equals && isset(EQUALS) && str[1] && str[1] != Inpar) { /* =foo */ char *expn = equalsubstr(str+1, assign, isset(NOMATCH)); if (expn) { *namptr = expn; @@ -1565,6 +1565,11 @@ check_colon_subscript(char *str, char **endp) if (!*str || ialpha(*str) || *str == '&') return NULL; + if (*str == ':') { + *endp = str; + return dupstring("0"); + } + *endp = parse_subscript(str, 0, ':'); if (!*endp) { /* No trailing colon? */ @@ -1575,8 +1580,10 @@ check_colon_subscript(char *str, char **endp) sav = **endp; **endp = '\0'; str = dupstring(str); - if (parsestr(&str)) + if (parsestr(&str)) { + **endp = sav; return NULL; + } singsub(&str); remnulargs(str); untokenize(str); @@ -1701,7 +1708,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, /* * This expressive name refers to the set of flags which * is applied to matching for #, %, / and their doubled variants: - * (M), (R), (B), (E), (N), (S). + * (M), (R), (B), (E), (N), (S), (*). */ int flags = 0; /* Value from (I) flag, used for ditto. */ @@ -1847,6 +1854,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * nested (P) flags. */ int fetch_needed; + /* + * If an array parameter is quoted but has :offset:length (as in + * "${array:off:len}"), we apply :off:len as array index before + * joining the array into a string (for compatibility with ksh/bash). + */ + int quoted_array_with_offset = 0; *s++ = '\0'; /* @@ -1923,6 +1936,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, case '@': nojoin = 2; /* nojoin = 2 means force */ break; + case '*': + case Star: + flags |= SUB_EGLOB; + break; case 'M': flags |= SUB_MATCH; break; @@ -1972,6 +1989,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, case 'n': sortit |= SORTIT_NUMERICALLY; break; + case '-': + case Dash: + sortit |= SORTIT_NUMERICALLY_SIGNED; + break; case 'a': sortit |= SORTIT_SOMEHOW; indord = 1; @@ -2244,9 +2265,31 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, break; default: - flagerr: - zerr("error in flags"); - return NULL; + flagerr: + { + /* + * We're trying to output the string that failed to + * parse and the offset of the parse error within that. + * + * The string is *str. It hasn't been changed since + * entry to this function, I think, except that the + * first non-variable-declaration line in this function + * (currently the 238th line in this function) + * writes a NUL to the first place in *str, so we'll + * compensate by outputting the dollar sign manually. + */ + char *str_copy_for_output = dupstring(*str + 1); + + /* + * Convert to a 1-based offset, because the shell + * language is 1-based by default. + */ + zlong offset = s - *str + 1; + + untokenize(str_copy_for_output); + zerr("error in flags near position %z in '$%s'", offset, str_copy_for_output); + return NULL; + } } } s++; @@ -2526,7 +2569,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * Handle the (t) flag: value now becomes the type * information for the parameter. */ - if (v && v->pm && !(v->pm->node.flags & PM_UNSET)) { + if (v && v->pm && ((v->pm->node.flags & PM_DECLARED) || + !(v->pm->node.flags & PM_UNSET))) { int f = v->pm->node.flags; switch (PM_TYPE(f)) { @@ -2781,7 +2825,6 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } if (bct) { - noclosebrace: zerr("closing brace expected"); return NULL; } @@ -2800,7 +2843,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, c == '#' || c == Pound || c == '?' || c == Quest || c == '/')) { - + int eglob = isset(EXTENDEDGLOB); /* * Default index is 1 if no (I) or (I) gave zero. But * why don't we set the default explicitly at the start @@ -2822,9 +2865,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, char *ptr; /* * previous flags are irrelevant, except for (S) which - * indicates shortest substring; else look for longest. + * indicates shortest substring; else look for longest, + * and (*) which temporarily enables extended globbing. */ - flags = (flags & SUB_SUBSTR) ? 0 : SUB_LONG; + flags = ((flags & SUB_SUBSTR) ? 0 : SUB_LONG)|(flags & SUB_EGLOB); if ((c = *s) == '/') { /* doubled, so replace all occurrences */ flags |= SUB_GLOBAL; @@ -2937,9 +2981,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } break; case ':': - /* this must be `::=', unconditional assignment */ - if (*s != '=' && *s != Equals) - goto noclosebrace; + /* this could be either `::=', unconditional assignment + * or a ${name:offset:length} with an empty offset */ + if (*s != '=' && *s != Equals) { + s -= 1; + goto colonsubscript; + } vunset = 1; s++; /* Fall through */ @@ -3123,7 +3170,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, for (ap = aval; *ap; ap++) { untokenize(*ap); } + if (flags & SUB_EGLOB) + opts[EXTENDEDGLOB] = 1; getmatcharr(&aval, s, flags, flnum, replstr); + opts[EXTENDEDGLOB] = eglob; } else { if (vunset) { if (vunset > 0 && unset(UNSET)) { @@ -3138,7 +3188,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, copied = 1; untokenize(val); } + if (flags & SUB_EGLOB) + opts[EXTENDEDGLOB] = 1; getmatch(&val, s, flags, flnum, replstr); + opts[EXTENDEDGLOB] = eglob; } break; } @@ -3282,6 +3335,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * if there isn't a trailing modifier? Why don't we do this * e.g. when we handle the ${(t)...} flag? */ +colonsubscript: if (chkset) { val = dupstring(vunset ? "0" : "1"); isarr = 0; @@ -3314,20 +3368,31 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, return NULL; } if (*check_offset2) { + char *nextp; check_offset = check_colon_subscript(check_offset2 + 1, - &check_offset2); - if (*check_offset2 && *check_offset2 != ':') { - zerr("invalid length: %s", check_offset); - return NULL; - } + &nextp); if (check_offset) { + check_offset2 = nextp; + if (*check_offset2 && *check_offset2 != ':') { + zerr("invalid length: %s", check_offset); + return NULL; + } length = mathevali(check_offset); length_set = 1; if (errflag) return NULL; } } - if (isarr) { + /* + * We've got :OFFSET (and :LENGTH). + * If aval is non-NULL but isarr is 0, PARAM is (probably) + * an array but quoted like "${PARAM:OFFSET}". We apply + * :OFFSET as array index (as if it is not quoted). We will + * join them later (search for quoted_array_with_offset). + */ + if (aval && !isarr) + quoted_array_with_offset = 1; + if (isarr || quoted_array_with_offset) { int alen, count; char **srcptr, **dstptr, **newarr; @@ -3572,9 +3637,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * exception is that ${name:-word} and ${name:+word} will have already * done any requested splitting of the word value with quoting preserved. */ - if (ssub || spbreak || spsep || sep) { + if (ssub || spbreak || spsep || sep || quoted_array_with_offset) { int force_split = !ssub && (spbreak || spsep); - if (isarr) { + if (isarr || quoted_array_with_offset) { /* sep non-null here means F or j flag, force join */ if (nojoin == 0 || sep) { val = sepjoin(aval, sep, 1); @@ -4399,7 +4464,7 @@ modify(char **str, char **ptr, int inbrace) chabspath(©); break; case 'A': - chrealpath(©); + chrealpath(©, 'A', 1); break; case 'c': { @@ -4485,7 +4550,7 @@ modify(char **str, char **ptr, int inbrace) chabspath(str); break; case 'A': - chrealpath(str); + chrealpath(str, 'A', 1); break; case 'c': { |