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. --- Src/builtin.c | 70 +++++++++++++++++++++++++++++++++--------------- Src/exec.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- Src/params.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ Src/zsh.h | 17 ++++++++++-- 4 files changed, 206 insertions(+), 33 deletions(-) (limited to 'Src') 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) */ -- cgit 1.4.1