From 0fcc6c8fb28f4cc61520770da2156de3a124e432 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 6 Nov 2015 17:28:02 +0000 Subject: 37073: another SH_WORD_SPLIT problem. In cases like x${:- y} the space was simply removed instead of being used for splitting. --- ChangeLog | 6 ++++++ Src/subst.c | 40 +++++++++++++++++++++++++++++++------ Test/D04parameter.ztst | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6b4a38a85..71d44bc90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2015-11-06 Peter Stephenson + + * 37073: Src/subst.c, Test/D04parameter.ztst: fix case of + SH_WORD_SPLIT with string before parameter substitution and + nested string that starts with a space e.g. x${:- y}. + 2015-11-04 Peter Stephenson * 37062: Completion/Unix/Command/_git: allow verbose diff --git a/Src/subst.c b/Src/subst.c index 021d23444..528912134 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 cb7079c98..59c14a408 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 -- cgit 1.4.1