summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Zsh/params.yo5
-rw-r--r--Src/exec.c11
-rw-r--r--Src/lex.c1
-rw-r--r--Src/params.c128
-rw-r--r--Src/parse.c27
-rw-r--r--Src/text.c1
-rw-r--r--Src/zsh.h10
-rw-r--r--Test/.distfiles2
-rw-r--r--Test/A06assign.ztst253
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