summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@zsh.org>2017-09-27 09:41:50 +0100
committerPeter Stephenson <pws@zsh.org>2017-09-27 09:41:50 +0100
commit6230e82d44da25773437c5438c83a5d5fe275420 (patch)
tree123f2cfd0c646b662208cd231e5431b97024feb9
parent03af5fdbeed0930b5f1d3715ee1080fb993ed145 (diff)
downloadzsh-6230e82d44da25773437c5438c83a5d5fe275420.tar.gz
zsh-6230e82d44da25773437c5438c83a5d5fe275420.tar.xz
zsh-6230e82d44da25773437c5438c83a5d5fe275420.zip
41764 (test tweaked): allow [key]+=value when modifying arrays
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Zsh/params.yo17
-rw-r--r--Src/params.c39
-rw-r--r--Src/subst.c17
-rw-r--r--Src/zsh.h4
-rw-r--r--Test/D04parameter.ztst34
6 files changed, 101 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index 12922e338..0eae51f8f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2017-09-27  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 41764 (test tweaked): Doc/Zsh/params.yo, Src/params.c,
+	Src/subst.c, Src/zsh.h, Test/D04parameter.ztst: allow
+	[key]+=value when modifying array or associative array.
+
 2017-09-26  Peter Stephenson  <p.stephenson@samsung.com>
 
 	* 41761: Src/exec.c: Ensure status from interrupt is propagated
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 430f097b6..05ea45f98 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -112,7 +112,7 @@ available.
 The syntaxes with and without the explicit key may be mixed.  An implicit
 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.
+if latter assignments in this form overwrite earlier assignments.
 
 For example, assuming the option tt(KSH_ARRAYS) is not set, the following:
 
@@ -121,7 +121,11 @@ 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
+In the forms where only var(value) is specified, full command
+line expansion is performed.
+
+In the tt([)var(key)tt(]=)var(value) form,
+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
@@ -145,7 +149,8 @@ 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).
+var(value).  Also, it is possible to use tt([)var(key)tt(]+=)var(value)
+to append to the existing value at that index.
 
 Within the parentheses on the right hand side of either form of the
 assignment, newlines and semicolons are treated the same as white space,
@@ -180,7 +185,11 @@ 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.
+replaces the value for the existing key if it is.  In the second
+form it is also possible to use tt([)var(key)tt(]+=)var(value) to
+append to the existing value at that key.  Expansion is performed
+identically to the corresponding forms for normal arrays, as
+described above.
 
 To create an empty array (including associative arrays), use one of:
 ifzman()
diff --git a/Src/params.c b/Src/params.c
index 4d4d08085..3236f7165 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -3208,13 +3208,15 @@ assignaparam(char *s, char **val, int flags)
 	     * This is an ordinary array with key / value pairs.
 	     */
 	    int maxlen, origlen, nextind;
-	    char **fullval;
+	    char **fullval, **origptr;
 	    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));
+		origptr = v->pm->gsu.a->getfn(v->pm);
+		maxlen = origlen = arrlen(origptr);
 	    } else {
 		maxlen = origlen = 0;
+		origptr = NULL;
 	    }
 	    nextind = 0;
 	    for (aptr = val; *aptr; ) {
@@ -3245,25 +3247,36 @@ assignaparam(char *s, char **val, int flags)
 	    }
 	    fullval[maxlen] = NULL;
 	    if (flags & ASSPM_AUGMENT) {
-		char **srcptr = v->pm->gsu.a->getfn(v->pm);
+		char **srcptr = origptr;
 		for (aptr = fullval; aptr <= fullval + origlen; aptr++) {
-		    *aptr = ztrdup(*srcptr); 
+		    *aptr = ztrdup(*srcptr);
 		    srcptr++;
 		}
 	    }
 	    iptr = subscripts;
 	    nextind = 0;
 	    for (aptr = val; *aptr; ++aptr) {
+		char *old;
 		if (**aptr == Marker) {
+		    int augment = ((*aptr)[1] == '+');
 		    zsfree(*aptr);
 		    zsfree(*++aptr); /* Index, no longer needed */
-		    fullval[*iptr] = *++aptr;
+		    old = fullval[*iptr];
+		    if (augment && old) {
+			fullval[*iptr] = bicat(old, *++aptr);
+			zsfree(*aptr);
+		    } else {
+			fullval[*iptr] = *++aptr;
+		    }
 		    nextind = *iptr + 1;
 		    ++iptr;
 		} else {
+		    old = fullval[nextind];
 		    fullval[nextind] = *aptr;
 		    ++nextind;
 		}
+		if (old)
+		    zsfree(old);
 		/* aptr now on value in both cases */
 	    }
 	    if (*aptr) {		/* Shouldn't be possible */
@@ -3828,8 +3841,20 @@ arrhashsetfn(Param pm, char **val, int flags)
 	ht = paramtab = newparamtable(17, pm->node.nam);
     }
     for (aptr = val; *aptr; ) {
-	if (**aptr == Marker)
+	int eltflags = 0;
+	if (**aptr == Marker) {
+	    /* Either all elements have Marker or none. Checked in caller. */
+	    if ((*aptr)[1] == '+') {
+		/* Actually, assignstrvalue currently doesn't handle this... */
+		eltflags = ASSPM_AUGMENT;
+		/* ...so we'll use the trick from setsparam(). */
+		v->start = INT_MAX;
+	    } else {
+		v->start = 0;
+	    }
+	    v->end = -1;
 	    zsfree(*aptr++);
+	}
 	/* The parameter name is ztrdup'd... */
 	v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET);
 	/*
@@ -3840,7 +3865,7 @@ arrhashsetfn(Param pm, char **val, int flags)
 	    v->pm = (Param) paramtab->getnode(paramtab, *aptr);
 	zsfree(*aptr++);
 	/* ...but we can use the value without copying. */
-	setstrvalue(v, *aptr++);
+	assignstrvalue(v, *aptr++, eltflags);
     }
     paramtab = opmtab;
     pm->gsu.h->setfn(pm, ht);
diff --git a/Src/subst.c b/Src/subst.c
index 55b5444c6..eef0dc75b 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -53,16 +53,25 @@ keyvalpairelement(LinkList list, LinkNode node)
     if ((start = (char *)getdata(node)) &&
 	start[0] == Inbrack &&
 	(end = strchr(start+1, Outbrack)) &&
-	end[1] == Equals) {
+	/* ..]=value or ]+=Value */
+	(end[1] == Equals ||
+	 (end[1] == '+' && end[2] == Equals))) {
 	static char marker[2] = { Marker, '\0' };
+	static char marker_plus[3] = { Marker, '+', '\0' };
 	*end = '\0';
 
 	dat = start + 1;
 	singsub(&dat);
 	untokenize(dat);
-	setdata(node, marker);
-	node = insertlinknode(list, node, dat);
-	dat = end + 2;
+	if (end[1] == '+') {
+	    setdata(node, marker_plus);
+	    node = insertlinknode(list, node, dat);
+	    dat = end + 3;
+	} else {
+	    setdata(node, marker);
+	    node = insertlinknode(list, node, dat);
+	    dat = end + 2;
+	}
 	singsub(&dat);
 	untokenize(dat);
 	return insertlinknode(list, node, dat);
diff --git a/Src/zsh.h b/Src/zsh.h
index b6e2001fb..c1138bfab 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -228,7 +228,9 @@ struct mathfunc {
  * - 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.
+ *   mark that there is a key / value pair following.  If this
+ *   comes from [key]=value the Marker is followed by a null;
+ *   if from [key]+=value the Marker is followed by a '+' then a null.
  * All the above are local uses --- any case where the Marker has
  * escaped beyond the context in question is an error.
  */
diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index abac8f7a0..d6ed6487b 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -2302,3 +2302,37 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888
 >KVA2two
 >KVA3three
 >*
+
+  local -a keyvalarray
+  keyvalarray=(1 2 3)
+  keyvalarray+=([1]+=a [2]=b)
+  print $keyvalarray
+0:Append to element(s) of array
+>1a b 3
+
+  local -A keyvalhash
+  keyvalhash=([a]=a [b]=b [c]=c)
+  keyvalhash+=([a]+=yee [b]=ee)
+  local key val
+  for key in "${(ok)keyvalhash[@]}"; do
+    val=${keyvalhash[$key]}
+    print -r -- $key $val
+  done
+0:Append to element(s) of associative array
+>a ayee
+>b ee
+>c c
+
+  local -a keyvalarray
+  keyvalarray=([1]=who [2]=anyway [1]+=is [1]+=that [1]+=mysterious [1]+=man)
+  print -rl -- "${keyvalarray[@]}"
+0:Append to element of associative array on creation
+>whoisthatmysteriousman
+>anyway
+
+  local -A keyvalhash
+  keyvalhash=([one]=hows [one]+=your [one]+=father [one]+=today)
+  print -rl -- ${(kv)keyvalhash}
+0:Append to element of associative array on creation
+>one
+>howsyourfathertoday