summary refs log tree commit diff
path: root/Src/subst.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/subst.c')
-rw-r--r--Src/subst.c40
1 files changed, 34 insertions, 6 deletions
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;