summaryrefslogtreecommitdiff
path: root/Src/subst.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/subst.c')
-rw-r--r--Src/subst.c131
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);