Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: [BUG] var=$* and var=$@ create array with SHWORDSPLIT and null or unset IFS



On Feb 16,  9:14pm, Bart Schaefer wrote:
}
} } In SHWORDSPLIT mode, if IFS is null (no field splitting) or unset
} } (default fieldsplitting), both var=$* and var=$@ act like var=("$@"), so
} } turn 'var' into an array.
} 
} Undoing 30299 fixes the bug Martijn is reporting here, but re-introduces
} the incorrect splitting behavior.

I have a question about one of your test cases.

>--- null IFS, native splitting ---
>> var=$@
>[one two three four]
>> var=$*
>[onetwo threefour]
>> var="$@"
>[one two three four]
>> var="$*"
>[onetwo threefour]

Joining the array for assignment is forced by prefork() passing
PREFORK_SINGLE down to paramsubst().  Joining is supposed to be done on
the first character of $IFS in this case.  When IFS is null (as opposed
to unset), this causes both $@ and $* to join on empty string.

Where is the space supposed to come from when joining $@ here?

As for the reported bug -- in certain circumstances paramsubst() skips
doing joins/splits to avoid other problems with the semantics, which
results in an array being returned to prefork() even though it asked
for a scalar.  Instead of choking on this, prefork() converts to an
array assignment.

So the following fixes this, but still joins on empty string rather
than on space in the case of $@.  There's probably a better way to turn
the LinkList back into an array?  But if this join is done any sooner,
then sorting, rcexpandparam, etc. don't happen properly.  If we can
figure out what to pass to sepjoin() in place of NULL as the second
argument, to answer my question above, all Martijn's tests can pass.

The first bit with ms_flags may be meaningless, I was never sure whether
that needed resetting in any case.  Meat is the last hunk.

diff --git a/Src/subst.c b/Src/subst.c
index 1c2397c..4df53bd 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -3475,7 +3475,6 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	    if (nojoin == 0 || sep) {
 		val = sepjoin(aval, sep, 1);
 		isarr = 0;
-		ms_flags = 0;
 	    } else if (force_split &&
 		       (spsep || nojoin == 2 || (!ifs && isarr < 0))) {
 		/* Hack to simulate splitting individual elements:
@@ -3485,6 +3484,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 		val = sepjoin(aval, (nojoin == 1 ? NULL : spsep), 1);
 		isarr = 0;
 	    }
+	    if (!isarr)
+		ms_flags = 0;
 	}
 	if (force_split && !isarr) {
 	    aval = sepsplit(val, spsep, 0, 1);
@@ -4007,6 +4008,18 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	    y = dupstring(nulstring);
 	setdata(n, (void *) y);
     }
+    if (isarr && ssub) {
+	/* prefork() wants a scalar, so join no matter what else */
+	LinkNode tn;
+
+	aval = hlinklist2array(l, 0);
+	val = sepjoin(aval, NULL, 1);
+	n = firstnode(l);
+	for (tn = lastnode(l); tn && tn != n; tn = lastnode(l))
+	    uremnode(l, tn);
+	setdata(n, (void *) val);
+	l->list.flags &= ~LF_ARRAY;
+    }
     if (eval)
 	*str = (char *) getdata(n);
 



Messages sorted by: Reverse Date, Date, Thread, Author