diff options
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | Doc/Zsh/params.yo | 5 | ||||
-rw-r--r-- | Src/exec.c | 11 | ||||
-rw-r--r-- | Src/lex.c | 1 | ||||
-rw-r--r-- | Src/params.c | 128 | ||||
-rw-r--r-- | Src/parse.c | 27 | ||||
-rw-r--r-- | Src/text.c | 1 | ||||
-rw-r--r-- | Src/zsh.h | 10 | ||||
-rw-r--r-- | Test/.distfiles | 2 | ||||
-rw-r--r-- | Test/A06assign.ztst | 253 |
10 files changed, 419 insertions, 25 deletions
diff --git a/ChangeLog b/ChangeLog index 38a42ee70..511c4904f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2001-12-17 Oliver Kiddle <opk@zsh.org> + + * 16353: Src/exec.c, Src/lex.c, Src/params.c, Src/parse.c, + Src/text.c, Src/zsh.h, Doc/Zsh/params.yo, Test/.distfiles, + Test/A06assign.ztst: add += parameter assignments + 2001-12-17 Clint Adams <clint@zsh.org> * 16357: Doc/Zsh/expn.yo, Src/subst.c: diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 0c5d87d35..9c17117a3 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -20,8 +20,9 @@ cindex(assignment) indent(var(name)tt(=)var(value)) If the integer attribute, tt(-i), is set for var(name), the var(value) -is subject to arithmetic evaluation. See noderef(Array Parameters) -for additional forms of assignment. +is subject to arithmetic evaluation. Furthermore, by replacing `tt(=)' +with `tt(+=)', a parameter can be added or appended to. See +noderef(Array Parameters) for additional forms of assignment. To refer to the value of a parameter, write `tt($)var(name)' or `tt(${)var(name)tt(})'. See diff --git a/Src/exec.c b/Src/exec.c index ed8293b24..e7b57967b 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1512,7 +1512,8 @@ addvars(Estate state, Wordcode pc, int export) if (htok) untokenize(name); if (xtr) - fprintf(xtrerr, "%s=", name); + fprintf(xtrerr, + WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC ? "%s+=" : "%s=", name); if ((isstr = (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR))) { init_list1(svl, ecgetstr(state, EC_DUPTOK, &htok)); vl = &svl; @@ -1561,10 +1562,12 @@ addvars(Estate state, Wordcode pc, int export) } allexp = opts[ALLEXPORT]; opts[ALLEXPORT] = 1; - pm = setsparam(name, val); + pm = assignsparam(name, val, + WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC); opts[ALLEXPORT] = allexp; } else - pm = setsparam(name, val); + pm = assignsparam(name, val, + WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC); if (errflag) { state->pc = opc; return; @@ -1587,7 +1590,7 @@ addvars(Estate state, Wordcode pc, int export) fprintf(xtrerr, "%s ", *ptr); fprintf(xtrerr, ") "); } - setaparam(name, arr); + assignaparam(name, arr, WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC); if (errflag) { state->pc = opc; return; diff --git a/Src/lex.c b/Src/lex.c index 245e7bb15..11404a646 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -1140,6 +1140,7 @@ gettokstr(int c, int sub) skipparens(Inbrack, Outbrack, &t); } } + if (*t == '+') t++; if (t == bptr) { e = hgetc(); if (e == '(' && incmdpos) { diff --git a/Src/params.c b/Src/params.c index 5c4d61e69..6fb5da57f 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1724,9 +1724,12 @@ setarrvalue(Value v, char **val) } if (v->start == 0 && v->end == -1) { if (PM_TYPE(v->pm->flags) == PM_HASHED) - arrhashsetfn(v->pm, val); + arrhashsetfn(v->pm, val, 0); else (v->pm->sets.afn) (v->pm, val); + } else if (v->start == -1 && v->end == 0 && + PM_TYPE(v->pm->flags) == PM_HASHED) { + arrhashsetfn(v->pm, val, 1); } else { char **old, **new, **p, **q, **r; int n, ll, i; @@ -1870,12 +1873,15 @@ gethkparam(char *s) /**/ mod_export Param -setsparam(char *s, char *val) +assignsparam(char *s, char *val, int augment) { struct value vbuf; Value v; char *t = s; - char *ss; + char *ss, *copy, *var; + size_t lv; + mnumber lhs, rhs; + int sstart; if (!isident(s)) { zerr("not an identifier: %s", s, 0); @@ -1893,8 +1899,10 @@ setsparam(char *s, char *val) } else { if (!(v = getvalue(&vbuf, &s, 1))) createparam(t, PM_SCALAR); - else if ((PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) && - !(v->pm->flags & (PM_SPECIAL|PM_TIED)) && unset(KSHARRAYS)) { + else if ((((v->pm->flags & PM_ARRAY) && !augment) || + (v->pm->flags & PM_HASHED)) && + !(v->pm->flags & (PM_SPECIAL|PM_TIED)) && + unset(KSHARRAYS)) { unsetparam(t); createparam(t, PM_SCALAR); v = NULL; @@ -1905,6 +1913,78 @@ setsparam(char *s, char *val) zsfree(val); return NULL; } + if (augment) { + if (v->start == 0 && v->end == -1) { + switch (PM_TYPE(v->pm->flags)) { + case PM_SCALAR: + v->start = INT_MAX; /* just append to scalar value */ + break; + case PM_INTEGER: + case PM_EFLOAT: + case PM_FFLOAT: + rhs = matheval(val); + lhs = getnumvalue(v); + if (lhs.type == MN_FLOAT) { + if ((rhs.type) == MN_FLOAT) + lhs.u.d = lhs.u.d + rhs.u.d; + else + lhs.u.d = lhs.u.d + (double)rhs.u.l; + } else { + if ((rhs.type) == MN_INTEGER) + lhs.u.l = lhs.u.l + rhs.u.l; + else + lhs.u.l = lhs.u.l + (zlong)rhs.u.d; + } + setnumvalue(v, lhs); + unqueue_signals(); + zsfree(val); + return v->pm; /* avoid later setstrvalue() call */ + case PM_ARRAY: + if (unset(KSHARRAYS)) { + v->start = arrlen(v->pm->gets.afn(v->pm)); + v->end = v->start + 1; + } else { + /* ksh appends scalar to first element */ + v->end = 1; + goto kshappend; + } + break; + } + } else { + switch (PM_TYPE(v->pm->flags)) { + case PM_SCALAR: + if (v->end > 0) + v->start = v->end; + else + v->start = v->end = strlen(v->pm->gets.cfn(v->pm)) + + v->end + 1; + break; + case PM_INTEGER: + case PM_EFLOAT: + case PM_FFLOAT: + unqueue_signals(); + zerr("attempt to add to slice of a numeric variable", + NULL, 0); + zsfree(val); + return NULL; + case PM_ARRAY: + kshappend: + /* treat slice as the end element */ + v->start = sstart = v->end > 0 ? v->end - 1 : v->end; + v->isarr = 0; + var = getstrvalue(v); + v->start = sstart; + copy = val; + lv = strlen(var); + val = (char *)zalloc(lv + strlen(var)); + strcpy(val, var); + strcpy(val + lv, copy); + zsfree(copy); + break; + } + } + } + setstrvalue(v, val); unqueue_signals(); return v->pm; @@ -1912,7 +1992,7 @@ setsparam(char *s, char *val) /**/ mod_export Param -setaparam(char *s, char **val) +assignaparam(char *s, char **val, int augment) { struct value vbuf; Value v; @@ -1946,6 +2026,18 @@ setaparam(char *s, char **val) else if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) && !(v->pm->flags & (PM_SPECIAL|PM_TIED))) { int uniq = v->pm->flags & PM_UNIQUE; + if (augment) { + /* insert old value at the beginning of the val array */ + char **new; + int lv = arrlen(val); + + new = (char **) zalloc(sizeof(char *) * (lv + 2)); + *new = ztrdup(getstrvalue(v)); + memcpy(new+1, val, sizeof(char *) * (lv + 1)); + free(val); + val = new; + + } unsetparam(t); createparam(t, PM_ARRAY | uniq); v = NULL; @@ -1954,8 +2046,27 @@ setaparam(char *s, char **val) if (!v) if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) { unqueue_signals(); + freearray(val); return NULL; } + + if (augment) { + if (v->start == 0 && v->end == -1) { + if (PM_TYPE(v->pm->flags) & PM_ARRAY) { + v->start = arrlen(v->pm->gets.afn(v->pm)); + v->end = v->start + 1; + } else if (PM_TYPE(v->pm->flags) & PM_HASHED) + v->start = -1, v->end = 0; + } else { + if (v->end > 0) + v->start = v->end--; + else if (PM_TYPE(v->pm->flags) & PM_ARRAY) { + v->end = arrlen(v->pm->gets.afn(v->pm)) + v->end; + v->start = v->end + 1; + } + } + } + setarrvalue(v, val); unqueue_signals(); return v->pm; @@ -2291,7 +2402,7 @@ hashsetfn(Param pm, HashTable x) /**/ static void -arrhashsetfn(Param pm, char **val) +arrhashsetfn(Param pm, char **val, int augment) { /* Best not to shortcut this by using the existing hash table, * * since that could cause trouble for special hashes. This way, * @@ -2309,7 +2420,8 @@ arrhashsetfn(Param pm, char **val) return; } if (alen) - ht = paramtab = newparamtable(17, pm->nam); + if (!(augment && (ht = paramtab = pm->gets.hfn(pm)))) + ht = paramtab = newparamtable(17, pm->nam); while (*aptr) { /* The parameter name is ztrdup'd... */ v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET); diff --git a/Src/parse.c b/Src/parse.c index fefbbfe32..48332733b 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -1488,11 +1488,17 @@ par_simple(int *complex, int nr) } else if (tok == ENVSTRING) { char *p, *name, *str; - ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, 0)); name = tokstr; - for (p = tokstr; *p && *p != Inbrack && *p != '='; p++); - if (*p == Inbrack && !skipparens(Inbrack, Outbrack, &p) && - *p == '=') { + for (p = tokstr; *p && *p != Inbrack && *p != '=' && *p != '+'; + p++); + if (*p == Inbrack) skipparens(Inbrack, Outbrack, &p); + if (*p == '+') { + *p++ = '\0'; + ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0)); + } else + ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0)); + + if (*p == '=') { *p = '\0'; str = p + 1; } else @@ -1501,15 +1507,20 @@ par_simple(int *complex, int nr) ecstr(str); isnull = 0; } else if (tok == ENVARRAY) { - int oldcmdpos = incmdpos, n; + int oldcmdpos = incmdpos, n, type2; p = ecadd(0); incmdpos = 0; + if ((type2 = strlen(tokstr) - 1) && tokstr[type2] == '+') { + tokstr[type2] = '\0'; + type2 = WC_ASSIGN_INC; + } else + type2 = WC_ASSIGN_NEW; ecstr(tokstr); cmdpush(CS_ARRAY); yylex(); n = par_nl_wordlist(); - ecbuf[p] = WCB_ASSIGN(WC_ASSIGN_ARRAY, n); + ecbuf[p] = WCB_ASSIGN(WC_ASSIGN_ARRAY, type2, n); cmdpop(); if (tok != OUTPAR) YYERROR(oecused); @@ -2288,8 +2299,8 @@ init_eprog(void) #define FD_MINMAP 4096 #define FD_PRELEN 12 -#define FD_MAGIC 0x03040506 -#define FD_OMAGIC 0x06050403 +#define FD_MAGIC 0x04050607 +#define FD_OMAGIC 0x07060504 #define FDF_MAP 1 #define FDF_OTHER 2 diff --git a/Src/text.c b/Src/text.c index ddb4b8a2a..44527666d 100644 --- a/Src/text.c +++ b/Src/text.c @@ -323,6 +323,7 @@ gettext2(Estate state) break; case WC_ASSIGN: taddstr(ecgetstr(state, EC_NODUP, NULL)); + if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) taddchr('+'); taddchr('='); if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) { taddchr('('); diff --git a/Src/zsh.h b/Src/zsh.h index cd6caf3b3..28a3c20e9 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -591,10 +591,13 @@ struct eccstr { #define WCB_REDIR(T) wc_bld(WC_REDIR, (T)) #define WC_ASSIGN_TYPE(C) (wc_data(C) & ((wordcode) 1)) +#define WC_ASSIGN_TYPE2(C) ((wc_data(C) & ((wordcode) 2)) >> 1) #define WC_ASSIGN_SCALAR 0 #define WC_ASSIGN_ARRAY 1 -#define WC_ASSIGN_NUM(C) (wc_data(C) >> 1) -#define WCB_ASSIGN(T,N) wc_bld(WC_ASSIGN, ((T) | ((N) << 1))) +#define WC_ASSIGN_NEW 0 +#define WC_ASSIGN_INC 1 +#define WC_ASSIGN_NUM(C) (wc_data(C) >> 2) +#define WCB_ASSIGN(T,A,N) wc_bld(WC_ASSIGN, ((T) | ((A) << 1) | ((N) << 2))) #define WC_SIMPLE_ARGC(C) wc_data(C) #define WCB_SIMPLE(N) wc_bld(WC_SIMPLE, (N)) @@ -1198,6 +1201,9 @@ struct paramdef { { name, PM_ARRAY, (void *) var, (void *) arrvarsetfn, \ (void *) arrvargetfn, (void *) stdunsetfn } +#define setsparam(S,V) assignsparam(S,V,0) +#define setaparam(S,V) assignaparam(S,V,0) + /* node for named directory hash table (nameddirtab) */ struct nameddir { diff --git a/Test/.distfiles b/Test/.distfiles index fc74ba7fb..4a4904085 100644 --- a/Test/.distfiles +++ b/Test/.distfiles @@ -8,6 +8,6 @@ A03quoting.ztst C04funcdef.ztst Makefile.in ztst.zsh A04redirect.ztst D01prompt.ztst V02zregexparse.ztst A05execution.ztst D02glob.ztst Y01completion.ztst D06subscript.ztst V01zmodload.ztst E01options.ztst -B02typeset.ztst B03print.ztst +B02typeset.ztst B03print.ztst A06assign.ztst README ' diff --git a/Test/A06assign.ztst b/Test/A06assign.ztst new file mode 100644 index 000000000..fa3714f22 --- /dev/null +++ b/Test/A06assign.ztst @@ -0,0 +1,253 @@ +# Tests of parameter assignments + +%test + + typeset -A assoc + assoc=(one 1 two 2 odd) +1:assign to association with odd no. of values +?(eval):2: bad set of key/value pairs for associative array + +# tests of var+=scalar + + s+=foo + echo $s +0:append scalar to unset scalar +>foo + + s=foo + s+=bar + echo $s +0:append to scalar +>foobar + + set -- a b c + 2+=end + echo $2 +0:append to positional parameter +>bend + + a=(first second) + a+=last + print -l $a +0:add scalar to array +>first +>second +>last + + setopt ksharrays + a=(first second) + a+=last + print -l $a + unsetopt ksharrays +0:add scalar to array with ksharrays set +>firstlast + + a=(1 2) + a[@]+=3 + print -l $a +0:add scalar to array with alternate syntax +>1 +>2 +>3 + + integer i=10 + i+=20 + (( i == 30 )) +0:add to integer + + float f=3.4 + f+=2.3 + printf "%g\n" f +0:add to float +>5.7 + + typeset -A hash + hash=(one 1) + h+=string + [[ $h[@] == string ]] +0:add scalar to association + +# tests of var+=(array) + + unset a + a+=(1 2 3) + print -l $a +0:add array to unset parameter +>1 +>2 +>3 + + a=(a) + a+=(b) + print -l $a +0:add array to existing array +>a +>b + + s=foo + s+=(bar) + print -l $s +0:add array to scalar +>foo +>bar + + integer i=1 + i+=(2 3) + print -l $i +0:add array to integer +>1 +>2 +>3 + + float f=2.5 + f+=(3.5 4.5) + printf '%g\n' $f +0:add array to float +>2.5 +>3.5 +>4.5 + + typeset -A h + h+=(a 1 b 2) + print -l $h +0:add to empty association +>1 +>2 + + typeset -A h + h=(a 1) + h+=(b 2 c 3) + print -l $h +0:add to association +>1 +>2 +>3 + +# tests of var[range]+=scalar + + s=sting + s[2]+=art + echo $s +0:insert scalar inside another +>starting + + s=inert + s[-4]+=s + echo $s +0:insert scalar inside another with negative index +>insert + + s=append + s[2,6]+=age + echo $s +0:append scalar to scalar using a range +>appendage + + s=123456789 + s[3,-5]+=X + echo $s +0:insert scalar inside another, specifying a slice +>12345X6789 + + a=(a b c) + a[2]+=oo + echo $a +0:append to array element +>a boo c + + a=(a b c d) + a[-2]+=ool + echo $a +0:append to array element with negative index +>a b cool d + + a=(a b c d) + a[2,-1]+=oom + echo $a +0:append to array element, specifying a slice +>a b c doom + + setopt ksharrays + a=(a b c d) + a[0]+=0 + echo $a + unsetopt ksharrays +0:append to array element with ksharrays set +>a0 + + typeset -A h + h=(one foo) + h[one]+=bar + echo $h +0:append to association element +>foobar + + typeset -A h + h[foo]+=bar + echo ${(kv)h} +0:append to non-existent association element +>foo bar + + typeset -A h + h=(one a two b three c four d) + h[(I)*o*]+=append +1:attempt to append to slice of association +?(eval):3: h: attempt to set slice of associative array + + integer i=123 + i[2]+=6 +1:attempt to add to indexed integer variable +?(eval):2: attempt to add to slice of a numeric variable + + float f=1234.5 + f[2,4]+=3 +1:attempt to add to slice of float variable +?(eval):2: attempt to add to slice of a numeric variable + + unset u + u[3]+=third + echo $u[1]:$u[3] +0:append to unset variable with index +>:third + +# tests of var[range]+=(array) + + a=(1 2 3) + a[2]+=(a b) + echo $a +0:insert array inside another +>1 2 a b 3 + + a=(a b c) + a[-1]+=(d) + echo $a +0:append to array using negative index +>a b c d + + a=(1 2 3 4) + a[-1,-3]+=(x) + echo $a +0:insert array using negative range +>1 2 x 3 4 + + s=string + s[2]+=(a b) +1:attempt to insert array into string +?(eval):2: s: attempt to assign array value to non-array + + integer i=365 + i[2]+=(1 2) +1:attempt to insert array into string +?(eval):2: i: attempt to assign array value to non-array + + typeset -A h + h=(a 1) + h[a]+=(b 2) +1:attempt to append array to hash element +?(eval):3: h: attempt to set slice of associative array + + unset u + u[-34,-2]+=(a z) + echo $u +0:add array to indexed unset variable +>a z |