summaryrefslogtreecommitdiff
path: root/Src/subst.c
diff options
context:
space:
mode:
authorAxel Beckert <abe@deuxchevaux.org>2022-04-11 00:17:48 +0200
committerAxel Beckert <abe@deuxchevaux.org>2022-04-11 00:17:48 +0200
commitb09f4483416c54c1782824633dfabaf2ec0265b6 (patch)
tree304bc82642862525ae680c7fbaa249663b10ad57 /Src/subst.c
parent12eb3e5356f2fc3351eed58ef1cef1b8fb83b504 (diff)
parent6e55c920503071e917619b8cb1a188cd35d772db (diff)
downloadzsh-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.c115
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(&copy);
break;
case 'A':
- chrealpath(&copy);
+ chrealpath(&copy, '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':
{