From 7d1877da4e6fabde8f0b5eb2f9a9f03d83e6c28d Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 27 Aug 2017 20:28:38 +0100 Subject: 41608 (plus tests): restore ERR_EXIT before function. There was an exception to the usual ERR_EXIT pattern that causes problems when executing a function in an else branch. It seems the exception is no longer needed as the regression tests pass without it. --- Src/exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/Src/exec.c b/Src/exec.c index cd99733f1..76a6bb1a4 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3025,7 +3025,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, preargs = NULL; /* if we get this far, it is OK to pay attention to lastval again */ - if ((noerrexit & NOERREXIT_UNTIL_EXEC) && !is_shfunc) + if (noerrexit & NOERREXIT_UNTIL_EXEC) noerrexit = 0; /* Do prefork substitutions. -- cgit v1.2.3 From c9df6bc8d46415e270273777c80025948762e897 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 10 Sep 2017 18:02:52 +0100 Subject: 41662: exec -a arguments weren't sanitised --- ChangeLog | 8 ++++++++ Src/exec.c | 3 +++ Test/A01grammar.ztst | 6 ++++++ 3 files changed, 17 insertions(+) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 39f334370..85fa84de5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2017-09-10 Peter Stephenson + + * 41622: Src/exec., Test/A01grammar.ztst: exec -a arguments + weren't sanitised. + + * 41661: Src/subst.c, Test/D04parameter.ztst: need to remove + tokens before passing to bufferwords() for (z). + 2017-09-09 Jun-ichi Takimoto * 41628 (plus 41645 by Oliver): Completion/Unix/Command/_ps: diff --git a/Src/exec.c b/Src/exec.c index 76a6bb1a4..e2432fda4 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3007,6 +3007,9 @@ execcmd_exec(Estate state, Execcmd_params eparams, } if (exec_argv0) { char *str, *s; + exec_argv0 = dupstring(exec_argv0); + remnulargs(exec_argv0); + untokenize(exec_argv0); size_t sz = strlen(exec_argv0); str = s = zalloc(5 + 1 + sz + 1); strcpy(s, "ARGV0="); diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index 0302c9624..5e7d6acd8 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -103,6 +103,12 @@ 0:`exec' with -a option, no space >/bin/SPLOOSH + (exec -a foo* $ZTST_testdir/../Src/zsh -fc 'print -r -- ${(V)0}') + (exec -a "" $ZTST_testdir/../Src/zsh -fc 'print -r -- ${(V)0}') +0:rationalisation of arguments to exec -a +>foo* +> + ( opts=(-a /bin/WHOOOSH) exec $opts /bin/sh -c 'echo $0' -- cgit v1.2.3 From 54b395844030342213cacba4c569a6c5e6781c46 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 13 Sep 2017 20:54:00 +0100 Subject: First go at var=([key]=value) syntax. Works for both normal and typeset case, also var+=... Still to do: allow to be mixed with straight array assignment, improve typeset -p, implement [key]+=value. --- ChangeLog | 8 +++++ Doc/Zsh/params.yo | 27 +++++++++++++++- Src/builtin.c | 70 +++++++++++++++++++++++++++------------- Src/exec.c | 86 ++++++++++++++++++++++++++++++++++++++++++++------ Src/params.c | 66 ++++++++++++++++++++++++++++++++++++++ Src/zsh.h | 17 ++++++++-- Test/B02typeset.ztst | 55 ++++++++++++++++++++++++++++++++ Test/D04parameter.ztst | 68 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 363 insertions(+), 34 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 3d0982b24..15e331c90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2017-09-13 Peter Stephenson + + * 41698: Doc/Zsh/params.yo, Src/builtin.c, Src/exec.c, + Src/params.c, Src/zsh.h, Test/B02typeset.ztst, + Test/D04parameter.ztst: add ([key]=val) assignment syntax. + Still to do: allow to mix with other syntax, improve typeset -p, + implement [key]+=value. + 2017-09-13 Peter Stephenson * 41688: Src/jobs.c: "wait" for all active jobs should ignore diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 817496b8a..31266a7d0 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -91,13 +91,36 @@ cindex(array assignment) ifzman() indent(tt(set -A) var(name) var(value) ...) indent(var(name)tt(=LPAR())var(value) ...tt(RPAR())) +indent(var(name)tt(=LPAR())tt([)var(key)tt(]=)var(value) ...tt(RPAR())) + +In the third form, var(key) is an expression that will be evaluated in +arithmetic context (in its simplest form, an integer) that gives the +index of the element to be assigned with var(value). In this form any +elements not explicitly mentioned that come before the largest index to +which a value is assigned will be assigned an empty string. The indices +may be in any order. Note that this syntax is strict: tt([) and tt(]=) must +not be quoted, while var(key) may not consist of the unquoted string +tt(]=), but is otherwise treated as a simple string. Furthermore, all +elements must match this form or an error is genereted; likewise, if the +first entry does not match this form any later entry that does is taken +as a simple value rather than a key / value pair. The enhanced forms of +subscript expression that may be used when directly subscripting a +variable name, described in the section Array Subscripts below, are not +available. Both var(key) and var(value) undergo all forms of expansion +allowed for single word substitutions (this does not include filename +generation). If no parameter var(name) exists, an ordinary array parameter is created. If the parameter var(name) exists and is a scalar, it is replaced by a new array. To append to an array without changing the existing values, use -the syntax: +one of the following: ifzman() indent(var(name)tt(+=LPAR())var(value) ...tt(RPAR())) +indent(var(name)tt(+=LPAR())tt([)var(key)tt(]=)var(value) ...tt(RPAR())) + +In the second form var(key) may specify an existing index as well as an +index off the end of the old array; any existing value is overwritten by +var(value). Within the parentheses on the right hand side of either form of the assignment, newlines and semicolons are treated the same as white space, @@ -118,12 +141,14 @@ is interpreted as alternating keys and values: ifzman() indent(tt(set -A) var(name) var(key) var(value) ...) indent(var(name)tt(=LPAR())var(key) var(value) ...tt(RPAR())) +indent(var(name)tt(=LPAR())tt([)var(key)tt(]=)var(value) ...tt(RPAR())) Every var(key) must have a var(value) in this case. Note that this assigns to the entire array, deleting any elements that do not appear in the list. The append syntax may also be used with an associative array: ifzman() indent(var(name)tt(+=LPAR())var(key) var(value) ...tt(RPAR())) +indent(var(name)tt(+=LPAR())tt([)var(key)tt(]=)var(value) ...tt(RPAR())) This adds a new key/value pair if the key is not already present, and replaces the value for the existing key if it is. diff --git a/Src/builtin.c b/Src/builtin.c index 0c2a62a88..f5ccf52f8 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -450,15 +450,35 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn) Asgment asg = (Asgment)node; fputc(' ', xtrerr); quotedzputs(asg->name, xtrerr); - if (asg->is_array) { - LinkNode arrnode; + if (asg->flags & ASG_ARRAY) { fprintf(xtrerr, "=("); if (asg->value.array) { - for (arrnode = firstnode(asg->value.array); - arrnode; - incnode(arrnode)) { - fputc(' ', xtrerr); - quotedzputs((char *)getdata(arrnode), xtrerr); + if (asg->flags & ASG_KEY_VALUE) { + LinkNode keynode, valnode; + keynode = firstnode(asg->value.array); + for (;;) { + if (!keynode) + break; + valnode = nextnode(keynode); + if (!valnode) + break; + fputc('[', xtrerr); + quotedzputs((char *)getdata(keynode), + xtrerr); + fprintf(stderr, "]="); + quotedzputs((char *)getdata(valnode), + xtrerr); + keynode = nextnode(valnode); + } + } else { + LinkNode arrnode; + for (arrnode = firstnode(asg->value.array); + arrnode; + incnode(arrnode)) { + fputc(' ', xtrerr); + quotedzputs((char *)getdata(arrnode), + xtrerr); + } } } fprintf(xtrerr, " )"); @@ -1519,7 +1539,7 @@ bin_fc(char *nam, char **argv, Options ops, int func) asgl = a; } a->name = *argv; - a->is_array = 0; + a->flags = 0; a->value.scalar = s; a->node.next = a->node.prev = NULL; argv++; @@ -1910,7 +1930,7 @@ getasg(char ***argvp, LinkList assigns) return NULL; } asg.name = s; - asg.is_array = 0; + asg.flags = 0; /* search for `=' */ for (; *s && *s != '='; s++); @@ -2171,7 +2191,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), * ii. we are creating a new local parameter */ if (usepm) { - if (asg->is_array ? + if ((asg->flags & ASG_ARRAY) ? !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) : (asg->value.scalar && (PM_TYPE(pm->node.flags & (PM_ARRAY|PM_HASHED))))) { @@ -2241,10 +2261,11 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), if (asg->value.scalar && !(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0))) return NULL; - } else if (asg->is_array) { + } else if (asg->flags & ASG_ARRAY) { + int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; if (!(pm = assignaparam(pname, asg->value.array ? zlinklist2array(asg->value.array) : - mkarray(NULL), 0))) + mkarray(NULL), flags))) return NULL; } if (errflag) @@ -2255,7 +2276,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), return pm; } - if (asg->is_array ? + if ((asg->flags & ASG_ARRAY) ? !(on & (PM_ARRAY|PM_HASHED)) : (asg->value.scalar && (on & (PM_ARRAY|PM_HASHED)))) { zerrnam(cname, "%s: inconsistent type for assignment", pname); @@ -2287,7 +2308,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), */ if (!ASG_VALUEP(asg) && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED))) { asg->value.scalar = dupstring(getsparam(pname)); - asg->is_array = 0; + asg->flags = 0; } /* pname may point to pm->nam which is about to disappear */ pname = dupstring(pname); @@ -2396,13 +2417,14 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), ztrdup(asg->value.scalar ? asg->value.scalar : ""), 0))) return NULL; dont_set = 1; - asg->is_array = 0; + asg->flags = 0; keeplocal = 0; on = pm->node.flags; } else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) { + int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; if (!(pm = assignaparam(pname, asg->value.array ? zlinklist2array(asg->value.array) : - mkarray(NULL), 0))) + mkarray(NULL), flags))) return NULL; dont_set = 1; keeplocal = 0; @@ -2479,6 +2501,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), Param ipm = pm; if (pm->node.flags & (PM_ARRAY|PM_HASHED)) { char **arrayval; + int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; if (!ASG_ARRAYP(asg)) { /* * Attempt to assign a scalar value to an array. @@ -2497,7 +2520,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), arrayval = zlinklist2array(asg->value.array); else arrayval = mkarray(NULL); - if (!(pm=assignaparam(pname, arrayval, 0))) + if (!(pm=assignaparam(pname, arrayval, flags))) return NULL; } else { DPUTS(ASG_ARRAYP(asg), "BUG: inconsistent array value for scalar"); @@ -2750,13 +2773,15 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) * Already tied in the fashion requested. */ struct tieddata *tdp = (struct tieddata*)pm->u.data; + int flags = (asg->flags & ASG_KEY_VALUE) ? + ASSPM_KEY_VALUE : 0; /* Update join character */ tdp->joinchar = joinchar; if (asg0.value.scalar) assignsparam(asg0.name, ztrdup(asg0.value.scalar), 0); else if (asg->value.array) assignaparam( - asg->name, zlinklist2array(asg->value.array), 0); + asg->name, zlinklist2array(asg->value.array),flags); return 0; } else { zwarnnam(name, "can't tie already tied scalar: %s", @@ -2778,7 +2803,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) * to be exported properly. */ asg2.name = asg->name; - asg2.is_array = 0; + asg2.flags = 0; asg2.value.array = (LinkList)0; if (!(apm=typeset_single(name, asg->name, (Param)paramtab->getnode(paramtab, @@ -2816,9 +2841,10 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) if (apm->ename) zsfree(apm->ename); apm->ename = ztrdup(asg0.name); - if (asg->value.array) - assignaparam(asg->name, zlinklist2array(asg->value.array), 0); - else if (oldval) + if (asg->value.array) { + int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; + assignaparam(asg->name, zlinklist2array(asg->value.array), flags); + } else if (oldval) assignsparam(asg0.name, oldval, 0); unqueue_signals(); diff --git a/Src/exec.c b/Src/exec.c index e2432fda4..d1367660c 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2389,6 +2389,60 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag, } } +/* Check for array assignent with entries like [key]=val. + * + * All entries or none must match this form, else error and return 0. + * + * Convert list to alternate key / val form, perform + * appropriate substitution, and return 1 if found. + * + * Caller to check errflag. + */ + +/**/ +static int +keyvalpairarray(LinkList vl, int htok) +{ + char *start, *end, *dat; + LinkNode ve, next; + + if (vl && + (ve = firstnode(vl)) && + (start = (char *)getdata(ve)) && + start[0] == Inbrack && + (end = strchr(start+1, Outbrack)) && + end[1] == Equals) { + for (;;) { + *end = '\0'; + next = nextnode(ve); + + dat = start + 1; + if (htok) + singsub(&dat); + untokenize(dat); + setdata(ve, dat); + dat = end + 2; + if (htok) + singsub(&dat); + untokenize(dat); + insertlinknode(vl, ve, dat); + ve = next; + if (!ve) + break; + if (!(start = (char *)getdata(ve)) || + start[0] != Inbrack || + !(end = strchr(start+1, Outbrack)) || + end[1] != Equals) { + zerr("bad array element, expected [key]=value: %s", + start); + return 0; + } + } + return 1; + } + return 0; +} + /**/ static void addvars(Estate state, Wordcode pc, int addflags) @@ -2428,8 +2482,17 @@ addvars(Estate state, Wordcode pc, int addflags) if ((isstr = (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR))) { init_list1(svl, ecgetstr(state, EC_DUPTOK, &htok)); vl = &svl; - } else + } else { vl = ecgetlist(state, WC_ASSIGN_NUM(ac), EC_DUPTOK, &htok); + if (keyvalpairarray(vl, htok)) { + myflags |= ASSPM_KEY_VALUE; + htok = 0; + } + if (errflag) { + state->pc = opc; + return; + } + } if (vl && htok) { prefork(vl, (isstr ? (PREFORK_SINGLE|PREFORK_ASSIGN) : @@ -3914,7 +3977,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, while ((data = ugetnode(&svl))) { char *ptr; asg = (Asgment)zhalloc(sizeof(struct asgment)); - asg->is_array = 0; + asg->flags = 0; if ((ptr = strchr(data, '='))) { *ptr++ = '\0'; asg->name = data; @@ -3936,7 +3999,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, asg->name = name; if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR) { char *val = ecgetstr(state, EC_DUPTOK, &htok); - asg->is_array = 0; + asg->flags = 0; if (WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) { /* Fake assignment, no value */ asg->value.scalar = NULL; @@ -3961,18 +4024,23 @@ execcmd_exec(Estate state, Execcmd_params eparams, asg->value.scalar = val; } } else { - asg->is_array = 1; + asg->flags = ASG_ARRAY; asg->value.array = ecgetlist(state, WC_ASSIGN_NUM(ac), EC_DUPTOK, &htok); if (asg->value.array) { - prefork(asg->value.array, PREFORK_ASSIGN, NULL); - if (errflag) { - state->pc = opc; - break; + if (keyvalpairarray(asg->value.array, 1)) + asg->flags |= ASG_KEY_VALUE; + else if (!errflag) { + prefork(asg->value.array, PREFORK_ASSIGN, + NULL); + if (errflag) { + state->pc = opc; + break; + } + globlist(asg->value.array, 0); } - globlist(asg->value.array, 0); if (errflag) { state->pc = opc; break; diff --git a/Src/params.c b/Src/params.c index 6fbee880c..e0aaaf620 100644 --- a/Src/params.c +++ b/Src/params.c @@ -3185,6 +3185,72 @@ assignaparam(char *s, char **val, int flags) if (flags & ASSPM_WARN) check_warn_pm(v->pm, "array", created, may_warn_about_nested_vars); + + if ((flags & ASSPM_KEY_VALUE) && (PM_TYPE(v->pm->node.flags) & PM_ARRAY)) { + /* + * This is an ordinary array with key / value pairs. + */ + int maxlen, origlen; + char **aptr, **fullval; + zlong *subscripts = (zlong *)zhalloc(arrlen(val) * sizeof(zlong)); + zlong *iptr = subscripts; + if (flags & ASSPM_AUGMENT) { + maxlen = origlen = arrlen(v->pm->gsu.a->getfn(v->pm)); + } else { + maxlen = origlen = 0; + } + for (aptr = val; *aptr && aptr[1]; aptr += 2) { + *iptr = mathevali(*aptr); + if (*iptr < 0 || + (!isset(KSHARRAYS) && *iptr == 0)) { + unqueue_signals(); + zerr("bad subscript for direct array assignment: %s", *aptr); + return NULL; + } + if (!isset(KSHARRAYS)) + --*iptr; + if (*iptr + 1 > maxlen) + maxlen = *iptr + 1; + ++iptr; + } + fullval = zshcalloc((maxlen+1) * sizeof(char *)); + if (!fullval) { + zerr("array too large"); + return NULL; + } + fullval[maxlen] = NULL; + if (flags & ASSPM_AUGMENT) { + char **srcptr = v->pm->gsu.a->getfn(v->pm); + for (aptr = fullval; aptr <= fullval + origlen; aptr++) { + *aptr = ztrdup(*srcptr); + srcptr++; + } + } + iptr = subscripts; + for (aptr = val; *aptr && aptr[1]; aptr += 2) { + zsfree(*aptr); + fullval[*iptr] = aptr[1]; + ++iptr; + } + if (*aptr) { /* Shouldn't be possible */ + DPUTS(1, "Extra element in key / value array"); + zsfree(*aptr); + } + free(val); + for (aptr = fullval; aptr < fullval + maxlen; aptr++) { + /* + * Remember we don't have sparse arrays but and they're null + * terminated --- so any value we don't set has to be an + * empty string. + */ + if (!*aptr) + *aptr = ztrdup(""); + } + setarrvalue(v, fullval); + unqueue_signals(); + return v->pm; + } + if (flags & ASSPM_AUGMENT) { if (v->start == 0 && v->end == -1) { if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) { diff --git a/Src/zsh.h b/Src/zsh.h index 1e982a632..27642f239 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1217,17 +1217,25 @@ struct alias { struct asgment { struct linknode node; char *name; - int is_array; + int flags; union { char *scalar; LinkList array; } value; }; +/* Flags for flags element of asgment */ +enum { + /* Array value */ + ASG_ARRAY = 1, + /* Key / value array pair */ + ASG_KEY_VALUE = 2 +}; + /* * Assignment is array? */ -#define ASG_ARRAYP(asg) ((asg)->is_array) +#define ASG_ARRAYP(asg) ((asg)->flags & ASG_ARRAY) /* * Assignment has value? @@ -2060,6 +2068,11 @@ enum { ASSPM_WARN = (ASSPM_WARN_CREATE|ASSPM_WARN_NESTED), /* Import from environment, so exercise care evaluating value */ ASSPM_ENV_IMPORT = 1 << 3, + /* Array is key / value pairs. + * This is normal for associative arrays but variant behaviour for + * normal arrays. + */ + ASSPM_KEY_VALUE = 1 << 4 }; /* node for named directory hash table (nameddirtab) */ diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index b27bb4f6b..ae218047c 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -721,3 +721,58 @@ # 'date' did not run. >Status is printed, 1 *?*: failed to change user ID: * + + typeset -A keyvalhash=([one]=eins [two]=zwei) + keyvalhash+=([three]=drei) + for key in ${(ok)keyvalhash}; do + print $key $keyvalhash[$key] + done +0:[key]=val for hashes +>one eins +>three drei +>two zwei + + local keyvalarray=([1]=one [3]=three) + print -l "${keyvalarray[@]}" + keyvalarray+=([2]=two) + print -l "${keyvalarray[@]}" + local keyvalarray=([1]=one [3]=three) + print -l "${keyvalarray[@]}" +0:[key]=val for normal arrays +>one +> +>three +>one +>two +>three +>one +> +>three + + touch foo Xnot_globbedX + inkey="another key" val="another value" + typeset -A keyvalhash=([$(echo the key)]=$(echo the value) + [$inkey]=$val + [*]=?not_globbed?) + for key in ${(ok)keyvalhash}; do + print -l $key $keyvalhash[$key] + done + typeset -A keyvalhash=([$(echo the key)]=$(echo the value) + [$inkey]=$val + [*]=?not_globbed?) + for key in ${(ok)keyvalhash}; do + print -l $key $keyvalhash[$key] + done +0:Substitution in [key]=val syntax +>* +>?not_globbed? +>another key +>another value +>the key +>the value +>* +>?not_globbed? +>another key +>another value +>the key +>the value diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 8dbc1e8b8..367bca120 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -2207,3 +2207,71 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 0:(z) splitting with remaining tokens >foo-bar*thingy? + typeset -A keyvalhash + keyvalhash=([one]=eins [two]=zwei) + keyvalhash+=([three]=drei) + for key in ${(ok)keyvalhash}; do + print $key $keyvalhash[$key] + done +0:[key]=val for hashes +>one eins +>three drei +>two zwei + + local keyvalarray + keyvalarray=([1]=one [3]=three) + print -l "${keyvalarray[@]}" + keyvalarray+=([2]=two) + print -l "${keyvalarray[@]}" +0:[key]=val for normal arrays +>one +> +>three +>one +>two +>three + + typeset -A keyvalhash + touch foo Xnot_globbedX + key="another key" val="another value" + keyvalhash=([$(echo the key)]=$(echo the value) + [$key]=$val + [*]=?not_globbed?) + for key in ${(ok)keyvalhash}; do + print -l $key $keyvalhash[$key] + done +0:Substitution in [key]=val syntax +>* +>?not_globbed? +>another key +>another value +>the key +>the value + + local keyvalarray + keyvalarray=(1 2 3) + keyvalarray+=([5]=5 [7]=7) + keyvalarray+=([4]=4 [6]=6) + print $#keyvalarray + print $keyvalarray +0:append to normal array using [key]=val +>7 +>1 2 3 4 5 6 7 + + local -A keyvalhash + keyvalhash=(['1first element!']=first' 'value + ["2second element?"]=second" "value + [$'3third element#']=third$' 'value + [\4\f\o\u\r\t\h\ \e\l\e\m\e\n\t\\]=fourth\ value) + for key in ${(ok)keyvalhash}; do + print -rl -- $key $keyvalhash[$key] + done +0:quoting in [key]=value syntax +>1first element! +>first value +>2second element? +>second value +>3third element# +>third value +>4fourth element\ +>fourth value -- cgit v1.2.3 From d1914c5af484791ee5d8f20261c08cecf113d3a9 Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Wed, 20 Sep 2017 10:17:59 -0700 Subject: 41736: NO_INTERACTIVE_COMMENTS in $(...) --- ChangeLog | 2 ++ NEWS | 8 ++++++++ Src/exec.c | 7 ++++++- 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 302177f9b..34163e55f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2017-09-20 Barton E. Schaefer + * 41736: NEWS, Src/exec.c: NO_INTERACTIVE_COMMENTS in $(...) + * 41731: Doc/Zsh/params.yo: reformat and expand doc for 41698. 2017-09-18 Oliver Kiddle diff --git a/NEWS b/NEWS index 6847350ef..796a2c90d 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,14 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH Note also the list of incompatibilities in the README file. +Changes from 5.4 to 5.4.3 +------------------------- + +The effect of the NO_INTERACTIVE_COMMENTS option extends into $(...) and +`...` command substitutions when used on the command line. Previously, +comments were always recognized within command substitutions unless the +comment character "#" was disabled via reset of $histchars. + Changes from 5.3.1 to 5.4 ------------------------- diff --git a/Src/exec.c b/Src/exec.c index d1367660c..31edfab55 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4509,7 +4509,12 @@ getoutput(char *cmd, int qt) pid_t pid; char *s; - if (!(prog = parse_string(cmd, 0))) + int onc = nocomments; + nocomments = (interact && unset(INTERACTIVECOMMENTS)); + prog = parse_string(cmd, 0); + nocomments = onc; + + if (!prog) return NULL; if ((s = simple_redir_name(prog, REDIR_READ))) { -- cgit v1.2.3 From 85b0dd71335c8ee6d4925be6b590cbe643edf196 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sat, 23 Sep 2017 18:17:51 +0100 Subject: Updates for ksh array element syntax. Move detection of key/value pairs down into prefork(). Detect normal array assignment and [key]=val array assignemnt separately. Mark key / value pairs with Marker and pass up flag. Deal with marked triads specially later on. --- ChangeLog | 28 +++++--- Doc/Zsh/params.yo | 23 ++++-- NEWS | 15 +++- README | 11 +++ Src/Zle/zle_tricky.c | 2 +- Src/exec.c | 86 +++++----------------- Src/params.c | 192 ++++++++++++++++++++++++++++++++----------------- Src/subst.c | 71 ++++++++++++++++-- Src/zsh.h | 20 ++++-- Test/B02typeset.ztst | 15 ++++ Test/D04parameter.ztst | 27 +++++++ 11 files changed, 325 insertions(+), 165 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 464837cd6..2c5e0001c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2017-09-24 Peter Stephenson + + * 41754: Doc/Zsh/params.yo, NEWS, README, + Src/Zle/zle_tricky.c, Src/exec.c, Src/params.c, Src/subst.c, + Src/zsh.h, Test/B02typeset.ztst, Test/D04parameter.ztst: + allow mix of [ind]=val and traditional assignment for normal + (but not associative) arrays, add tests. + 2017-09-22 Oliver Kiddle * 41742: Completion/Unix/Command/_mtr: update for mtr 0.92 @@ -2305,7 +2313,7 @@ 2016-10-24 Barton E. Schaefer * unposted: NEWS, README: update for 39704. - + * 39704: Src/params.c, Test/B02typeset.ztst, Test/B03print.ztst, Test/V10private.ztst: the output of "typeset -p" uses "export" commands or the "-g" option for parameters that are not local to @@ -3363,7 +3371,7 @@ 2016-07-17 Barton E. Schaefer * unposted: Functions/Misc/add-zle-hook-widget: Move from Zle/. - + * 38866: Doc/Zsh/contrib.yo: update add-zle-hook-widget for 38850. * 38866 (+ tweak 38872): Functions/Zle/add-zle-hook-widget: fix @@ -4122,7 +4130,7 @@ * 37971 (cf. users/21284: Eric Freese): Functions/Zle/bracketed-paste-magic: fix potential issues when interacting with user-defined widgets - + * 37961: Src/Zle/complist.c: in interactive menuselection, use of "compadd -x" (e.g. the "warnings" zstyle) may have replaced the completion list, so skip highlighting of the current selection @@ -5263,7 +5271,7 @@ 2015-09-30 Barton E. Schaefer * users/20672: Src/text.c: missing "do" in gettext2() for "select" - + * 36707: Src/exec.c, Src/loop.c: distinguish ERR_RETURN value of retflag so that execif() can ignore it in the test sublist @@ -5347,7 +5355,7 @@ 2015-09-28 Jun-ichi Takimoto * 36631: Completion/Unix/Command/_sh, - Completion/Unix/Command/_zsh: separate _zsh from _sh + Completion/Unix/Command/_zsh: separate _zsh from _sh 2015-09-26 Barton E. Schaefer @@ -6349,7 +6357,7 @@ 2015-07-22 Barton E. Schaefer * unposted: Test/B02typeset.ztst: fix another test for 35581 - + * 35582: Test/A06assign.ztst, Test/B02typeset.ztst: test for 35581 * 35581: Src/params.c: output array assignments with spaces inside @@ -6708,7 +6716,7 @@ occur anywhere in a coredump filename * 35476: Src/params.c: Allow setting $0 when POSIX_ARGZERO is - not set + not set 2015-06-16 Barton E. Schaefer @@ -7952,7 +7960,7 @@ tty_poll(). * 34369: Daniel Shahaf: document error / warning codes. - + * 34383: Src/utils.c: new ztrdup() shoud be dupstring(). 2015-01-25 Oliver Kiddle @@ -8008,7 +8016,7 @@ 2015-01-22 Barton E. Schaefer * 34344: Test/V07pcre.ztst: fix 34338, builtins need loading too - + * 34338: Test/V07pcre.ztst: check feature availability 2015-01-22 Marc Finet @@ -8348,7 +8356,7 @@ 2014-12-17 Barton E. Schaefer * 34002: Src/Zle/zle_keymap.c: zshcalloc() in init_keymaps() - + * 33992: Src/jobs.c: do not attempt attachtty() for process group zero (which is possible in a linux pid namespace) diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index c675cba3e..f3cec345d 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -109,10 +109,19 @@ of subscript expression that may be used when directly subscripting a variable name, described in the section Array Subscripts below, are not available. -When assigning with this third form, every element must use this syntax or -an error is generated. Likewise, if the first entry does not match this -form, any later entry that does is taken as a simple value rather than a -key / value pair. Both var(key) and var(value) undergo all forms of expansion +The syntaxes with and without the explicit key may be mixed. Any absent +var(key) is deduced by incrementing the index from the previously +assigned element. Note that it is not treated as an error +if latter assignements in this form overwrite earlier assignments. + +For example, assuming the option tt(KSH_ARRAYS) is not set, the following: + +example(array=LPAR()one [3]=three four+RPAR()) + +causes the array variable tt(array) to contain four elements tt(one), +an empty string, tt(three) and tt(four), in that order. + +Both var(key) and var(value) undergo all forms of expansion allowed for single word shell expansions (this does not include filename generation); these are as performed by the parameter expansion flag tt(LPAR()e+RPAR()) as described in @@ -120,7 +129,7 @@ ifzman(zmanref(zshparam))\ ifnzman(noderef(Parameter Expansion)). Nested parentheses may surround var(value) and are included as part of the value, which is joined into a plain string; this differs from ksh which -allows the values to themselves be arrays. A future version of zsh may +allows the values themselves to be arrays. A future version of zsh may support that. To cause the brackets to be interpreted as a character class for filename generation, and therefore to treat the resulting list of files as a set of values, quote the equal sign using any form of quoting. @@ -159,6 +168,10 @@ indent(tt(set -A) var(name) var(key) var(value) ...) indent(var(name)tt(=LPAR())var(key) var(value) ...tt(RPAR())) indent(var(name)tt(=LPAR())tt([)var(key)tt(]=)var(value) ...tt(RPAR())) +Note that only one of the two syntaxes above may be used in any +given assignment; the forms may not be mixed. This is unlike the case +of numerically indexed arrays. + Every var(key) must have a var(value) in this case. Note that this assigns to the entire array, deleting any elements that do not appear in the list. The append syntax may also be used with an associative array: diff --git a/NEWS b/NEWS index 796a2c90d..77f13bb3e 100644 --- a/NEWS +++ b/NEWS @@ -4,7 +4,7 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH Note also the list of incompatibilities in the README file. -Changes from 5.4 to 5.4.3 +Changes from 5.4.2 to 5.5 ------------------------- The effect of the NO_INTERACTIVE_COMMENTS option extends into $(...) and @@ -12,8 +12,17 @@ The effect of the NO_INTERACTIVE_COMMENTS option extends into $(...) and comments were always recognized within command substitutions unless the comment character "#" was disabled via reset of $histchars. -Changes from 5.3.1 to 5.4 -------------------------- +An alternative assignment syntax for indicating indices for arrays +and keys for associative arrays: + +typeset -a array=([1]=first [2]=second) +typeset -A assoc=([key1]=val1 [key2]=val2) + +is allowed for compatibility with other shells. In the case of normal +arrays the new syntax can be mixed with the old. + +Changes from 5.3.1 to 5.4.2 +--------------------------- The 'exec' and 'command' precommand modifiers, and options to them, are now parsed after parameter expansion. Previously, both the modifier and diff --git a/README b/README index e22cfc12e..73733069d 100644 --- a/README +++ b/README @@ -252,6 +252,17 @@ This is also necessary in the unusual eventuality that the builtins are to be overridden by shell functions, since reserved words take precedence over functions. +10) For compatilibity with other shells, the syntax + +array=([index]=value) + +can be used with both assoiative arrays and normal arrays. In the +unlikely event that you wish to create an array with an entry +matching a file whose name consists of one of a range of characters +matched as a [...] expression, followed by an equal sign, followed +by arbitrary other charaters, it is now necessary to quote the equals +sign. + Incompatibilites between 5.0.7 and 5.0.8 ---------------------------------------- diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 5a9cccb6f..caeef7692 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -2268,7 +2268,7 @@ doexpansion(char *s, int lst, int olst, int explincmd) int ng = opts[NULLGLOB]; opts[NULLGLOB] = 1; - globlist(vl, 1); + globlist(vl, PREFORK_NO_UNTOK); opts[NULLGLOB] = ng; } if (errflag) diff --git a/Src/exec.c b/Src/exec.c index 31edfab55..bd242d140 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2389,60 +2389,6 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag, } } -/* Check for array assignent with entries like [key]=val. - * - * All entries or none must match this form, else error and return 0. - * - * Convert list to alternate key / val form, perform - * appropriate substitution, and return 1 if found. - * - * Caller to check errflag. - */ - -/**/ -static int -keyvalpairarray(LinkList vl, int htok) -{ - char *start, *end, *dat; - LinkNode ve, next; - - if (vl && - (ve = firstnode(vl)) && - (start = (char *)getdata(ve)) && - start[0] == Inbrack && - (end = strchr(start+1, Outbrack)) && - end[1] == Equals) { - for (;;) { - *end = '\0'; - next = nextnode(ve); - - dat = start + 1; - if (htok) - singsub(&dat); - untokenize(dat); - setdata(ve, dat); - dat = end + 2; - if (htok) - singsub(&dat); - untokenize(dat); - insertlinknode(vl, ve, dat); - ve = next; - if (!ve) - break; - if (!(start = (char *)getdata(ve)) || - start[0] != Inbrack || - !(end = strchr(start+1, Outbrack)) || - end[1] != Equals) { - zerr("bad array element, expected [key]=value: %s", - start); - return 0; - } - } - return 1; - } - return 0; -} - /**/ static void addvars(Estate state, Wordcode pc, int addflags) @@ -2484,10 +2430,6 @@ addvars(Estate state, Wordcode pc, int addflags) vl = &svl; } else { vl = ecgetlist(state, WC_ASSIGN_NUM(ac), EC_DUPTOK, &htok); - if (keyvalpairarray(vl, htok)) { - myflags |= ASSPM_KEY_VALUE; - htok = 0; - } if (errflag) { state->pc = opc; return; @@ -2495,25 +2437,28 @@ addvars(Estate state, Wordcode pc, int addflags) } if (vl && htok) { + int prefork_ret = 0; prefork(vl, (isstr ? (PREFORK_SINGLE|PREFORK_ASSIGN) : - PREFORK_ASSIGN), NULL); + PREFORK_ASSIGN), &prefork_ret); if (errflag) { state->pc = opc; return; } + if (prefork_ret & PREFORK_KEY_VALUE) + myflags |= ASSPM_KEY_VALUE; if (!isstr || (isset(GLOBASSIGN) && isstr && haswilds((char *)getdata(firstnode(vl))))) { - globlist(vl, 0); + globlist(vl, prefork_ret); /* Unset the parameter to force it to be recreated * as either scalar or array depending on how many * matches were found for the glob. */ if (isset(GLOBASSIGN) && isstr) - unsetparam(name); - } - if (errflag) { - state->pc = opc; - return; + unsetparam(name); + if (errflag) { + state->pc = opc; + return; + } } } if (isstr && (empty(vl) || !nextnode(firstnode(vl)))) { @@ -4030,16 +3975,17 @@ execcmd_exec(Estate state, Execcmd_params eparams, EC_DUPTOK, &htok); if (asg->value.array) { - if (keyvalpairarray(asg->value.array, 1)) - asg->flags |= ASG_KEY_VALUE; - else if (!errflag) { + if (!errflag) { + int prefork_ret = 0; prefork(asg->value.array, PREFORK_ASSIGN, - NULL); + &prefork_ret); if (errflag) { state->pc = opc; break; } - globlist(asg->value.array, 0); + if (prefork_ret & PREFORK_KEY_VALUE) + asg->flags |= ASG_KEY_VALUE; + globlist(asg->value.array, prefork_ret); } if (errflag) { state->pc = opc; diff --git a/Src/params.c b/Src/params.c index d628ddf37..c7514de8a 100644 --- a/Src/params.c +++ b/Src/params.c @@ -2704,7 +2704,7 @@ setarrvalue(Value v, char **val) v->pm->gsu.a->setfn(v->pm, val); } else if (v->start == -1 && v->end == 0 && PM_TYPE(v->pm->node.flags) == PM_HASHED) { - arrhashsetfn(v->pm, val, 1); + arrhashsetfn(v->pm, val, ASSPM_AUGMENT); } else if ((PM_TYPE(v->pm->node.flags) == PM_HASHED)) { freearray(val); zerr("%s: attempt to set slice of associative array", @@ -3186,69 +3186,124 @@ assignaparam(char *s, char **val, int flags) if (flags & ASSPM_WARN) check_warn_pm(v->pm, "array", created, may_warn_about_nested_vars); - if ((flags & ASSPM_KEY_VALUE) && (PM_TYPE(v->pm->node.flags) & PM_ARRAY)) { - /* - * This is an ordinary array with key / value pairs. - */ - int maxlen, origlen; - char **aptr, **fullval; - zlong *subscripts = (zlong *)zhalloc(arrlen(val) * sizeof(zlong)); - zlong *iptr = subscripts; - if (flags & ASSPM_AUGMENT) { - maxlen = origlen = arrlen(v->pm->gsu.a->getfn(v->pm)); - } else { - maxlen = origlen = 0; - } - for (aptr = val; *aptr && aptr[1]; aptr += 2) { - *iptr = mathevali(*aptr); - if (*iptr < 0 || - (!isset(KSHARRAYS) && *iptr == 0)) { - unqueue_signals(); - zerr("bad subscript for direct array assignment: %s", *aptr); + /* + * At this point, we may have array entries consisting of + * - a Marker element --- normally allocated array entry but + * with just Marker char and null + * - an array index element --- as normal for associative array, + * but non-standard for normal array which we handle now. + * - a value for the indexed element. + * This only applies if the flag ASSPM_KEY_VALUE is passed in, + * indicating prefork() detected this syntax. + * + * For associative arrays we just junk the Makrer elements. + */ + if (flags & ASSPM_KEY_VALUE) { + char **aptr; + if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) { + /* + * This is an ordinary array with key / value pairs. + */ + int maxlen, origlen, nextind; + char **fullval; + zlong *subscripts = (zlong *)zhalloc(arrlen(val) * sizeof(zlong)); + zlong *iptr = subscripts; + if (flags & ASSPM_AUGMENT) { + maxlen = origlen = arrlen(v->pm->gsu.a->getfn(v->pm)); + } else { + maxlen = origlen = 0; + } + nextind = 0; + for (aptr = val; *aptr; ) { + if (**aptr == Marker) { + *iptr = mathevali(*++aptr); + if (*iptr < 0 || + (!isset(KSHARRAYS) && *iptr == 0)) { + unqueue_signals(); + zerr("bad subscript for direct array assignment: %s", *aptr); + return NULL; + } + if (!isset(KSHARRAYS)) + --*iptr; + nextind = *iptr + 1; + ++iptr; + aptr += 2; + } else { + ++nextind; + ++aptr; + } + if (nextind > maxlen) + maxlen = nextind; + } + fullval = zshcalloc((maxlen+1) * sizeof(char *)); + if (!fullval) { + zerr("array too large"); return NULL; } - if (!isset(KSHARRAYS)) - --*iptr; - if (*iptr + 1 > maxlen) - maxlen = *iptr + 1; - ++iptr; - } - fullval = zshcalloc((maxlen+1) * sizeof(char *)); - if (!fullval) { - zerr("array too large"); - return NULL; - } - fullval[maxlen] = NULL; - if (flags & ASSPM_AUGMENT) { - char **srcptr = v->pm->gsu.a->getfn(v->pm); - for (aptr = fullval; aptr <= fullval + origlen; aptr++) { - *aptr = ztrdup(*srcptr); - srcptr++; + fullval[maxlen] = NULL; + if (flags & ASSPM_AUGMENT) { + char **srcptr = v->pm->gsu.a->getfn(v->pm); + for (aptr = fullval; aptr <= fullval + origlen; aptr++) { + *aptr = ztrdup(*srcptr); + srcptr++; + } } - } - iptr = subscripts; - for (aptr = val; *aptr && aptr[1]; aptr += 2) { - zsfree(*aptr); - fullval[*iptr] = aptr[1]; - ++iptr; - } - if (*aptr) { /* Shouldn't be possible */ - DPUTS(1, "Extra element in key / value array"); - zsfree(*aptr); - } - free(val); - for (aptr = fullval; aptr < fullval + maxlen; aptr++) { + iptr = subscripts; + nextind = 0; + for (aptr = val; *aptr; ++aptr) { + if (**aptr == Marker) { + zsfree(*aptr); + zsfree(*++aptr); /* Index, no longer needed */ + fullval[*iptr] = *++aptr; + nextind = *iptr + 1; + ++iptr; + } else { + fullval[nextind] = *aptr; + ++nextind; + } + /* aptr now on value in both cases */ + } + if (*aptr) { /* Shouldn't be possible */ + DPUTS(1, "Extra element in key / value array"); + zsfree(*aptr); + } + free(val); + for (aptr = fullval; aptr < fullval + maxlen; aptr++) { + /* + * Remember we don't have sparse arrays but and they're null + * terminated --- so any value we don't set has to be an + * empty string. + */ + if (!*aptr) + *aptr = ztrdup(""); + } + setarrvalue(v, fullval); + unqueue_signals(); + return v->pm; + } else if (PM_TYPE(v->pm->node.flags & PM_HASHED)) { /* - * Remember we don't have sparse arrays but and they're null - * terminated --- so any value we don't set has to be an - * empty string. + * We strictly enforce [key]=value syntax for associative + * arrays. Marker can only indicate a Marker / key / value + * triad; it cannot be there by accident. + * + * It's too inefficient to strip Markers here, and they + * can't be there in the other form --- so just ignore + * them willy nilly lower down. */ - if (!*aptr) - *aptr = ztrdup(""); + for (aptr = val; *aptr; aptr += 3) { + if (**aptr != Marker) { + unqueue_signals(); + freearray(val); + zerr("bad [key]=value syntax for associative array"); + return NULL; + } + } + } else { + unqueue_signals(); + freearray(val); + zerr("invalid use of [key]=value assignment syntax"); + return NULL; } - setarrvalue(v, fullval); - unqueue_signals(); - return v->pm; } if (flags & ASSPM_AUGMENT) { @@ -3741,30 +3796,37 @@ nullsethashfn(UNUSED(Param pm), HashTable x) /* Function to set value of an association parameter using key/value pairs */ /**/ -mod_export void -arrhashsetfn(Param pm, char **val, int augment) +static void +arrhashsetfn(Param pm, char **val, int flags) { /* Best not to shortcut this by using the existing hash table, * * since that could cause trouble for special hashes. This way, * * it's up to pm->gsu.h->setfn() what to do. */ - int alen = arrlen(val); + int alen = 0; HashTable opmtab = paramtab, ht = 0; - char **aptr = val; + char **aptr; Value v = (Value) hcalloc(sizeof *v); v->end = -1; + for (aptr = val; *aptr; ++aptr) { + if (**aptr != Marker) + ++alen; + } + if (alen % 2) { freearray(val); zerr("bad set of key/value pairs for associative array"); return; } - if (augment) { + if (flags & ASSPM_AUGMENT) { ht = paramtab = pm->gsu.h->getfn(pm); } - if (alen && (!augment || !paramtab)) { + if (alen && (!(flags & ASSPM_AUGMENT) || !paramtab)) { ht = paramtab = newparamtable(17, pm->node.nam); } - while (*aptr) { + for (aptr = val; *aptr; ) { + if (**aptr == Marker) + zsfree(*aptr++); /* The parameter name is ztrdup'd... */ v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET); /* diff --git a/Src/subst.c b/Src/subst.c index 5df2a8b2d..357dc9168 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -35,6 +35,41 @@ /**/ char nulstring[] = {Nularg, '\0'}; +/* Check for array assignent with entries like [key]=val. + * + * Insert Marker node, convert following nodes to list to alternate key + * / val form, perform appropriate substitution, and return last + * inserted (value) node if found. + * + * Caller to check errflag. + */ + +/**/ +static LinkNode +keyvalpairelement(LinkList list, LinkNode node) +{ + char *start, *end, *dat; + + if ((start = (char *)getdata(node)) && + start[0] == Inbrack && + (end = strchr(start+1, Outbrack)) && + end[1] == Equals) { + static char marker[2] = { Marker, '\0' }; + *end = '\0'; + + dat = start + 1; + singsub(&dat); + untokenize(dat); + setdata(node, marker); + node = insertlinknode(list, node, dat); + dat = end + 2; + singsub(&dat); + untokenize(dat); + return insertlinknode(list, node, dat); + } + return NULL; +} + /* Do substitutions before fork. These are: * - Process substitution: <(...), >(...), =(...) * - Parameter substitution @@ -46,17 +81,16 @@ char nulstring[] = {Nularg, '\0'}; * * "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. + * "ret_flags" is used to return PREFORK_* 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, int *ret_flags) { - LinkNode node, stop = 0; + LinkNode node, insnode, stop = 0; int keep = 0, asssub = (flags & PREFORK_TYPESET) && isset(KSHTYPESET); int ret_flags_local = 0; if (!ret_flags) @@ -64,6 +98,14 @@ prefork(LinkList list, int flags, int *ret_flags) queue_signals(); for (node = firstnode(list); node; incnode(node)) { + if ((flags & (PREFORK_SINGLE|PREFORK_ASSIGN)) == PREFORK_ASSIGN && + (insnode = keyvalpairelement(list, node))) { + node = insnode; + *ret_flags |= PREFORK_KEY_VALUE; + continue; + } + if (errflag) + return; if (isset(SHFILEEXPANSION)) { /* * Here and below we avoid taking the address @@ -400,16 +442,31 @@ quotesubst(char *str) return str; } +/* Glob entries of a linked list. + * + * flags are from PREFORK_*, but only two are handled: + * - PREFORK_NO_UNTOK: pass into zglob() a flag saying do not untokenise. + * - PREFORK_KEY_VALUE: look out for Marker / Key / Value list triads + * and don't glob them. The key and value should already have + * been untokenised as they are not subject to further expansion. + */ + /**/ mod_export void -globlist(LinkList list, int nountok) +globlist(LinkList list, int flags) { LinkNode node, next; badcshglob = 0; for (node = firstnode(list); !errflag && node; node = next) { next = nextnode(node); - zglob(list, node, nountok); + if ((flags & PREFORK_KEY_VALUE) && + *(char *)getdata(node) == Marker) { + /* Skip key / value pair */ + next = nextnode(nextnode(next)); + } else { + zglob(list, node, (flags & PREFORK_NO_UNTOK) != 0); + } } if (noerrs) badcshglob = 0; diff --git a/Src/zsh.h b/Src/zsh.h index 27642f239..f7242dd34 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -223,9 +223,14 @@ struct mathfunc { * tokens here. */ /* - * Marker used in paramsubst for rc_expand_param. - * Also used in pattern character arrays as guaranteed not to - * mark a character in a string. + * Marker is used in the following special circumstances: + * - In paramsubst for rc_expand_param. + * - In pattern character arrays as guaranteed not to mark a character in + * a string. + * - In assignments with the ASSPM_KEY_VALUE flag set in order to + * mark that there is a key / value pair following. + * All the above are local uses --- any case where the Marker has + * escaped beyond the context in question is an error. */ #define Marker ((char) 0xa2) @@ -1969,7 +1974,14 @@ enum { /* SHWORDSPLIT forced off in nested subst */ PREFORK_NOSHWORDSPLIT = 0x20, /* Prefork is part of a parameter subexpression */ - PREFORK_SUBEXP = 0x40 + PREFORK_SUBEXP = 0x40, + /* Prefork detected an assignment list with [key]=value syntax, + * Only used on return from prefork, not meaningful passed down. + * Also used as flag to globlist. + */ + PREFORK_KEY_VALUE = 0x80, + /* No untokenise: used only as flag to globlist */ + PREFORK_NO_UNTOK = 0x100 }; /* diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index 7923ae3a6..13f0d5e30 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -778,3 +778,18 @@ >the key >the value >typeset -A keyvalhash=( ['*']='?not_globbed?' ['another key']='another value' ['the key']='the value' ) + + local keyvalarray=(first [2]=second third [6]=sixth seventh [5]=fifth new_sixth) + print -l "${keyvalarray[@]}" +0:mixed syntax [key]=val with normal arrays +>first +>second +>third +> +>fifth +>new_sixth +>seventh + + local -A keyvalhash=(1 one [2]=two 3 three) +1:Mixed syntax with [key]=val not allowed for hash. +?(eval):1: bad [key]=value syntax for associative array diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 367bca120..abac8f7a0 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -2275,3 +2275,30 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 >third value >4fourth element\ >fourth value + + local keyvalarray + keyvalarray=(first [2]=second third [6]=sixth seventh [5]=fifth new_sixth) + print -l "${keyvalarray[@]}" +0:mixed syntax [key]=val with normal arrays +>first +>second +>third +> +>fifth +>new_sixth +>seventh + + local -A keyvalhash + keyvalhash=(1 one [2]=two 3 three) +1:Mixed syntax with [key]=val not allowed for hash. +?(eval):2: bad [key]=value syntax for associative array + + touch KVA1one KVA2two KVA3three + local keyvalarray + keyvalarray=(KVA* [4]=*) + print -l "${keyvalarray[@]}" +0:Globbing in non-[key]=val parts of mixed syntax. +>KVA1one +>KVA2two +>KVA3three +>* -- cgit v1.2.3 From 03af5fdbeed0930b5f1d3715ee1080fb993ed145 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 26 Sep 2017 09:26:57 +0100 Subject: 41761: Ensure status from interrupt is propagated by builtin --- ChangeLog | 5 +++++ Src/exec.c | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 4ed4acefe..12922e338 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2017-09-26 Peter Stephenson + + * 41761: Src/exec.c: Ensure status from interrupt is propagated + from builtin. + 2017-09-25 Peter Stephenson * 41747: Sr/hist.c, Src/params.c, Src/subst.c. Src/zsh.h: add diff --git a/Src/exec.c b/Src/exec.c index bd242d140..161d4ac5e 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3999,8 +3999,15 @@ execcmd_exec(Estate state, Execcmd_params eparams, state->pc = opc; } dont_queue_signals(); - if (!errflag) - lastval = execbuiltin(args, assigns, (Builtin) hn); + if (!errflag) { + int ret = execbuiltin(args, assigns, (Builtin) hn); + /* + * In case of interruption assume builtin status + * is less useful than what interrupt set. + */ + if (!(errflag & ERRFLAG_INT)) + lastval = ret; + } if (do_save & BINF_COMMAND) errflag &= ~ERRFLAG_ERROR; restore_queue_signals(q); -- cgit v1.2.3 From 7a0718796a56be64b5498806b4d8491ef2f9bb7d Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Wed, 27 Sep 2017 20:34:11 -0700 Subject: 41767: preserve nonzero exit status on interrupt --- ChangeLog | 4 ++++ Src/exec.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 0eae51f8f..56a5cdbb7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2017-09-27 Barton E. Schaefer + + * 41767: Src/exec.c: preserve nonzero exit status on interrupt + 2017-09-27 Peter Stephenson * 41764 (test tweaked): Doc/Zsh/params.yo, Src/params.c, diff --git a/Src/exec.c b/Src/exec.c index 161d4ac5e..0d2dc4ebb 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3199,7 +3199,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, } if (errflag) { - lastval = 1; + if (!lastval) + lastval = 1; if (oautocont >= 0) opts[AUTOCONTINUE] = oautocont; return; -- cgit v1.2.3 From a1276c88e1137c643bb8055d719afc17cb37bf0f Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 29 Sep 2017 16:29:49 +0100 Subject: 41789: Don't save fd if -1. We try to move an fd which isn't opend but it will feel. This needs handling specially in the new code for marking saved fd's. --- ChangeLog | 5 +++++ Src/exec.c | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 959f7d000..9b1567d23 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2017-09-29 Peter Stephenson + + * 41789: Src/exec.c: Don't try to mark an FD as saved if it + couldn't be moved. + 2017-09-28 Oliver Kiddle * 41772: Src/Zle/computil.c, Test/Y03arguments.ztst: fix bug diff --git a/Src/exec.c b/Src/exec.c index 0d2dc4ebb..780998b1a 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2325,16 +2325,19 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag, * fd1 may already be closed here, so * ignore bad file descriptor error */ - if (fdN < 0 && errno != EBADF) { - zerr("cannot duplicate fd %d: %e", fd1, errno); - mfds[fd1] = NULL; - closemnodes(mfds); - return; + if (fdN < 0) { + if (errno != EBADF) { + zerr("cannot duplicate fd %d: %e", fd1, errno); + mfds[fd1] = NULL; + closemnodes(mfds); + return; + } + } else { + DPUTS(fdtable[fdN] != FDT_INTERNAL, + "Saved file descriptor not marked as internal"); + fdtable[fdN] |= FDT_SAVED_MASK; } save[fd1] = fdN; - DPUTS(fdtable[fdN] != FDT_INTERNAL, - "Saved file descriptor not marked as internal"); - fdtable[fdN] |= FDT_SAVED_MASK; } } } -- cgit v1.2.3 From 174e560a23e40725cd0b50669a52d831342e5246 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 29 Sep 2017 16:46:01 +0100 Subject: 41787 (plus minor tweaks): use $FUNCSTACK for function nesting depth. Initialised from existing configuration value. --- ChangeLog | 7 +++++++ Doc/Zsh/params.yo | 10 ++++++++++ README | 26 +++++++++++++++++++++++++- Src/exec.c | 17 ++++++----------- Src/params.c | 14 ++++++++++++++ Test/C04funcdef.ztst | 8 ++++++++ configure.ac | 6 +++--- 7 files changed, 73 insertions(+), 15 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 2019a0fa7..f79a204ad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2017-10-02 Peter Stephenson + + * 41787 (minor corrections): configure.ac, README, + Doc/Zsh/params.yo, Src/exec.c, Src/parms.c, + Test/C04funcdef.ztst: reduce default nested function depth to + 500 and expose as $FUNCNEST. + 2017-10-01 Peter Stephenson * 41797 (tweaked): Doc/Zsh/builtins.yo, Src/builtin.c, diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 05ea45f98..5757111b2 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -731,6 +731,16 @@ This value is system dependent and is intended for debugging purposes. It is also useful with the tt(zsh/system) module which allows the number to be turned into a name or message. ) +vindex(FUNCNEST) +item(tt(FUNCNEST) )( +Integer. If greater than or equal to zero, the maximum nesting depth of +shell functions. When it is exceeded, an error is raised at the point +where a function is called. The default value is determined when +the shell is configured, but is typically 500. Increasing +the value increases the danger of a runaway function recursion +causing the shell to crash. Setting a negative value turns off +the check. +) vindex(GID) item(tt(GID) )( The real group ID of the shell process. If you have sufficient privileges, diff --git a/README b/README index 73733069d..6fad1d516 100644 --- a/README +++ b/README @@ -30,9 +30,33 @@ Zsh is a shell with lots of features. For a list of some of these, see the file FEATURES, and for the latest changes see NEWS. For more details, see the documentation. -Incompatibilities since 5.3.1 +Incompatibilities since 5.4.2 ----------------------------- +1) The default build-time maximum nested function depth has been +decreased from 1000 to 500 based on user experience. However, +it can now be changed at run time via the variable FUNCNEST. +If you previously configured the shell to set a different value, +or to remove the check, this is now reflected in the default +value of the variable. + +2) The syntax + +foo=([key]=value) + +can be used to set elements of arrays and associative arrays. In the +unlikely event that you need to set an array by matching files using a +pattern that starts with a character range followed by '=', you need to +quote the '=', e.g.: + +foo=([aeiou]\=vowel) + +This is only required for array values contained within parentheses; +command line expansion for normal arguments has not changed. + +Incompatibilities between 5.3.1 and 5.4.2 +----------------------------------------- + 1) The default behaviour of code like the following has changed: alias foo='noglob foo' diff --git a/Src/exec.c b/Src/exec.c index 780998b1a..dfb50c3b3 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5508,9 +5508,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) struct funcstack fstack; static int oflags; Emulation_options save_sticky = NULL; -#ifdef MAX_FUNCTION_DEPTH static int funcdepth; -#endif Heap funcheap; queue_signals(); /* Lots of memory and global state changes coming */ @@ -5640,13 +5638,12 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) argzero = ztrdup(argzero); } } -#ifdef MAX_FUNCTION_DEPTH - if(++funcdepth > MAX_FUNCTION_DEPTH) - { - zerr("maximum nested function level reached"); - goto undoshfunc; - } -#endif + ++funcdepth; + if (zsh_funcnest >= 0 && funcdepth > zsh_funcnest) { + zerr("maximum nested function level reached; increase FUNCNEST?"); + lastval = 1; + goto undoshfunc; + } fstack.name = dupstring(name); /* * The caller is whatever is immediately before on the stack, @@ -5685,10 +5682,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) runshfunc(prog, wrappers, fstack.name); doneshfunc: funcstack = fstack.prev; -#ifdef MAX_FUNCTION_DEPTH undoshfunc: --funcdepth; -#endif if (retflag) { retflag = 0; breaks = obreaks; diff --git a/Src/params.c b/Src/params.c index ddf3ce164..31ff0445b 100644 --- a/Src/params.c +++ b/Src/params.c @@ -101,6 +101,19 @@ zlong lastval, /* $? */ rprompt_indent, /* $ZLE_RPROMPT_INDENT */ ppid, /* $PPID */ zsh_subshell; /* $ZSH_SUBSHELL */ + +/* $FUNCNEST */ +/**/ +mod_export +zlong zsh_funcnest = +#ifdef MAX_FUNCTION_DEPTH + MAX_FUNCTION_DEPTH +#else + /* Disabled by default but can be enabled at run time */ + -1 +#endif + ; + /**/ zlong lineno, /* $LINENO */ zoptind, /* $OPTIND */ @@ -337,6 +350,7 @@ IPDEF5("COLUMNS", &zterm_columns, zlevar_gsu), IPDEF5("LINES", &zterm_lines, zlevar_gsu), IPDEF5U("ZLE_RPROMPT_INDENT", &rprompt_indent, rprompt_indent_gsu), IPDEF5("SHLVL", &shlvl, varinteger_gsu), +IPDEF5("FUNCNEST", &zsh_funcnest, varinteger_gsu), /* Don't import internal integer status variables. */ #define IPDEF6(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0} diff --git a/Test/C04funcdef.ztst b/Test/C04funcdef.ztst index 6a675e0b4..0c00a0477 100644 --- a/Test/C04funcdef.ztst +++ b/Test/C04funcdef.ztst @@ -515,6 +515,14 @@ 0:autoload with absolute path not cancelled by bare autoload >I have been loaded by explicit path. + ( + FUNCNEST=0 + fn() { true; } + fn + ) +1: +?fn:4: maximum nested function level reached; increase FUNCNEST? + %clean rm -f file.in file.out diff --git a/configure.ac b/configure.ac index ec0bdae6e..1a498f8b2 100644 --- a/configure.ac +++ b/configure.ac @@ -415,13 +415,13 @@ ifdef([max_function_depth],[undefine([max_function_depth])])dnl AH_TEMPLATE([MAX_FUNCTION_DEPTH], [Define for function depth limits]) AC_ARG_ENABLE(max-function-depth, -AC_HELP_STRING([--enable-max-function-depth=MAX], [limit function depth to MAX, default 1000]), +AC_HELP_STRING([--enable-max-function-depth=MAX], [limit function depth to MAX, default 500]), [if test x$enableval = xyes; then - AC_DEFINE(MAX_FUNCTION_DEPTH, 1000) + AC_DEFINE(MAX_FUNCTION_DEPTH, 500) elif test x$enableval != xno; then AC_DEFINE_UNQUOTED(MAX_FUNCTION_DEPTH, $enableval) fi], -[AC_DEFINE(MAX_FUNCTION_DEPTH, 1000)] +[AC_DEFINE(MAX_FUNCTION_DEPTH, 500)] ) ifdef([default_readnullcmd],[undefine([default_readnullcmd])])dnl -- cgit v1.2.3 From e573857a033504f7918c4a2309151329820f115f Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 4 Oct 2017 09:18:51 +0100 Subject: 41802 (minor tweaks): use heap during shell function call. Replaces stack for more efficient memory management. Also fix debug message when FUNCNEST is increased. --- ChangeLog | 6 +++ Src/exec.c | 154 +++++++++++++++++++++++++++++++++--------------------------- Src/parse.c | 3 +- 3 files changed, 93 insertions(+), 70 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index f79a204ad..08ccc3470 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2017-10-04 Peter Stephenson + + * 41802 (minor tweaks): Src/exec.c, Src/parse.c: use heap + instead of stack for temporary storage over shell function call, + also fix debug message when FUNCNEST increased. + 2017-10-02 Peter Stephenson * 41787 (minor corrections): configure.ac, README, diff --git a/Src/exec.c b/Src/exec.c index dfb50c3b3..fc6d02dc3 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -41,6 +41,20 @@ enum { ADDVAR_RESTORE = 1 << 2 }; +/* Structure in which to save values around shell function call */ + +struct funcsave { + char opts[OPT_SIZE]; + char *argv0; + int zoptind, lastval, optcind, numpipestats; + int *pipestats; + char *scriptname; + int breaks, contflag, loops, emulation, noerrexit, oflags, restore_sticky; + Emulation_options sticky; + struct funcstack fstack; +}; +typedef struct funcsave *Funcsave; + /* * used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT. * Bits from noerrexit_bits. @@ -5495,34 +5509,36 @@ int sticky_emulation_differs(Emulation_options sticky2) mod_export int doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) { - char **pptab, **x, *oargv0; - int oldzoptind, oldlastval, oldoptcind, oldnumpipestats, ret; - int *oldpipestats = NULL; - char saveopts[OPT_SIZE], *oldscriptname = scriptname; + char **pptab, **x; + int ret; char *name = shfunc->node.nam; - int flags = shfunc->node.flags, ooflags; - int savnoerrexit; + int flags = shfunc->node.flags; char *fname = dupstring(name); - int obreaks, ocontflag, oloops, saveemulation, restore_sticky; Eprog prog; - struct funcstack fstack; static int oflags; - Emulation_options save_sticky = NULL; static int funcdepth; Heap funcheap; queue_signals(); /* Lots of memory and global state changes coming */ NEWHEAPS(funcheap) { - oargv0 = NULL; - obreaks = breaks; - ocontflag = contflag; - oloops = loops; + /* + * Save data in heap rather than on stack to keep recursive + * function cost down --- use of heap memory should be efficient + * at this point. Saving is not actually massive. + */ + Funcsave funcsave = zhalloc(sizeof(struct funcsave)); + funcsave->scriptname = scriptname; + funcsave->argv0 = NULL; + funcsave->breaks = breaks; + funcsave->contflag = contflag; + funcsave->loops = loops; + funcsave->lastval = lastval; + funcsave->pipestats = NULL; + funcsave->numpipestats = numpipestats; + funcsave->noerrexit = noerrexit; if (trap_state == TRAP_STATE_PRIMED) trap_return--; - oldlastval = lastval; - oldnumpipestats = numpipestats; - savnoerrexit = noerrexit; /* * Suppression of ERR_RETURN is turned off in function scope. */ @@ -5533,8 +5549,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) * immediately by a pushheap/popheap pair. */ size_t bytes = sizeof(int)*numpipestats; - oldpipestats = (int *)zhalloc(bytes); - memcpy(oldpipestats, pipestats, bytes); + funcsave->pipestats = (int *)zhalloc(bytes); + memcpy(funcsave->pipestats, pipestats, bytes); } starttrapscope(); @@ -5543,8 +5559,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) pptab = pparams; if (!(flags & PM_UNDEFINED)) scriptname = dupstring(name); - oldzoptind = zoptind; - oldoptcind = optcind; + funcsave->zoptind = zoptind; + funcsave->optcind = optcind; if (!isset(POSIXBUILTINS)) { zoptind = 1; optcind = 0; @@ -5553,9 +5569,9 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) /* We need to save the current options even if LOCALOPTIONS is * * not currently set. That's because if it gets set in the * * function we need to restore the original options on exit. */ - memcpy(saveopts, opts, sizeof(opts)); - saveemulation = emulation; - save_sticky = sticky; + memcpy(funcsave->opts, opts, sizeof(opts)); + funcsave->emulation = emulation; + funcsave->sticky = sticky; if (sticky_emulation_differs(shfunc->sticky)) { /* @@ -5572,7 +5588,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) */ sticky = sticky_emulation_dup(shfunc->sticky, 1); emulation = sticky->emulation; - restore_sticky = 1; + funcsave->restore_sticky = 1; installemulation(emulation, opts); if (sticky->n_on_opts) { OptIndex *onptr; @@ -5591,7 +5607,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) /* All emulations start with pattern disables clear */ clearpatterndisables(); } else - restore_sticky = 0; + funcsave->restore_sticky = 0; if (flags & (PM_TAGGED|PM_TAGGED_LOCAL)) opts[XTRACE] = 1; @@ -5609,11 +5625,11 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) else opts[WARNNESTEDVAR] = 0; } - ooflags = oflags; + funcsave->oflags = oflags; /* * oflags is static, because we compare it on the next recursive - * call. Hence also we maintain ooflags for restoring the previous - * value of oflags after the call. + * call. Hence also we maintain a saved version for restoring + * the previous value of oflags after the call. */ oflags = flags; opts[PRINTEXITVALUE] = 0; @@ -5624,7 +5640,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) pparams = x = (char **) zshcalloc(((sizeof *x) * (1 + countlinknodes(doshargs)))); if (isset(FUNCTIONARGZERO)) { - oargv0 = argzero; + funcsave->argv0 = argzero; argzero = ztrdup(getdata(node)); } /* first node contains name regardless of option */ @@ -5634,7 +5650,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) } else { pparams = (char **) zshcalloc(sizeof *pparams); if (isset(FUNCTIONARGZERO)) { - oargv0 = argzero; + funcsave->argv0 = argzero; argzero = ztrdup(argzero); } } @@ -5644,21 +5660,21 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) lastval = 1; goto undoshfunc; } - fstack.name = dupstring(name); + funcsave->fstack.name = dupstring(name); /* * The caller is whatever is immediately before on the stack, * unless we're at the top, in which case it's the script * or interactive shell name. */ - fstack.caller = funcstack ? funcstack->name : - dupstring(oargv0 ? oargv0 : argzero); - fstack.lineno = lineno; - fstack.prev = funcstack; - fstack.tp = FS_FUNC; - funcstack = &fstack; + funcsave->fstack.caller = funcstack ? funcstack->name : + dupstring(funcsave->argv0 ? funcsave->argv0 : argzero); + funcsave->fstack.lineno = lineno; + funcsave->fstack.prev = funcstack; + funcsave->fstack.tp = FS_FUNC; + funcstack = &funcsave->fstack; - fstack.flineno = shfunc->lineno; - fstack.filename = getshfuncfile(shfunc); + funcsave->fstack.flineno = shfunc->lineno; + funcsave->fstack.filename = getshfuncfile(shfunc); prog = shfunc->funcdef; if (prog->flags & EF_RUN) { @@ -5666,7 +5682,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) prog->flags &= ~EF_RUN; - runshfunc(prog, NULL, fstack.name); + runshfunc(prog, NULL, funcsave->fstack.name); if (!(shf = (Shfunc) shfunctab->getnode(shfunctab, (name = fname)))) { @@ -5679,52 +5695,52 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) } prog = shf->funcdef; } - runshfunc(prog, wrappers, fstack.name); + runshfunc(prog, wrappers, funcsave->fstack.name); doneshfunc: - funcstack = fstack.prev; + funcstack = funcsave->fstack.prev; undoshfunc: --funcdepth; if (retflag) { retflag = 0; - breaks = obreaks; + breaks = funcsave->breaks; } freearray(pparams); - if (oargv0) { + if (funcsave->argv0) { zsfree(argzero); - argzero = oargv0; + argzero = funcsave->argv0; } pparams = pptab; if (!isset(POSIXBUILTINS)) { - zoptind = oldzoptind; - optcind = oldoptcind; + zoptind = funcsave->zoptind; + optcind = funcsave->optcind; } - scriptname = oldscriptname; - oflags = ooflags; + scriptname = funcsave->scriptname; + oflags = funcsave->oflags; endpatternscope(); /* before restoring old LOCALPATTERNS */ - if (restore_sticky) { + if (funcsave->restore_sticky) { /* * If we switched to an emulation environment just for * this function, we interpret the option and emulation * switch as being a firewall between environments. */ - memcpy(opts, saveopts, sizeof(opts)); - emulation = saveemulation; - sticky = save_sticky; + memcpy(opts, funcsave->opts, sizeof(opts)); + emulation = funcsave->emulation; + sticky = funcsave->sticky; } else if (isset(LOCALOPTIONS)) { /* restore all shell options except PRIVILEGED and RESTRICTED */ - saveopts[PRIVILEGED] = opts[PRIVILEGED]; - saveopts[RESTRICTED] = opts[RESTRICTED]; - memcpy(opts, saveopts, sizeof(opts)); - emulation = saveemulation; + funcsave->opts[PRIVILEGED] = opts[PRIVILEGED]; + funcsave->opts[RESTRICTED] = opts[RESTRICTED]; + memcpy(opts, funcsave->opts, sizeof(opts)); + emulation = funcsave->emulation; } else { /* just restore a couple. */ - opts[XTRACE] = saveopts[XTRACE]; - opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE]; - opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS]; - opts[LOCALLOOPS] = saveopts[LOCALLOOPS]; - opts[WARNNESTEDVAR] = saveopts[WARNNESTEDVAR]; + opts[XTRACE] = funcsave->opts[XTRACE]; + opts[PRINTEXITVALUE] = funcsave->opts[PRINTEXITVALUE]; + opts[LOCALOPTIONS] = funcsave->opts[LOCALOPTIONS]; + opts[LOCALLOOPS] = funcsave->opts[LOCALLOOPS]; + opts[WARNNESTEDVAR] = funcsave->opts[WARNNESTEDVAR]; } if (opts[LOCALLOOPS]) { @@ -5732,9 +5748,9 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) zwarn("`continue' active at end of function scope"); if (breaks) zwarn("`break' active at end of function scope"); - breaks = obreaks; - contflag = ocontflag; - loops = oloops; + breaks = funcsave->breaks; + contflag = funcsave->contflag; + loops = funcsave->loops; } endtrapscope(); @@ -5742,11 +5758,11 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) if (trap_state == TRAP_STATE_PRIMED) trap_return++; ret = lastval; - noerrexit = savnoerrexit; + noerrexit = funcsave->noerrexit; if (noreturnval) { - lastval = oldlastval; - numpipestats = oldnumpipestats; - memcpy(pipestats, oldpipestats, sizeof(int)*numpipestats); + lastval = funcsave->lastval; + numpipestats = funcsave->numpipestats; + memcpy(pipestats, funcsave->pipestats, sizeof(int)*numpipestats); } } OLDHEAPS; diff --git a/Src/parse.c b/Src/parse.c index 6e0856bbc..5fe3ebd2c 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -2742,7 +2742,8 @@ freeeprog(Eprog p) DPUTS(p->nref < 0 && !(p->flags & EF_HEAP), "Real EPROG has nref < 0"); DPUTS(p->nref < -1, "Uninitialised EPROG nref"); #ifdef MAX_FUNCTION_DEPTH - DPUTS(p->nref > MAX_FUNCTION_DEPTH + 10, "Overlarge EPROG nref"); + DPUTS(zsh_funcnest >=0 && p->nref > zsh_funcnest + 10, + "Overlarge EPROG nref"); #endif if (p->nref > 0 && !--p->nref) { for (i = p->npats, pp = p->pats; i--; pp++) -- cgit v1.2.3 From aab0f6d763b0eb3eb964c576953c9dd0b90916ae Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 15 Dec 2017 08:56:19 +0000 Subject: 42123 (tweaked): take account of Dash in function names. Needed when comparing word code function name with autoload request. Add test. --- ChangeLog | 5 +++++ Src/exec.c | 22 ++++++++++++++++++++-- Test/C04funcdef.ztst | 9 +++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index dfd30ca9c..5613c8c72 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2017-12-15 Peter Stephenson + + * 42123 (tweaked further): Src/exec.c: need to take account of + Dash when comparing functions names for autoloading. + 2017-12-14 Peter Stephenson * dana: 42119: Doc/zsh/params.yo, Src/jobs.c, diff --git a/Src/exec.c b/Src/exec.c index fc6d02dc3..664d79079 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5932,6 +5932,7 @@ stripkshdef(Eprog prog, char *name) { Wordcode pc; wordcode code; + char *ptr1, *ptr2; if (!prog) return NULL; @@ -5942,8 +5943,25 @@ stripkshdef(Eprog prog, char *name) return prog; pc++; code = *pc++; - if (wc_code(code) != WC_FUNCDEF || - *pc != 1 || strcmp(name, ecrawstr(prog, pc + 1, NULL))) + if (wc_code(code) != WC_FUNCDEF || *pc != 1) + return prog; + + /* + * See if name of function requested (name) is same as + * name of function in word code. name may still have "-" + * tokenised. The word code shouldn't, as function names should be + * untokenised, but reports say it sometimes does. + */ + ptr1 = name; + ptr2 = ecrawstr(prog, pc + 1, NULL); + while (*ptr1 && *ptr2) { + if (*ptr1 != *ptr2 && *ptr1 != Dash && *ptr1 != '-' && + *ptr2 != Dash && *ptr2 != '-') + break; + ptr1++; + ptr2++; + } + if (*ptr1 || *ptr2) return prog; { diff --git a/Test/C04funcdef.ztst b/Test/C04funcdef.ztst index 0c00a0477..5786018e0 100644 --- a/Test/C04funcdef.ztst +++ b/Test/C04funcdef.ztst @@ -523,6 +523,15 @@ 1: ?fn:4: maximum nested function level reached; increase FUNCNEST? + ( + fpath=(.) + print "foo-bar() { print this should run automatically; }" >foo-bar + autoload -Uz foo-bar + foo-bar + ) +0:autoload containing dash +>this should run automatically + %clean rm -f file.in file.out -- cgit v1.2.3 From a9e60dc1794dadaae6d73a1de3f5cf8418336771 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Wed, 6 Dec 2017 22:49:19 +0000 Subject: users/13148 (and workers/42060): Abort the command line when replying "[n] No" to the RM_STAR_SILENT prompt. Patch by Stephane, rebased by Mikael. --- ChangeLog | 6 ++++++ Src/exec.c | 17 +++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 30058dce3..f11321438 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2017-12-20 Daniel Shahaf + + * Stephane (rebased by Mikael): users/13148, workers/42060: + Src/exec.c: Abort the command line when replying "[n] No" to + the RM_STAR_SILENT prompt. + 2017-12-20 Jun-ichi Takimoto * 42143: Completion/Unix/Type/_object_files: fix a pattern for diff --git a/Src/exec.c b/Src/exec.c index 664d79079..3678ef84a 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3251,19 +3251,24 @@ execcmd_exec(Estate state, Execcmd_params eparams, next = nextnode(node); if (s[0] == Star && !s[1]) { - if (!checkrmall(pwd)) - uremnode(args, node); + if (!checkrmall(pwd)) { + errflag |= ERRFLAG_ERROR; + break; + } } else if (l >= 2 && s[l - 2] == '/' && s[l - 1] == Star) { char t = s[l - 2]; + int rmall; s[l - 2] = 0; - if (!checkrmall(*s ? s : "/")) - uremnode(args, node); + rmall = checkrmall(s); s[l - 2] = t; + + if (!rmall) { + errflag |= ERRFLAG_ERROR; + break; + } } } - if (!nextnode(firstnode(args))) - errflag |= ERRFLAG_ERROR; } if (type == WC_FUNCDEF) { -- cgit v1.2.3 From 8ec3d17b4ba5f19ae24695c9532606f7f266e1f1 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 22 Dec 2017 19:51:04 +0000 Subject: 33395: Improvments for function managment. Functions defined inside other fucntions needs file line number adding. Particularly useful for anonymous fucntions. Add flag to indicate a function is anonymous. Done up to now by comparing the name to a pointer but this is more consistent. --- ChangeLog | 6 ++++++ Src/exec.c | 7 ++++++- Src/zsh.h | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 90fd3f891..7456a9b85 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2017-12-22 Peter Stephenson + + * 33395: Src/exec.c, Src/zsh.h: improved line numbering for + functions (in particular anonymous functions) defined inside + others, also flag for anonymous functions. + 2017-12-20 Peter Stephenson * dana: 42145: Src/Zle/computil.c, Test/Y03arguments.ztst: fix diff --git a/Src/exec.c b/Src/exec.c index 3678ef84a..38cc24a12 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5089,7 +5089,11 @@ execfuncdef(Estate state, Eprog redir_prog) shf->node.flags = 0; /* No dircache here, not a directory */ shf->filename = ztrdup(scriptfilename); - shf->lineno = lineno; + shf->lineno = + (funcstack && (funcstack->tp == FS_FUNC || + funcstack->tp == FS_EVAL)) ? + funcstack->flineno + lineno : + lineno; /* * redir_prog is permanently allocated --- but if * this function has multiple names we need an additional @@ -5109,6 +5113,7 @@ execfuncdef(Estate state, Eprog redir_prog) LinkList args; anon_func = 1; + shf->node.flags |= PM_ANONYMOUS; state->pc = end; end += *state->pc++; diff --git a/Src/zsh.h b/Src/zsh.h index 24d06ba06..22ae95480 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1875,6 +1875,7 @@ struct tieddata { #define PM_DONTIMPORT_SUID (1<<19) /* do not import if running setuid */ #define PM_LOADDIR (1<<19) /* (function) filename gives load directory */ #define PM_SINGLE (1<<20) /* special can only have a single instance */ +#define PM_ANONYMOUS (1<<20) /* (function) anonymous function */ #define PM_LOCAL (1<<21) /* this parameter will be made local */ #define PM_SPECIAL (1<<22) /* special builtin parameter */ #define PM_DONTIMPORT (1<<23) /* do not import this variable */ -- cgit v1.2.3 From b476d89fa279469fd24926db7f2b117a318ff38b Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Sun, 14 Jan 2018 03:07:32 -0800 Subject: 42043: ZSH_DEBUG_CMD should not WARN_CREATE_GLOBAL --- ChangeLog | 4 ++++ Src/exec.c | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index b54966dd0..8aae9e7a8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2018-01-14 Barton E. Schaefer + + * 42043: Src/exec.c: ZSH_DEBUG_CMD should not WARN_CREATE_GLOBAL + 2018-01-13 Barton E. Schaefer * 42264: Doc/Zsh/expn.yo: Follow-up to previous: Clarify that diff --git a/Src/exec.c b/Src/exec.c index 38cc24a12..c39680de7 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1319,7 +1319,9 @@ execlist(Estate state, int dont_change_job, int exiting) noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; if (ltype & Z_SIMPLE) /* skip the line number */ pc2++; - pm = setsparam("ZSH_DEBUG_CMD", getpermtext(state->prog, pc2, 0)); + pm = assignsparam("ZSH_DEBUG_CMD", + getpermtext(state->prog, pc2, 0), + 0); exiting = donetrap; ret = lastval; -- cgit v1.2.3 From 47aa60950c488a49dc245659c126e3078bf499d0 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 12 Feb 2018 10:06:45 +0000 Subject: 42355: Fix use of backslashes on here doc input. Handling of white space in particular was confusing and inconsistent with other shells. --- ChangeLog | 6 ++++++ Src/exec.c | 11 ++++++++++- Test/A04redirect.ztst | 46 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 58 insertions(+), 5 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 4cc7820d2..522830fb4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2018-02-12 Peter Stephenson + + * Marijn: 42355: Src/exec.c, Test/A04redirect.ztst: + Interpreation of backslash on here doc input was inconsistent + and confusing. + 2018-02-12 Daniel Hahler * 42324: Completion/Unix/Command/_git: _git: handle mutually exclusive diff --git a/Src/exec.c b/Src/exec.c index c39680de7..e5c64555c 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4387,8 +4387,17 @@ gethere(char **strp, int typ) bptr = buf + bsiz; bsiz *= 2; } - if (lexstop || c == '\n') + if (lexstop) break; + if (c == '\n') { + if (!qt && bptr > t && *(bptr - 1) == '\\') { + /* line continuation */ + bptr--; + c = hgetc(); + continue; + } else + break; + } *bptr++ = c; c = hgetc(); } diff --git a/Test/A04redirect.ztst b/Test/A04redirect.ztst index b8105cf6d..ef7ddb25a 100644 --- a/Test/A04redirect.ztst +++ b/Test/A04redirect.ztst @@ -114,7 +114,7 @@ heretest() { print First line cat <<' HERE' - $foo$foo met celeste 'but with extra' "stuff to test quoting" + $foo$foo met celeste 'but with extra' "stuff to test quoting"\ HERE print Last line } @@ -125,19 +125,57 @@ heretest 0:Re-evaluation of function output with here document, quoted >First line -> $foo$foo met celeste 'but with extra' "stuff to test quoting" +> $foo$foo met celeste 'but with extra' "stuff to test quoting"\ >Last line >First line -> $foo$foo met celeste 'but with extra' "stuff to test quoting" +> $foo$foo met celeste 'but with extra' "stuff to test quoting"\ >Last line >First line -> $foo$foo met celeste 'but with extra' "stuff to test quoting" +> $foo$foo met celeste 'but with extra' "stuff to test quoting"\ >Last line read -r line <<' HERE' HERE 1:No input, not even newline, from empty here document. + heretest() { + print First line + cat <<-HERE + $foo\ + $foo + some\ + stuff + to\ + test + tab\stripping + HERE + print Last line + } + heretest + eval "$(functions heretest)" + heretest + eval "$(functions heretest)" + heretest +0:Line continuation in here-document with unquoted delimiter +>First line +>bar bar +>some stuff +>to test +>tab\stripping +>Last line +>First line +>bar bar +>some stuff +>to test +>tab\stripping +>Last line +>First line +>bar bar +>some stuff +>to test +>tab\stripping +>Last line + # # exec tests: perform these in subshells so if they fail the # shell won't exit. -- cgit v1.2.3 From 1219eae82938c918f5183e89af4a54296256372f Mon Sep 17 00:00:00 2001 From: Stephane Chazelas Date: Wed, 14 Mar 2018 14:42:48 +0000 Subject: 42465: Pass up error status from readoutput(). This improves the consistency of error reporting from $(...) constructs. --- ChangeLog | 6 ++++++ Src/Modules/mapfile.c | 3 ++- Src/exec.c | 16 +++++++++++++--- 3 files changed, 21 insertions(+), 4 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 97e23e456..155bfe29e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2018-03-15 Peter Stephenson + + * Stephane: 42465: Src/exec.c, Src/Modules/mapfile.c: pass error + status from readoutput, causing more consistent reporting + from $(...) constructs. + 2018-03-12 Peter Stephenson * 42453: Src/jobs.c: fix race looking up status of just diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c index 2503b361e..771e5b5fc 100644 --- a/Src/Modules/mapfile.c +++ b/Src/Modules/mapfile.c @@ -197,8 +197,9 @@ get_contents(char *fname) val = NULL; if ((fd = open(fname, O_RDONLY | O_NOCTTY)) >= 0) { LinkList ll; + int readerror; - if ((ll = readoutput(fd, 1))) + if ((ll = readoutput(fd, 1, &readerror))) val = peekfirst(ll); } #endif /* USE_MMAP */ diff --git a/Src/exec.c b/Src/exec.c index e5c64555c..ce8cf8c55 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4507,6 +4507,8 @@ getoutput(char *cmd, int qt) if ((s = simple_redir_name(prog, REDIR_READ))) { /* $(< word) */ int stream; + LinkList retval; + int readerror; singsub(&s); if (errflag) @@ -4514,9 +4516,15 @@ getoutput(char *cmd, int qt) untokenize(s); if ((stream = open(unmeta(s), O_RDONLY | O_NOCTTY)) == -1) { zwarn("%e: %s", errno, s); + lastval = cmdoutval = 1; return newlinklist(); } - return readoutput(stream, qt); + retval = readoutput(stream, qt, &readerror); + if (readerror) { + zwarn("error when reading %s: %e", s, readerror); + lastval = cmdoutval = 1; + } + return retval; } if (mpipe(pipes) < 0) { errflag |= ERRFLAG_ERROR; @@ -4537,7 +4545,7 @@ getoutput(char *cmd, int qt) LinkList retval; zclose(pipes[1]); - retval = readoutput(pipes[0], qt); + retval = readoutput(pipes[0], qt, NULL); fdtable[pipes[0]] = FDT_UNUSED; waitforpid(pid, 0); /* unblocks */ lastval = cmdoutval; @@ -4562,7 +4570,7 @@ getoutput(char *cmd, int qt) /**/ mod_export LinkList -readoutput(int in, int qt) +readoutput(int in, int qt, int *readerror) { LinkList ret; char *buf, *ptr; @@ -4591,6 +4599,8 @@ readoutput(int in, int qt) } *ptr++ = c; } + if (readerror && ferror(fin)) + *readerror = errno; fclose(fin); while (cnt && ptr[-1] == '\n') ptr--, cnt--; -- cgit v1.2.3 From d2350a1e8027e9550996464739885f5ec468bd83 Mon Sep 17 00:00:00 2001 From: Stephane Chazelas Date: Sun, 18 Mar 2018 16:49:42 -0700 Subject: 42469: necessary repairs to 42465 found by "make check" --- ChangeLog | 4 ++++ Src/Modules/mapfile.c | 3 +-- Src/exec.c | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 155bfe29e..43624741a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2018-03-18 Barton E. Schaefer + + * Stephane: 42469: Src/exec.c, Src/Modules/mapfile.c: fix 42465 + 2018-03-15 Peter Stephenson * Stephane: 42465: Src/exec.c, Src/Modules/mapfile.c: pass error diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c index 771e5b5fc..7a903418f 100644 --- a/Src/Modules/mapfile.c +++ b/Src/Modules/mapfile.c @@ -197,9 +197,8 @@ get_contents(char *fname) val = NULL; if ((fd = open(fname, O_RDONLY | O_NOCTTY)) >= 0) { LinkList ll; - int readerror; - if ((ll = readoutput(fd, 1, &readerror))) + if ((ll = readoutput(fd, 1, 0))) val = peekfirst(ll); } #endif /* USE_MMAP */ diff --git a/Src/exec.c b/Src/exec.c index ce8cf8c55..35b0bb191 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4599,8 +4599,8 @@ readoutput(int in, int qt, int *readerror) } *ptr++ = c; } - if (readerror && ferror(fin)) - *readerror = errno; + if (readerror) + *readerror = ferror(fin) ? errno : 0; fclose(fin); while (cnt && ptr[-1] == '\n') ptr--, cnt--; -- cgit v1.2.3 From 679b71ec4d852037fe5f73d35bf557b0f406c8d4 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sat, 24 Mar 2018 15:02:41 +0100 Subject: 42518, CVE-2018-1071: check bounds when copying path in hashcmd() --- ChangeLog | 5 +++++ Src/exec.c | 2 +- Src/utils.c | 6 +++--- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 02d60612b..084d971c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2018-03-24 Oliver Kiddle + + * 42518, CVE-2018-1071: Src/exec.c, Src/utils.c: + check bounds when copying path in hashcmd() + 2018-03-24 Jun-ichi Takimoto * 42501: Src/Zle/complete.c, Src/Zle/computil.c, diff --git a/Src/exec.c b/Src/exec.c index 35b0bb191..e154d1249 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -934,7 +934,7 @@ hashcmd(char *arg0, char **pp) for (; *pp; pp++) if (**pp == '/') { s = buf; - strucpy(&s, *pp); + struncpy(&s, *pp, PATH_MAX); *s++ = '/'; if ((s - buf) + strlen(arg0) >= PATH_MAX) continue; diff --git a/Src/utils.c b/Src/utils.c index 3b589aa35..998b16220 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -2283,10 +2283,10 @@ struncpy(char **s, char *t, int n) { char *u = *s; - while (n--) - *u++ = *t++; + while (n-- && (*u++ = *t++)); *s = u; - *u = '\0'; + if (n > 0) /* just one null-byte will do, unlike strncpy(3) */ + *u = '\0'; } /* Return the number of elements in an array of pointers. * -- cgit v1.2.3