about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--Src/subst.c40
-rw-r--r--Test/D04parameter.ztst54
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  <p.stephenson@samsung.com>
+
+	* 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  <p.stephenson@samsung.com>
 
 	* 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