about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--Src/subst.c50
-rw-r--r--Test/D04parameter.ztst64
3 files changed, 73 insertions, 46 deletions
diff --git a/ChangeLog b/ChangeLog
index 71d44bc90..1334e29d8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2015-11-07  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 37074: Src/subst.c, Test/D04parameter.ztst: extend previous
+	fix to cover whitespace at end of substitution.
+
 2015-11-06  Peter Stephenson  <p.stephenson@samsung.com>
 
 	* 37073: Src/subst.c, Test/D04parameter.ztst: fix case of
diff --git a/Src/subst.c b/Src/subst.c
index 528912134..febdc9bea 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -420,6 +420,22 @@ singsub(char **s)
     DPUTS(nonempty(&foo), "BUG: singsub() produced more than one word!");
 }
 
+/*
+ * Bit flags passed back from multsub() to paramsubst().
+ */
+enum {
+    /*
+     * Set if the string had whitespace at the start
+     * that should cause word splitting against any preceeding string.
+     */
+    WS_AT_START = 1,
+    /*
+     * Set if the string had whitespace at the end
+     * that should cause word splitting against any following string.
+     */
+    WS_AT_END = 2
+};
+
 /* Perform substitution on a single word, *s. Unlike with singsub(), the
  * result can be more than one word. If split is non-zero, the string is
  * first word-split using IFS, but only for non-quoted "whitespace" (as
@@ -432,14 +448,13 @@ singsub(char **s)
  * NULL to use IFS).  The return value is true iff the expansion resulted
  * 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.
+ * *ws_at_start is set to bits in the enum above as neeed.
  */
 
 /**/
 static int
 multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep,
-	int *ws_at_start)
+	int *ws_sub)
 {
     int l;
     char **r, **p, *x = *s;
@@ -455,7 +470,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep,
 	    l++;
 	    if (!iwsep(STOUC(c)))
 		break;
-	    *ws_at_start = 1;
+	    *ws_sub |= WS_AT_START;
 	}
     }
 
@@ -487,8 +502,10 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep,
 			if (!WC_ZISTYPE(c, ISEP))
 			    break;
 		    }
-		    if (!*x)
+		    if (!*x) {
+			*ws_sub |= WS_AT_END;
 			break;
+		    }
 		    insertlinknode(&foo, n, (void *)x), incnode(n);
 		}
 	    }
@@ -1730,7 +1747,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
      * whitespace.  However, if there's no "x" the whitespace is
      * simply removed.
      */
-    int ws_at_start = 0;
+    int ws_sub = 0;
 
     *s++ = '\0';
     /*
@@ -2280,7 +2297,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
 	 * necessary, when we handle (P) lower down.
 	 */
 	if (multsub(&val, 0, (aspar ? NULL : &aval), &isarr, NULL,
-		    &ws_at_start) && quoted) {
+		    &ws_sub) && quoted) {
 	    /* Empty quoted string --- treat as null string, not elided */
 	    isarr = -1;
 	    aval = (char **) hcalloc(sizeof(char *));
@@ -2751,7 +2768,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, &ws_at_start);
+			&isarr, NULL, &ws_sub);
 		copied = 1;
 		spbreak = 0;
 		/* Leave globsubst on if forced */
@@ -2780,14 +2797,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, &ws_at_start);
+			    NULL, &isarr, NULL, &ws_sub);
 		} else {
 		    if (spbreak)
 			split_flags = PREFORK_SPLIT|PREFORK_SHWORDSPLIT;
 		    else
 			split_flags = PREFORK_NOSHWORDSPLIT;
 		    multsub(&val, split_flags, &aval, &isarr, NULL,
-			    &ws_at_start);
+			    &ws_sub);
 		    spbreak = 0;
 		}
 		if (arrasg) {
@@ -3319,7 +3336,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
 	}
 	if (haserr || errflag)
 	    return NULL;
-	ws_at_start = 0;
+	ws_sub = 0;
     }
     /*
      * This handles taking a length with ${#foo} and variations.
@@ -3358,7 +3375,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;
+	ws_sub = 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
@@ -3388,7 +3405,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;
+	    ws_sub = 0;
 	}
 	if (!ssub && (spbreak || spsep)) {
 	    aval = sepsplit(val, spsep, 0, 1);
@@ -3673,10 +3690,15 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
      * 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) {
+    if ((ws_sub & WS_AT_START) && aptr > ostr) {
 	insertlinknode(l, n, dupstrpfx(ostr, aptr - ostr)), incnode(n);
 	ostr = aptr;
     }
+    /* Likewise at the end */
+    if ((ws_sub & WS_AT_END) && *fstr) {
+	insertlinknode(l, n, dupstring(fstr)); /* appended, no incnode */
+	*fstr = '\0';
+    }
     if (isarr) {
 	char *x;
 	char *y;
diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index 59c14a408..694b613c5 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -1751,26 +1751,26 @@
  }
  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}
+ do_test ${:- foo }
+ do_test ${:- foo bar }
+ do_test ${:- $foo }
+ do_test ${:- $foo2 }
+ do_test x${:- foo }y
+ do_test x${:- foo bar }y
+ do_test x${:- $foo }y
+ do_test x${:- $foo2 }y
+ do_test x${foo:+ $foo }y
  )
 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
+>3: x foo y
+>4: x foo bar y
+>3: x bar y
+>4: x bar bar y
+>3: x bar y
 
  (unsetopt shwordsplit # default, for clarity
  do_test() {
@@ -1778,23 +1778,23 @@
  }
  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}
+ do_test ${:- foo }
+ do_test ${:- foo bar }
+ do_test ${:- $foo }
+ do_test ${:- $foo2 }
+ do_test x${:- foo }y
+ do_test x${:- foo bar }y
+ do_test x${:- $foo }y
+ do_test x${:- $foo2 }y
+ do_test x${foo:+ $foo }y
  )
 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
+>1:  foo 
+>1:  foo bar 
+>1:  bar 
+>1:  bar bar 
+>1: x foo y
+>1: x foo bar y
+>1: x bar y
+>1: x bar bar y
+>1: x bar y