about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorPeter Stephenson <p.w.stephenson@ntlworld.com>2017-09-13 20:54:00 +0100
committerPeter Stephenson <p.w.stephenson@ntlworld.com>2017-09-13 20:54:00 +0100
commit54b395844030342213cacba4c569a6c5e6781c46 (patch)
treeb9de3dad4206d09d12c2b745e9bac6731d9bf221 /Src
parentda4146bdffb54423ad4d6db893b8ed25663169dc (diff)
downloadzsh-54b395844030342213cacba4c569a6c5e6781c46.tar.gz
zsh-54b395844030342213cacba4c569a6c5e6781c46.tar.xz
zsh-54b395844030342213cacba4c569a6c5e6781c46.zip
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.
Diffstat (limited to 'Src')
-rw-r--r--Src/builtin.c70
-rw-r--r--Src/exec.c86
-rw-r--r--Src/params.c66
-rw-r--r--Src/zsh.h17
4 files changed, 206 insertions, 33 deletions
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) */