summaryrefslogtreecommitdiff
path: root/Src/subst.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/subst.c')
-rw-r--r--Src/subst.c122
1 files changed, 90 insertions, 32 deletions
diff --git a/Src/subst.c b/Src/subst.c
index bb1dd8939..06d2c9ea9 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -145,8 +145,12 @@ stringsubstquote(char *strstart, char **pstrdpos)
strret = dyncat(strstart, strsub);
} else if (strdpos[len])
strret = dyncat(strsub, strdpos + len);
- else
+ else if (*strsub)
strret = strsub;
+ else {
+ /* This ensures a $'' doesn't get elided. */
+ strret = dupstring(nulstring);
+ }
*pstrdpos = strret + (strdpos - strstart) + strlen(strsub);
@@ -407,7 +411,9 @@ globlist(LinkList list, int nountok)
next = nextnode(node);
zglob(list, node, nountok);
}
- if (badcshglob == 1)
+ if (noerrs)
+ badcshglob = 0;
+ else if (badcshglob == 1)
zerr("no match");
}
@@ -623,7 +629,7 @@ equalsubstr(char *str, int assign, int nomatch)
cmdstr = dupstrpfx(str, pp-str);
untokenize(cmdstr);
remnulargs(cmdstr);
- if (!(cnam = findcmd(cmdstr, 1))) {
+ if (!(cnam = findcmd(cmdstr, 1, 0))) {
if (nomatch)
zerr("%s not found", cmdstr);
return NULL;
@@ -680,19 +686,19 @@ filesubstr(char **namptr, int assign)
*namptr = dyncat(ds, ptr);
return 1;
} else if ((ptr = itype_end(str+1, IUSER, 0)) != str+1) { /* ~foo */
- char *hom, save;
+ char *untok, *hom;
- save = *ptr;
- if (!isend(save))
+ if (!isend(*ptr))
return 0;
- *ptr = 0;
- if (!(hom = getnameddir(++str))) {
- if (isset(NOMATCH))
- zerr("no such user or named directory: %s", str);
- *ptr = save;
+ untok = dupstring(++str);
+ untok[ptr-str] = 0;
+ untokenize(untok);
+
+ if (!(hom = getnameddir(untok))) {
+ if (isset(NOMATCH) && isset(EXECOPT))
+ zerr("no such user or named directory: %s", untok);
return 0;
}
- *ptr = save;
*namptr = dyncat(hom, ptr);
return 1;
}
@@ -2364,7 +2370,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* 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
+ * Treat this as a normal value here; all transformations on
* result are in outer instance.
*/
aspar = 0;
@@ -2544,12 +2550,19 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* necessary joining of arrays until this point
* to avoid the multsub() horror.
*/
- int tmplen = arrlen(v->pm->gsu.a->getfn(v->pm));
- if (v->start < 0)
+ /* arrlen() is expensive, so only compute it if needed. */
+ int tmplen = -1;
+
+ if (v->start < 0) {
+ tmplen = arrlen(v->pm->gsu.a->getfn(v->pm));
v->start += tmplen + ((v->flags & VALFLAG_INV) ? 1 : 0);
- if (!(v->flags & VALFLAG_INV) &&
- (v->start >= tmplen || v->start < 0))
+ }
+ if (!(v->flags & VALFLAG_INV))
+ if (v->start < 0 ||
+ (tmplen != -1
+ ? v->start >= tmplen
+ : arrlen_le(v->pm->gsu.a->getfn(v->pm), v->start)))
vunset = 1;
}
if (!vunset) {
@@ -2886,6 +2899,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
aval = paramvalarr(pm->gsu.h->getfn(pm), hkeys|hvals);
} else
setaparam(idbeg, a);
+ isarr = 1;
} else {
untokenize(val);
setsparam(idbeg, ztrdup(val));
@@ -2910,6 +2924,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
if (isset(EXECOPT)) {
*idend = '\0';
zerr("%s: %s", idbeg, *s ? s : "parameter not set");
+ /*
+ * In interactive shell we need to return to
+ * top-level prompt --- don't clear this error
+ * after handling a command as we do with
+ * most errors.
+ */
+ errflag |= ERRFLAG_HARD;
if (!interact) {
if (mypid == getpid()) {
/*
@@ -3443,13 +3464,24 @@ 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 && isarr >= 0) || spsep || sep) {
+ if (ssub || spbreak || spsep || sep) {
+ int force_split = !ssub && (spbreak || spsep);
if (isarr) {
- val = sepjoin(aval, sep, 1);
- isarr = 0;
- ms_flags = 0;
+ /* sep non-null here means F or j flag, force join */
+ if (nojoin == 0 || sep) {
+ val = sepjoin(aval, sep, 1);
+ isarr = 0;
+ ms_flags = 0;
+ } else if (force_split && (spsep || nojoin == 2)) {
+ /* Hack to simulate splitting individual elements:
+ * forced joining as previously determined, or
+ * join on what we later use to forcibly split
+ */
+ val = sepjoin(aval, (nojoin == 1 ? NULL : spsep), 1);
+ isarr = 0;
+ }
}
- if (!ssub && (spbreak || spsep)) {
+ if (force_split && !isarr) {
aval = sepsplit(val, spsep, 0, 1);
if (!aval || !aval[0])
val = dupstring("");
@@ -3516,7 +3548,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
}
/*
* TODO: It would be really quite nice to abstract the
- * isarr and !issarr code into a function which gets
+ * isarr and !isarr code into a function which gets
* passed a pointer to a function with the effect of
* the promptexpand bit. Then we could use this for
* a lot of stuff and bury val/aval/isarr inside a structure
@@ -3592,20 +3624,20 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
char *tmp;
for (; *ap; ap++) {
- tmp = quotestring(*ap, NULL, quotetype);
+ tmp = quotestring(*ap, quotetype);
sl = strlen(tmp);
*ap = (char *) zhalloc(pre + sl + post + 1);
strcpy((*ap) + pre, tmp);
if (pre)
ap[0][pre - 1] = ap[0][pre + sl] =
(quotetype != QT_DOUBLE ? '\'' : '"');
- ap[0][pre + sl + 1] = '\0';
+ ap[0][pre + sl + post] = '\0';
if (quotetype == QT_DOLLARS)
ap[0][0] = '$';
}
} else
for (; *ap; ap++)
- *ap = quotestring(*ap, NULL, QT_BACKSLASH_SHOWNULL);
+ *ap = quotestring(*ap, QT_BACKSLASH_SHOWNULL);
} else {
int one = noerrs, oef = errflag, haserr = 0;
@@ -3635,18 +3667,18 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
} else if (quotetype > QT_BACKSLASH) {
int sl;
char *tmp;
- tmp = quotestring(val, NULL, quotetype);
+ tmp = quotestring(val, quotetype);
sl = strlen(tmp);
- val = (char *) zhalloc(pre + sl + 2);
+ val = (char *) zhalloc(pre + sl + post + 1);
strcpy(val + pre, tmp);
if (pre)
val[pre - 1] = val[pre + sl] =
(quotetype != QT_DOUBLE ? '\'' : '"');
- val[pre + sl + 1] = '\0';
+ val[pre + sl + post] = '\0';
if (quotetype == QT_DOLLARS)
val[0] = '$';
} else
- val = quotestring(val, NULL, QT_BACKSLASH_SHOWNULL);
+ val = quotestring(val, QT_BACKSLASH_SHOWNULL);
} else {
int one = noerrs, oef = errflag, haserr;
@@ -4011,6 +4043,19 @@ arithsubst(char *a, char **bptr, char *rest)
return t;
}
+/* This function implements colon modifiers.
+ *
+ * STR is an in/out parameter. On entry it is the string (e.g., path)
+ * to modified. On return it is the modified path.
+ *
+ * PTR is an in/out parameter. On entry it contains the string of colon
+ * modifiers. On return it points past the last recognised modifier.
+ *
+ * Example:
+ * ENTRY: *str is "." *ptr is ":AN"
+ * RETURN: *str is "/home/foobar" (equal to $PWD) *ptr points to the "N"
+ */
+
/**/
void
modify(char **str, char **ptr)
@@ -4046,6 +4091,7 @@ modify(char **str, char **ptr)
case 'u':
case 'q':
case 'Q':
+ case 'P':
c = **ptr;
break;
@@ -4237,7 +4283,7 @@ modify(char **str, char **ptr)
subst(&copy, hsubl, hsubr, gbal);
break;
case 'q':
- copy = quotestring(copy, NULL, QT_BACKSLASH_SHOWNULL);
+ copy = quotestring(copy, QT_BACKSLASH_SHOWNULL);
break;
case 'Q':
{
@@ -4252,6 +4298,12 @@ modify(char **str, char **ptr)
untokenize(copy);
}
break;
+ case 'P':
+ if (*copy != '/') {
+ copy = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", copy);
+ }
+ copy = xsymlink(copy, 1);
+ break;
}
tc = *tt;
*tt = '\0';
@@ -4313,7 +4365,7 @@ modify(char **str, char **ptr)
subst(str, hsubl, hsubr, gbal);
break;
case 'q':
- *str = quotestring(*str, NULL, QT_BACKSLASH);
+ *str = quotestring(*str, QT_BACKSLASH);
break;
case 'Q':
{
@@ -4328,6 +4380,12 @@ modify(char **str, char **ptr)
untokenize(*str);
}
break;
+ case 'P':
+ if (**str != '/') {
+ *str = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", *str);
+ }
+ *str = xsymlink(*str, 1);
+ break;
}
}
if (rec < 0) {