diff options
Diffstat (limited to 'Src/subst.c')
-rw-r--r-- | Src/subst.c | 131 |
1 files changed, 111 insertions, 20 deletions
diff --git a/Src/subst.c b/Src/subst.c index 021d23444..b7f8338c7 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -44,15 +44,23 @@ char nulstring[] = {Nularg, '\0'}; * - Brace expansion * - Tilde and equals substitution * - * PREFORK_* flags are defined in zsh.h + * "flag"s contains PREFORK_* flags, defined in zsh.h. + * + * "ret_flags" is used to return values from nested parameter + * substitions. It may be NULL in which case PREFORK_SUBEXP + * must not appear in flags; any return value from below + * will be discarded. */ /**/ mod_export void -prefork(LinkList list, int flags) +prefork(LinkList list, int flags, int *ret_flags) { LinkNode node, stop = 0; int keep = 0, asssub = (flags & PREFORK_TYPESET) && isset(KSHTYPESET); + int ret_flags_local = 0; + if (!ret_flags) + ret_flags = &ret_flags_local; /* will be discarded */ queue_signals(); for (node = firstnode(list); node; incnode(node)) { @@ -75,10 +83,8 @@ prefork(LinkList list, int flags) setdata(node, cptr); } if (!(node = stringsubst(list, node, - flags & (PREFORK_SINGLE|PREFORK_SPLIT| - PREFORK_SHWORDSPLIT| - PREFORK_NOSHWORDSPLIT), - asssub))) { + flags & ~(PREFORK_TYPESET|PREFORK_ASSIGN), + ret_flags, asssub))) { unqueue_signals(); return; } @@ -149,7 +155,8 @@ stringsubstquote(char *strstart, char **pstrdpos) /**/ static LinkNode -stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub) +stringsubst(LinkList list, LinkNode node, int pf_flags, int *ret_flags, + int asssub) { int qt; char *str3 = (char *)getdata(node); @@ -235,7 +242,8 @@ stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub) pf_flags |= PREFORK_SHWORDSPLIT; node = paramsubst( list, node, &str, qt, - pf_flags & (PREFORK_SINGLE|PREFORK_SHWORDSPLIT)); + pf_flags & (PREFORK_SINGLE|PREFORK_SHWORDSPLIT| + PREFORK_SUBEXP), ret_flags); if (errflag || !node) return NULL; str3 = (char *)getdata(node); @@ -413,7 +421,7 @@ singsub(char **s) init_list1(foo, *s); - prefork(&foo, PREFORK_SINGLE); + prefork(&foo, PREFORK_SINGLE, NULL); if (errflag) return; *s = (char *) ugetnode(&foo); @@ -430,11 +438,15 @@ singsub(char **s) * set to 1. Otherwise, *isarr is set to 0, and the result is put into *s, * with any necessary joining of multiple elements using sep (which can be * NULL to use IFS). The return value is true iff the expansion resulted - * in an empty list. */ + * in an empty list. + * + * *ms_flags is set to bits in the enum above as neeed. + */ /**/ static int -multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep) +multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep, + int *ms_flags) { int l; char **r, **p, *x = *s; @@ -450,6 +462,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep) l++; if (!iwsep(STOUC(c))) break; + *ms_flags |= MULTSUB_WS_AT_START; } } @@ -481,8 +494,10 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep) if (!WC_ZISTYPE(c, ISEP)) break; } - if (!*x) + if (!*x) { + *ms_flags |= MULTSUB_WS_AT_END; break; + } insertlinknode(&foo, n, (void *)x), incnode(n); } } @@ -509,7 +524,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep) } } - prefork(&foo, pf_flags); + prefork(&foo, pf_flags, ms_flags); if (errflag) { if (isarr) *isarr = 0; @@ -1494,7 +1509,8 @@ check_colon_subscript(char *str, char **endp) /**/ static LinkNode -paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) +paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, + int *ret_flags) { char *aptr = *str, c, cc; char *s = aptr, *fstr, *idbeg, *idend, *ostr = (char *) getdata(n); @@ -1717,6 +1733,20 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) * This is for compatibility. */ int horrible_offset_hack = 0; + /* + * Signal back from multsub: with something like + * x${:- $foo} + * with word-splitting active we need to split on that leading + * whitespace. However, if there's no "x" the whitespace is + * simply removed. + */ + int ms_flags = 0; + /* + * We need to do an extra fetch to honour the (P) flag. + * Complicated by the use of subexpressions that may have + * nested (P) flags. + */ + int fetch_needed; *s++ = '\0'; /* @@ -2265,7 +2295,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) * remove the aspar test and extract a value from an array, if * necessary, when we handle (P) lower down. */ - if (multsub(&val, 0, (aspar ? NULL : &aval), &isarr, NULL) && quoted) { + if (multsub(&val, PREFORK_SUBEXP, (aspar ? NULL : &aval), &isarr, NULL, + &ms_flags) && quoted) { /* Empty quoted string --- treat as null string, not elided */ isarr = -1; aval = (char **) hcalloc(sizeof(char *)); @@ -2279,8 +2310,39 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) */ while (inull(*s)) s++; + if (ms_flags & MULTSUB_PARAM_NAME) { + /* + * Downbelow has told us this is a parameter name, e.g. + * ${${(P)name}...}. We're going to behave as if + * we have exactly that name followed by the rest of + * the parameter for subscripting etc. + * + * See below for where we set the flag in the nested + * substitution. + */ + if (isarr) { + if (aval[0] && aval[1]) { + zerr("parameter name reference used with array"); + return NULL; + } + val = aval[0]; + isarr = 0; + } + s = dyncat(val, s); + /* Now behave po-faced as if it was always like that... */ + subexp = 0; + /* + * If this is a (P) (first test) and at the top level + * (second test) we can't rely on the caller fetching + * the result from the pending aspar. So do it below. + */ + fetch_needed = aspar && !(pf_flags & PREFORK_SUBEXP); + } else + fetch_needed = 0; /* any initial aspar fetch already done */ v = (Value) NULL; - } else if (aspar) { + } else + fetch_needed = aspar; /* aspar fetch still needed */ + if (fetch_needed) { /* * No subexpression, but in any case the value is going * to give us the name of a parameter on which we do @@ -2296,6 +2358,17 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) } else vunset = 1; } + if (aspar && (pf_flags & PREFORK_SUBEXP)) { + /* + * This is the inner handling for the case referred to above + * where we have something like ${${(P)name}...}. + * + * Treat this as as a normal value here; all transformations on + * result are in outer instance. + */ + aspar = 0; + *ret_flags |= MULTSUB_PARAM_NAME; + } /* * We need to retrieve a value either if we haven't already * got it from a subexpression, or if the processing so @@ -2736,7 +2809,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) split_flags = PREFORK_NOSHWORDSPLIT; } multsub(&val, split_flags, (aspar ? NULL : &aval), - &isarr, NULL); + &isarr, NULL, &ms_flags); copied = 1; spbreak = 0; /* Leave globsubst on if forced */ @@ -2765,13 +2838,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) * behavior on caller choice of PREFORK_SHWORDSPLIT. */ multsub(&val, spbreak ? PREFORK_SINGLE : PREFORK_NOSHWORDSPLIT, - NULL, &isarr, NULL); + NULL, &isarr, NULL, &ms_flags); } else { if (spbreak) split_flags = PREFORK_SPLIT|PREFORK_SHWORDSPLIT; else split_flags = PREFORK_NOSHWORDSPLIT; - multsub(&val, split_flags, &aval, &isarr, NULL); + multsub(&val, split_flags, &aval, &isarr, NULL, + &ms_flags); spbreak = 0; } if (arrasg) { @@ -3303,6 +3377,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) } if (haserr || errflag) return NULL; + ms_flags = 0; } /* * This handles taking a length with ${#foo} and variations. @@ -3341,6 +3416,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) sprintf(buf, "%ld", len); val = dupstring(buf); isarr = 0; + ms_flags = 0; } /* At this point we make sure that our arrayness has affected the * arrayness of the linked list. Then, we can turn our value into @@ -3370,6 +3446,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) if (isarr) { val = sepjoin(aval, sep, 1); isarr = 0; + ms_flags = 0; } if (!ssub && (spbreak || spsep)) { aval = sepsplit(val, spsep, 0, 1); @@ -3649,6 +3726,20 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) * equivalent and only locally decide if we need to treat it * as a scalar.) */ + + /* + * If a multsub result had whitespace at the start and we're + * splitting and there's a previous string, now's the time to do so. + */ + if ((ms_flags & MULTSUB_WS_AT_START) && aptr > ostr) { + insertlinknode(l, n, dupstrpfx(ostr, aptr - ostr)), incnode(n); + ostr = aptr; + } + /* Likewise at the end */ + if ((ms_flags & MULTSUB_WS_AT_END) && *fstr) { + insertlinknode(l, n, dupstring(fstr)); /* appended, no incnode */ + *fstr = '\0'; + } if (isarr) { char *x; char *y; @@ -3727,7 +3818,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) *--fstr = Marker; init_list1(tl, fstr); - if (!eval && !stringsubst(&tl, firstnode(&tl), ssub, 0)) + if (!eval && !stringsubst(&tl, firstnode(&tl), ssub, ret_flags, 0)) return NULL; *str = aptr; tn = firstnode(&tl); |