From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 18155 invoked by alias); 6 Nov 2015 17:10:20 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 37073 Received: (qmail 657 invoked from network); 6 Nov 2015 17:10:17 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.0 X-AuditID: cbfec7f5-f794b6d000001495-68-563cdc9a22d9 Date: Fri, 06 Nov 2015 17:00:07 +0000 From: Peter Stephenson To: zsh-workers@zsh.org Subject: Re: Inconsistency with SHWORDSPLIT and leading spaces Message-id: <20151106170007.5196bd5e@pwslap01u.europe.root.pri> In-reply-to: <87a8qr75za.fsf@gmail.com> References: <87a8qr75za.fsf@gmail.com> Organization: Samsung Cambridge Solution Centre X-Mailer: Claws Mail 3.7.9 (GTK+ 2.22.0; i386-redhat-linux-gnu) MIME-version: 1.0 Content-type: text/plain; charset=US-ASCII Content-transfer-encoding: 7bit X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrMLMWRmVeSWpSXmKPExsVy+t/xq7qz7tiEGTz7y2ZxsPkhkwOjx6qD H5gCGKO4bFJSczLLUov07RK4MmadmMFW8Mig4sLB4ywNjGtUuhg5OSQETCTmH/vMBGGLSVy4 t56ti5GLQ0hgKaPEqrY7LBDODCaJrt0gVSDOVkaJ/5+/g7WwCKhK9Ox6wwZiswkYSkzdNJsR xBYREJc4u/Y8C4gtLGAnsffmArA4r4C9xPxPa8HqOQXUJdb0bwCawwE0VE3i9hthkDC/gL7E 1b+foC6yl5h55QxUq6DEj8n3wEYyC2hJbN7WxAphy0tsXvOWGcQWAhp54+5u9gmMQrOQtMxC 0jILScsCRuZVjKKppckFxUnpuUZ6xYm5xaV56XrJ+bmbGCFB+3UH49JjVocYBTgYlXh4DZZb hwmxJpYVV+YeYpTgYFYS4V1zySZMiDclsbIqtSg/vqg0J7X4EKM0B4uSOO/MXe9DhATSE0tS s1NTC1KLYLJMHJxSDYyXXZZPUNXp9dm8Xki62L1z+xanRXdPNh1bpf2aQeGD5dV1vC88crV9 H3jbXWJdx7S1v2KjfYrl9aMrFjvrXupwde53Z33Tr/pi/ZnLceW+F+6FTN4ZIifzs6nm57O5 desU62UP7X6tv7k14OYXp5Unimc33Ffk96pSXtlzM2RRn3j/jnuqP78qsRRnJBpqMRcVJwIA qUD+7FYCAAA= On Fri, 6 Nov 2015 11:54:49 +0100 Christian Neukirchen wrote: > juno ~% zsh -c 'setopt shwordsplit; x=foo; echo x${x:+ $x}' > xfoo This is unlikely to be deliberate --- we should do *something* with the space, not pretend it's not there. This makes already insane code even worse, but it doesn't seem to have broken anything in the tests. Possible breakage is I haven't spotted some case where we use multsub() with word splitting but we need to reset the knock-on splitting (ws_at_start) for some reason similar to transforming the result into a length (though I've picked that particular one up). pws diff --git a/Src/subst.c b/Src/subst.c index 021d234..5289121 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -430,11 +430,16 @@ singsub(char **s) * set to 1. Otherwise, *isarr is set to 0, and the result is put into *s, * with any necessary joining of multiple elements using sep (which can be * NULL to use IFS). The return value is true iff the expansion resulted - * in an empty list. */ + * in an empty list. + * + * *ws_at_start is set to 1 if the string had whitespace at thes start + * that should cause word splitting against any preceeding string. + */ /**/ static int -multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep) +multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep, + int *ws_at_start) { int l; char **r, **p, *x = *s; @@ -450,6 +455,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep) l++; if (!iwsep(STOUC(c))) break; + *ws_at_start = 1; } } @@ -1717,6 +1723,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) * This is for compatibility. */ int horrible_offset_hack = 0; + /* + * Signal back from multsub: with something like + * x${:- $foo} + * with word-splitting active we need to split on that leading + * whitespace. However, if there's no "x" the whitespace is + * simply removed. + */ + int ws_at_start = 0; *s++ = '\0'; /* @@ -2265,7 +2279,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) * remove the aspar test and extract a value from an array, if * necessary, when we handle (P) lower down. */ - if (multsub(&val, 0, (aspar ? NULL : &aval), &isarr, NULL) && quoted) { + if (multsub(&val, 0, (aspar ? NULL : &aval), &isarr, NULL, + &ws_at_start) && quoted) { /* Empty quoted string --- treat as null string, not elided */ isarr = -1; aval = (char **) hcalloc(sizeof(char *)); @@ -2736,7 +2751,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) split_flags = PREFORK_NOSHWORDSPLIT; } multsub(&val, split_flags, (aspar ? NULL : &aval), - &isarr, NULL); + &isarr, NULL, &ws_at_start); copied = 1; spbreak = 0; /* Leave globsubst on if forced */ @@ -2765,13 +2780,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) * behavior on caller choice of PREFORK_SHWORDSPLIT. */ multsub(&val, spbreak ? PREFORK_SINGLE : PREFORK_NOSHWORDSPLIT, - NULL, &isarr, NULL); + NULL, &isarr, NULL, &ws_at_start); } else { if (spbreak) split_flags = PREFORK_SPLIT|PREFORK_SHWORDSPLIT; else split_flags = PREFORK_NOSHWORDSPLIT; - multsub(&val, split_flags, &aval, &isarr, NULL); + multsub(&val, split_flags, &aval, &isarr, NULL, + &ws_at_start); spbreak = 0; } if (arrasg) { @@ -3303,6 +3319,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) } if (haserr || errflag) return NULL; + ws_at_start = 0; } /* * This handles taking a length with ${#foo} and variations. @@ -3341,6 +3358,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) sprintf(buf, "%ld", len); val = dupstring(buf); isarr = 0; + ws_at_start = 0; } /* At this point we make sure that our arrayness has affected the * arrayness of the linked list. Then, we can turn our value into @@ -3370,6 +3388,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) if (isarr) { val = sepjoin(aval, sep, 1); isarr = 0; + ws_at_start = 0; } if (!ssub && (spbreak || spsep)) { aval = sepsplit(val, spsep, 0, 1); @@ -3649,6 +3668,15 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) * equivalent and only locally decide if we need to treat it * as a scalar.) */ + + /* + * If a multsub result had whitespace at the start and we're + * splitting and there's a previous string, now's the time to do so. + */ + if (ws_at_start && aptr > ostr) { + insertlinknode(l, n, dupstrpfx(ostr, aptr - ostr)), incnode(n); + ostr = aptr; + } if (isarr) { char *x; char *y; diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index cb7079c..59c14a4 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -1744,3 +1744,57 @@ >3_4_5_6 >6 >1_2_3_4_5_6 + + (setopt shwordsplit + do_test() { + print $#: "$@" + } + foo=bar + foo2="bar bar" + do_test ${:- foo} + do_test ${:- foo bar} + do_test ${:- $foo} + do_test ${:- $foo2} + do_test x${:- foo} + do_test x${:- foo bar} + do_test x${:- $foo} + do_test x${:- $foo2} + do_test x${foo:+ $foo} + ) +0:We Love SH_WORD_SPLIT Day celebrated with space at start of internal subst +>1: foo +>2: foo bar +>1: bar +>2: bar bar +>2: x foo +>3: x foo bar +>2: x bar +>3: x bar bar +>2: x bar + + (unsetopt shwordsplit # default, for clarity + do_test() { + print $#: "$@" + } + foo=bar + foo2="bar bar" + do_test ${:- foo} + do_test ${:- foo bar} + do_test ${:- $foo} + do_test ${:- $foo2} + do_test x${:- foo} + do_test x${:- foo bar} + do_test x${:- $foo} + do_test x${:- $foo2} + do_test x${foo:+ $foo} + ) +0:We Love NO_SH_WORD_SPLIT Even More Day celebrated as sanity check +>1: foo +>1: foo bar +>1: bar +>1: bar bar +>1: x foo +>1: x foo bar +>1: x bar +>1: x bar bar +>1: x bar