about summary refs log tree commit diff
path: root/Src/exec.c
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/exec.c
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/exec.c')
-rw-r--r--Src/exec.c86
1 files changed, 77 insertions, 9 deletions
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;