a57977
From 6b8f3f9906ee44be46e022480b6e01755feeaa99 Mon Sep 17 00:00:00 2001
a57977
From: Peter Stephenson <pws@zsh.org>
a57977
Date: Tue, 6 Jan 2015 17:05:17 +0000
a57977
Subject: [PATCH 1/4] Fix command substitutions to parse contents as they are
a57977
 read in.
a57977
a57977
Do this by refactoring misnamed lexsave()/lexrestore() to allow
a57977
continuity of history and input.
a57977
a57977
Add test.
a57977
a57977
Upstream-commit: c0d01a6fe0c67911650730cf13a2b9a0db16e59b
a57977
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
a57977
---
a57977
 Src/init.c            |   3 +-
a57977
 Src/input.c           |  13 +-
a57977
 Src/lex.c             | 498 ++++++++++++++++++++++++++++++++------------------
a57977
 Src/parse.c           |  29 ++-
a57977
 Src/zsh.h             |   9 +
a57977
 Test/D08cmdsubst.ztst |  42 +++++
a57977
 6 files changed, 401 insertions(+), 193 deletions(-)
a57977
a57977
diff --git a/Src/init.c b/Src/init.c
a57977
index 0742a9f..78f171d 100644
a57977
--- a/Src/init.c
a57977
+++ b/Src/init.c
a57977
@@ -129,7 +129,8 @@ loop(int toplevel, int justonce)
a57977
 	use_exit_printed = 0;
a57977
 	intr();			/* interrupts on            */
a57977
 	lexinit();              /* initialize lexical state */
a57977
-	if (!(prog = parse_event())) {	/* if we couldn't parse a list */
a57977
+	if (!(prog = parse_event(ENDINPUT))) {
a57977
+	    /* if we couldn't parse a list */
a57977
 	    hend(NULL);
a57977
 	    if ((tok == ENDINPUT && !errflag) ||
a57977
 		(tok == LEXERR && (!isset(SHINSTDIN) || !toplevel)) ||
a57977
diff --git a/Src/input.c b/Src/input.c
a57977
index 5cff22d..1579762 100644
a57977
--- a/Src/input.c
a57977
+++ b/Src/input.c
a57977
@@ -175,12 +175,12 @@ shingetline(void)
a57977
 /* Get the next character from the input.
a57977
  * Will call inputline() to get a new line where necessary.
a57977
  */
a57977
-  
a57977
+
a57977
 /**/
a57977
 int
a57977
 ingetc(void)
a57977
 {
a57977
-    int lastc;
a57977
+    int lastc = ' ';
a57977
 
a57977
     if (lexstop)
a57977
 	return ' ';
a57977
@@ -192,7 +192,7 @@ ingetc(void)
a57977
 		continue;
a57977
 	    if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n')
a57977
 		lineno++;
a57977
-	    return lastc;
a57977
+	    break;
a57977
 	}
a57977
 
a57977
 	/*
a57977
@@ -204,7 +204,7 @@ ingetc(void)
a57977
 	 */
a57977
 	if (!inbufct && (strin || errflag)) {
a57977
 	    lexstop = 1;
a57977
-	    return ' ';
a57977
+	    break;
a57977
 	}
a57977
 	/* If the next element down the input stack is a continuation of
a57977
 	 * this, use it.
a57977
@@ -215,8 +215,10 @@ ingetc(void)
a57977
 	}
a57977
 	/* As a last resort, get some more input */
a57977
 	if (inputline())
a57977
-	    return ' ';
a57977
+	    break;
a57977
     }
a57977
+    zshlex_raw_add(lastc);
a57977
+    return lastc;
a57977
 }
a57977
 
a57977
 /* Read a line from the current command stream and store it as input */
a57977
@@ -421,6 +423,7 @@ inungetc(int c)
a57977
 	    inbufleft = 0;
a57977
 	    inbuf = inbufptr = "";
a57977
 	}
a57977
+	zshlex_raw_back();
a57977
     }
a57977
 }
a57977
 
a57977
diff --git a/Src/lex.c b/Src/lex.c
a57977
index 82bf848..bcceda6 100644
a57977
--- a/Src/lex.c
a57977
+++ b/Src/lex.c
a57977
@@ -146,6 +146,16 @@ mod_export int parend;
a57977
 /**/
a57977
 mod_export int nocomments;
a57977
 
a57977
+/* add raw input characters while parsing command substitution */
a57977
+
a57977
+/**/
a57977
+static int lex_add_raw;
a57977
+
a57977
+/* variables associated with the above */
a57977
+
a57977
+static char *tokstr_raw, *bptr_raw;
a57977
+static int len_raw, bsiz_raw;
a57977
+
a57977
 /* text of punctuation tokens */
a57977
 
a57977
 /**/
a57977
@@ -214,6 +224,11 @@ struct lexstack {
a57977
     char *bptr;
a57977
     int bsiz;
a57977
     int len;
a57977
+    int lex_add_raw;
a57977
+    char *tokstr_raw;
a57977
+    char *bptr_raw;
a57977
+    int bsiz_raw;
a57977
+    int len_raw;
a57977
     short *chwords;
a57977
     int chwordlen;
a57977
     int chwordpos;
a57977
@@ -239,89 +254,121 @@ struct lexstack {
a57977
 
a57977
 static struct lexstack *lstack = NULL;
a57977
 
a57977
-/* save the lexical state */
a57977
+/* save the context or parts thereof */
a57977
 
a57977
 /* is this a hack or what? */
a57977
 
a57977
 /**/
a57977
 mod_export void
a57977
-lexsave(void)
a57977
+lexsave_partial(int parts)
a57977
 {
a57977
     struct lexstack *ls;
a57977
 
a57977
     ls = (struct lexstack *)malloc(sizeof(struct lexstack));
a57977
 
a57977
-    ls->incmdpos = incmdpos;
a57977
-    ls->incond = incond;
a57977
-    ls->incasepat = incasepat;
a57977
-    ls->dbparens = dbparens;
a57977
-    ls->isfirstln = isfirstln;
a57977
-    ls->isfirstch = isfirstch;
a57977
-    ls->histactive = histactive;
a57977
-    ls->histdone = histdone;
a57977
-    ls->lexflags = lexflags;
a57977
-    ls->stophist = stophist;
a57977
-    stophist = 0;
a57977
-    if (!lstack) {
a57977
-	/* top level, make this version visible to ZLE */
a57977
-	zle_chline = chline;
a57977
-	/* ensure line stored is NULL-terminated */
a57977
-	if (hptr)
a57977
-	    *hptr = '\0';
a57977
+    if (parts & ZCONTEXT_LEX) {
a57977
+	ls->incmdpos = incmdpos;
a57977
+	ls->incond = incond;
a57977
+	ls->incasepat = incasepat;
a57977
+	ls->dbparens = dbparens;
a57977
+	ls->isfirstln = isfirstln;
a57977
+	ls->isfirstch = isfirstch;
a57977
+	ls->lexflags = lexflags;
a57977
+
a57977
+	ls->tok = tok;
a57977
+	ls->isnewlin = isnewlin;
a57977
+	ls->tokstr = tokstr;
a57977
+	ls->zshlextext = zshlextext;
a57977
+	ls->bptr = bptr;
a57977
+	ls->bsiz = bsiz;
a57977
+	ls->len = len;
a57977
+	ls->lex_add_raw = lex_add_raw;
a57977
+	ls->tokstr_raw = tokstr_raw;
a57977
+	ls->bptr_raw = bptr_raw;
a57977
+	ls->bsiz_raw = bsiz_raw;
a57977
+	ls->len_raw = len_raw;
a57977
+	ls->lexstop = lexstop;
a57977
+	ls->toklineno = toklineno;
a57977
+
a57977
+	tokstr = zshlextext = bptr = NULL;
a57977
+	bsiz = 256;
a57977
+	tokstr_raw = bptr_raw = NULL;
a57977
+	bsiz_raw = len_raw = lex_add_raw = 0;
a57977
+
a57977
+	inredir = 0;
a57977
+    }
a57977
+    if (parts & ZCONTEXT_HIST) {
a57977
+	if (!lstack) {
a57977
+	    /* top level, make this version visible to ZLE */
a57977
+	    zle_chline = chline;
a57977
+	    /* ensure line stored is NULL-terminated */
a57977
+	    if (hptr)
a57977
+		*hptr = '\0';
a57977
+	}
a57977
+	ls->histactive = histactive;
a57977
+	ls->histdone = histdone;
a57977
+	ls->stophist = stophist;
a57977
+	ls->hline = chline;
a57977
+	ls->hptr = hptr;
a57977
+	ls->chwords = chwords;
a57977
+	ls->chwordlen = chwordlen;
a57977
+	ls->chwordpos = chwordpos;
a57977
+	ls->hwgetword = hwgetword;
a57977
+	ls->hgetc = hgetc;
a57977
+	ls->hungetc = hungetc;
a57977
+	ls->hwaddc = hwaddc;
a57977
+	ls->hwbegin = hwbegin;
a57977
+	ls->hwend = hwend;
a57977
+	ls->addtoline = addtoline;
a57977
+	ls->hlinesz = hlinesz;
a57977
+	/*
a57977
+	 * We save and restore the command stack with history
a57977
+	 * as it's visible to the user interactively, so if
a57977
+	 * we're preserving history state we'll continue to
a57977
+	 * show the current set of commands from input.
a57977
+	 */
a57977
+	ls->cstack = cmdstack;
a57977
+	ls->csp = cmdsp;
a57977
+
a57977
+	stophist = 0;
a57977
+	chline = NULL;
a57977
+	hptr = NULL;
a57977
+	histactive = 0;
a57977
+	cmdstack = (unsigned char *)zalloc(CMDSTACKSZ);
a57977
+	cmdsp = 0;
a57977
+    }
a57977
+    if (parts & ZCONTEXT_PARSE) {
a57977
+	ls->hdocs = hdocs;
a57977
+	ls->eclen = eclen;
a57977
+	ls->ecused = ecused;
a57977
+	ls->ecnpats = ecnpats;
a57977
+	ls->ecbuf = ecbuf;
a57977
+	ls->ecstrs = ecstrs;
a57977
+	ls->ecsoffs = ecsoffs;
a57977
+	ls->ecssub = ecssub;
a57977
+	ls->ecnfunc = ecnfunc;
a57977
+	ecbuf = NULL;
a57977
+	hdocs = NULL;
a57977
     }
a57977
-    ls->hline = chline;
a57977
-    chline = NULL;
a57977
-    ls->hptr = hptr;
a57977
-    hptr = NULL;
a57977
-    ls->hlinesz = hlinesz;
a57977
-    ls->cstack = cmdstack;
a57977
-    ls->csp = cmdsp;
a57977
-    cmdstack = (unsigned char *)zalloc(CMDSTACKSZ);
a57977
-    ls->tok = tok;
a57977
-    ls->isnewlin = isnewlin;
a57977
-    ls->tokstr = tokstr;
a57977
-    ls->zshlextext = zshlextext;
a57977
-    ls->bptr = bptr;
a57977
-    tokstr = zshlextext = bptr = NULL;
a57977
-    ls->bsiz = bsiz;
a57977
-    bsiz = 256;
a57977
-    ls->len = len;
a57977
-    ls->chwords = chwords;
a57977
-    ls->chwordlen = chwordlen;
a57977
-    ls->chwordpos = chwordpos;
a57977
-    ls->hwgetword = hwgetword;
a57977
-    ls->lexstop = lexstop;
a57977
-    ls->hdocs = hdocs;
a57977
-    ls->hgetc = hgetc;
a57977
-    ls->hungetc = hungetc;
a57977
-    ls->hwaddc = hwaddc;
a57977
-    ls->hwbegin = hwbegin;
a57977
-    ls->hwend = hwend;
a57977
-    ls->addtoline = addtoline;
a57977
-    ls->eclen = eclen;
a57977
-    ls->ecused = ecused;
a57977
-    ls->ecnpats = ecnpats;
a57977
-    ls->ecbuf = ecbuf;
a57977
-    ls->ecstrs = ecstrs;
a57977
-    ls->ecsoffs = ecsoffs;
a57977
-    ls->ecssub = ecssub;
a57977
-    ls->ecnfunc = ecnfunc;
a57977
-    ls->toklineno = toklineno;
a57977
-    cmdsp = 0;
a57977
-    inredir = 0;
a57977
-    hdocs = NULL;
a57977
-    histactive = 0;
a57977
-    ecbuf = NULL;
a57977
 
a57977
     ls->next = lstack;
a57977
     lstack = ls;
a57977
 }
a57977
 
a57977
-/* restore lexical state */
a57977
+/* save context in full */
a57977
 
a57977
 /**/
a57977
 mod_export void
a57977
-lexrestore(void)
a57977
+lexsave(void)
a57977
+{
a57977
+    lexsave_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE);
a57977
+}
a57977
+
a57977
+/* restore context or part therefore */
a57977
+
a57977
+/**/
a57977
+mod_export void
a57977
+lexrestore_partial(int parts)
a57977
 {
a57977
     struct lexstack *ln = lstack;
a57977
 
a57977
@@ -330,65 +377,89 @@ lexrestore(void)
a57977
     queue_signals();
a57977
     lstack = lstack->next;
a57977
 
a57977
-    if (!lstack) {
a57977
-	/* Back to top level: don't need special ZLE value */
a57977
-	DPUTS(ln->hline != zle_chline, "BUG: Ouch, wrong chline for ZLE");
a57977
-	zle_chline = NULL;
a57977
+    if (parts & ZCONTEXT_LEX) {
a57977
+	incmdpos = ln->incmdpos;
a57977
+	incond = ln->incond;
a57977
+	incasepat = ln->incasepat;
a57977
+	dbparens = ln->dbparens;
a57977
+	isfirstln = ln->isfirstln;
a57977
+	isfirstch = ln->isfirstch;
a57977
+	lexflags = ln->lexflags;
a57977
+	tok = ln->tok;
a57977
+	isnewlin = ln->isnewlin;
a57977
+	tokstr = ln->tokstr;
a57977
+	zshlextext = ln->zshlextext;
a57977
+	bptr = ln->bptr;
a57977
+	bsiz = ln->bsiz;
a57977
+	len = ln->len;
a57977
+	lex_add_raw = ln->lex_add_raw;
a57977
+	tokstr_raw = ln->tokstr_raw;
a57977
+	bptr_raw = ln->bptr_raw;
a57977
+	bsiz_raw = ln->bsiz_raw;
a57977
+	len_raw = ln->len_raw;
a57977
+	lexstop = ln->lexstop;
a57977
+	toklineno = ln->toklineno;
a57977
+    }
a57977
+
a57977
+    if (parts & ZCONTEXT_HIST) {
a57977
+	if (!lstack) {
a57977
+	    /* Back to top level: don't need special ZLE value */
a57977
+	    DPUTS(ln->hline != zle_chline, "BUG: Ouch, wrong chline for ZLE");
a57977
+	    zle_chline = NULL;
a57977
+	}
a57977
+	histactive = ln->histactive;
a57977
+	histdone = ln->histdone;
a57977
+	stophist = ln->stophist;
a57977
+	chline = ln->hline;
a57977
+	hptr = ln->hptr;
a57977
+	chwords = ln->chwords;
a57977
+	chwordlen = ln->chwordlen;
a57977
+	chwordpos = ln->chwordpos;
a57977
+	hwgetword = ln->hwgetword;
a57977
+	hgetc = ln->hgetc;
a57977
+	hungetc = ln->hungetc;
a57977
+	hwaddc = ln->hwaddc;
a57977
+	hwbegin = ln->hwbegin;
a57977
+	hwend = ln->hwend;
a57977
+	addtoline = ln->addtoline;
a57977
+	hlinesz = ln->hlinesz;
a57977
+	if (cmdstack)
a57977
+	    zfree(cmdstack, CMDSTACKSZ);
a57977
+	cmdstack = ln->cstack;
a57977
+	cmdsp = ln->csp;
a57977
+    }
a57977
+
a57977
+    if (parts & ZCONTEXT_PARSE) {
a57977
+	if (ecbuf)
a57977
+	    zfree(ecbuf, eclen);
a57977
+
a57977
+	hdocs = ln->hdocs;
a57977
+	eclen = ln->eclen;
a57977
+	ecused = ln->ecused;
a57977
+	ecnpats = ln->ecnpats;
a57977
+	ecbuf = ln->ecbuf;
a57977
+	ecstrs = ln->ecstrs;
a57977
+	ecsoffs = ln->ecsoffs;
a57977
+	ecssub = ln->ecssub;
a57977
+	ecnfunc = ln->ecnfunc;
a57977
+
a57977
+	errflag = 0;
a57977
     }
a57977
 
a57977
-    incmdpos = ln->incmdpos;
a57977
-    incond = ln->incond;
a57977
-    incasepat = ln->incasepat;
a57977
-    dbparens = ln->dbparens;
a57977
-    isfirstln = ln->isfirstln;
a57977
-    isfirstch = ln->isfirstch;
a57977
-    histactive = ln->histactive;
a57977
-    histdone = ln->histdone;
a57977
-    lexflags = ln->lexflags;
a57977
-    stophist = ln->stophist;
a57977
-    chline = ln->hline;
a57977
-    hptr = ln->hptr;
a57977
-    if (cmdstack)
a57977
-	zfree(cmdstack, CMDSTACKSZ);
a57977
-    cmdstack = ln->cstack;
a57977
-    cmdsp = ln->csp;
a57977
-    tok = ln->tok;
a57977
-    isnewlin = ln->isnewlin;
a57977
-    tokstr = ln->tokstr;
a57977
-    zshlextext = ln->zshlextext;
a57977
-    bptr = ln->bptr;
a57977
-    bsiz = ln->bsiz;
a57977
-    len = ln->len;
a57977
-    chwords = ln->chwords;
a57977
-    chwordlen = ln->chwordlen;
a57977
-    chwordpos = ln->chwordpos;
a57977
-    hwgetword = ln->hwgetword;
a57977
-    lexstop = ln->lexstop;
a57977
-    hdocs = ln->hdocs;
a57977
-    hgetc = ln->hgetc;
a57977
-    hungetc = ln->hungetc;
a57977
-    hwaddc = ln->hwaddc;
a57977
-    hwbegin = ln->hwbegin;
a57977
-    hwend = ln->hwend;
a57977
-    addtoline = ln->addtoline;
a57977
-    if (ecbuf)
a57977
-	zfree(ecbuf, eclen);
a57977
-    eclen = ln->eclen;
a57977
-    ecused = ln->ecused;
a57977
-    ecnpats = ln->ecnpats;
a57977
-    ecbuf = ln->ecbuf;
a57977
-    ecstrs = ln->ecstrs;
a57977
-    ecsoffs = ln->ecsoffs;
a57977
-    ecssub = ln->ecssub;
a57977
-    ecnfunc = ln->ecnfunc;
a57977
-    hlinesz = ln->hlinesz;
a57977
-    toklineno = ln->toklineno;
a57977
-    errflag = 0;
a57977
     free(ln);
a57977
 
a57977
     unqueue_signals();
a57977
 }
a57977
 
a57977
+/* complete restore context */
a57977
+
a57977
+/**/
a57977
+mod_export void
a57977
+lexrestore(void)
a57977
+{
a57977
+    lexrestore_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE);
a57977
+}
a57977
+
a57977
 /**/
a57977
 void
a57977
 zshlex(void)
a57977
@@ -1889,80 +1960,151 @@ exalias(void)
a57977
     return 0;
a57977
 }
a57977
 
a57977
-/* skip (...) */
a57977
+/**/
a57977
+void
a57977
+zshlex_raw_add(int c)
a57977
+{
a57977
+    if (!lex_add_raw)
a57977
+	return;
a57977
+
a57977
+    *bptr_raw++ = c;
a57977
+    if (bsiz_raw == ++len_raw) {
a57977
+	int newbsiz = bsiz_raw * 2;
a57977
+
a57977
+	tokstr_raw = (char *)hrealloc(tokstr_raw, bsiz_raw, newbsiz);
a57977
+	bptr_raw = tokstr_raw + len_raw;
a57977
+	memset(bptr_raw, 0, newbsiz - bsiz_raw);
a57977
+	bsiz_raw = newbsiz;
a57977
+    }
a57977
+}
a57977
+
a57977
+/**/
a57977
+void
a57977
+zshlex_raw_back(void)
a57977
+{
a57977
+    if (!lex_add_raw)
a57977
+	return;
a57977
+    bptr_raw--;
a57977
+    len_raw--;
a57977
+}
a57977
+
a57977
+/*
a57977
+ * Skip (...) for command-style substitutions: $(...), <(...), >(...)
a57977
+ *
a57977
+ * In order to ensure we don't stop at closing parentheses with
a57977
+ * some other syntactic significance, we'll parse the input until
a57977
+ * we find an unmatched closing parenthesis.  However, we'll throw
a57977
+ * away the result of the parsing and just keep the string we've built
a57977
+ * up on the way.
a57977
+ */
a57977
 
a57977
 /**/
a57977
 static int
a57977
 skipcomm(void)
a57977
 {
a57977
-    int pct = 1, c, start = 1;
a57977
+    char *new_tokstr, *new_bptr = bptr_raw;
a57977
+    int new_len, new_bsiz, new_lexstop, new_lex_add_raw;
a57977
 
a57977
     cmdpush(CS_CMDSUBST);
a57977
     SETPARBEGIN
a57977
-    c = Inpar;
a57977
-    do {
a57977
-	int iswhite;
a57977
-	add(c);
a57977
-	c = hgetc();
a57977
-	if (itok(c) || lexstop)
a57977
-	    break;
a57977
-	iswhite = inblank(c);
a57977
-	switch (c) {
a57977
-	case '(':
a57977
-	    pct++;
a57977
-	    break;
a57977
-	case ')':
a57977
-	    pct--;
a57977
-	    break;
a57977
-	case '\\':
a57977
-	    add(c);
a57977
-	    c = hgetc();
a57977
-	    break;
a57977
-	case '\'': {
a57977
-	    int strquote = bptr[-1] == '$';
a57977
-	    add(c);
a57977
-	    STOPHIST
a57977
-	    while ((c = hgetc()) != '\'' && !lexstop) {
a57977
-		if (c == '\\' && strquote) {
a57977
-		    add(c);
a57977
-		    c = hgetc();
a57977
-		}
a57977
-		add(c);
a57977
-	    }
a57977
-	    ALLOWHIST
a57977
-	    break;
a57977
-	}
a57977
-	case '\"':
a57977
-	    add(c);
a57977
-	    while ((c = hgetc()) != '\"' && !lexstop)
a57977
-		if (c == '\\') {
a57977
-		    add(c);
a57977
-		    add(hgetc());
a57977
-		} else
a57977
-		    add(c);
a57977
-	    break;
a57977
-	case '`':
a57977
-	    add(c);
a57977
-	    while ((c = hgetc()) != '`' && !lexstop)
a57977
-		if (c == '\\')
a57977
-		    add(c), add(hgetc());
a57977
-		else
a57977
-		    add(c);
a57977
-	    break;
a57977
-	case '#':
a57977
-	    if (start) {
a57977
-		add(c);
a57977
-		while ((c = hgetc()) != '\n' && !lexstop)
a57977
-		    add(c);
a57977
-		iswhite = 1;
a57977
-	    }
a57977
-	    break;
a57977
+    add(Inpar);
a57977
+
a57977
+    new_lex_add_raw = lex_add_raw + 1;
a57977
+    if (!lex_add_raw) {
a57977
+	/*
a57977
+	 * We'll combine the string so far with the input
a57977
+	 * read in for the command substitution.  To do this
a57977
+	 * we'll just propagate the current tokstr etc. as the
a57977
+	 * variables used for adding raw input, and
a57977
+	 * ensure we swap those for the real tokstr etc. at the end.
a57977
+	 *
a57977
+	 * However, we need to save and restore the rest of the
a57977
+	 * lexical and parse state as we're effectively parsing
a57977
+	 * an internal string.  Because we're still parsing it from
a57977
+	 * the original input source (we have to --- we don't know
a57977
+	 * when to stop inputting it otherwise and can't rely on
a57977
+	 * the input being recoverable until we've read it) we need
a57977
+	 * to keep the same history context.
a57977
+	 */
a57977
+	new_tokstr = tokstr;
a57977
+	new_bptr = bptr;
a57977
+	new_len = len;
a57977
+	new_bsiz = bsiz;
a57977
+
a57977
+	lexsave_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
a57977
+    } else {
a57977
+	/*
a57977
+	 * Set up for nested command subsitution, however
a57977
+	 * we don't actually need the string until we get
a57977
+	 * back to the top level and recover the lot.
a57977
+	 * The $() body just appears empty.
a57977
+	 *
a57977
+	 * We do need to propagate the raw variables which would
a57977
+	 * otherwise by cleared, though.
a57977
+	 */
a57977
+	new_tokstr = tokstr_raw;
a57977
+	new_bptr = bptr_raw;
a57977
+	new_len = len_raw;
a57977
+	new_bsiz = bsiz_raw;
a57977
+
a57977
+	lexsave_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
a57977
+    }
a57977
+    tokstr_raw = new_tokstr;
a57977
+    bsiz_raw = new_bsiz;
a57977
+    len_raw = new_len;
a57977
+    bptr_raw = new_bptr;
a57977
+    lex_add_raw = new_lex_add_raw;
a57977
+
a57977
+    if (!parse_event(OUTPAR) || tok != OUTPAR)
a57977
+	lexstop = 1;
a57977
+     /* Outpar lexical token gets added in caller if present */
a57977
+
a57977
+    /*
a57977
+     * We're going to keep the full raw input string
a57977
+     * as the current token string after popping the stack.
a57977
+     */
a57977
+    new_tokstr = tokstr_raw;
a57977
+    new_bptr = bptr_raw;
a57977
+    new_len = len_raw;
a57977
+    new_bsiz = bsiz_raw;
a57977
+    /*
a57977
+     * We're also going to propagate the lexical state:
a57977
+     * if we couldn't parse the command substitution we
a57977
+     * can't continue.
a57977
+     */
a57977
+    new_lexstop = lexstop;
a57977
+
a57977
+    lexrestore_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
a57977
+
a57977
+    if (lex_add_raw) {
a57977
+	/*
a57977
+	 * Keep going, so retain the raw variables.
a57977
+	 */
a57977
+	tokstr_raw = new_tokstr;
a57977
+	bptr_raw = new_bptr;
a57977
+	len_raw = new_len;
a57977
+	bsiz_raw = new_bsiz;
a57977
+    } else {
a57977
+	if (!new_lexstop) {
a57977
+	    /* Ignore the ')' added on input */
a57977
+	    new_len--;
a57977
+	    *--new_bptr = '\0';
a57977
 	}
a57977
-	start = iswhite;
a57977
+
a57977
+	/*
a57977
+	 * Convince the rest of lex.c we were examining a string
a57977
+	 * all along.
a57977
+	 */
a57977
+	tokstr = new_tokstr;
a57977
+	bptr = new_bptr;
a57977
+	len = new_len;
a57977
+	bsiz = new_bsiz;
a57977
+	lexstop = new_lexstop;
a57977
     }
a57977
-    while (pct);
a57977
+
a57977
     if (!lexstop)
a57977
 	SETPAREND
a57977
     cmdpop();
a57977
+
a57977
     return lexstop;
a57977
 }
a57977
diff --git a/Src/parse.c b/Src/parse.c
a57977
index 753080d..b0a7624 100644
a57977
--- a/Src/parse.c
a57977
+++ b/Src/parse.c
a57977
@@ -360,7 +360,8 @@ ecstrcode(char *s)
a57977
 
a57977
 /* Initialise wordcode buffer. */
a57977
 
a57977
-static void
a57977
+/**/
a57977
+void
a57977
 init_parse(void)
a57977
 {
a57977
     if (ecbuf) zfree(ecbuf, eclen);
a57977
@@ -439,11 +440,15 @@ clear_hdocs()
a57977
  * event	: ENDINPUT
a57977
  *			| SEPER
a57977
  *			| sublist [ SEPER | AMPER | AMPERBANG ]
a57977
+ *
a57977
+ * cmdsubst indicates our event is part of a command-style
a57977
+ * substitution terminated by the token indicationg, usual closing
a57977
+ * parenthesis.  In other cases endtok is ENDINPUT.
a57977
  */
a57977
 
a57977
 /**/
a57977
 Eprog
a57977
-parse_event(void)
a57977
+parse_event(int endtok)
a57977
 {
a57977
     tok = ENDINPUT;
a57977
     incmdpos = 1;
a57977
@@ -451,36 +456,42 @@ parse_event(void)
a57977
     zshlex();
a57977
     init_parse();
a57977
 
a57977
-    if (!par_event()) {
a57977
+    if (!par_event(endtok)) {
a57977
         clear_hdocs();
a57977
         return NULL;
a57977
     }
a57977
+    if (endtok != ENDINPUT) {
a57977
+	/* don't need to build an eprog for this */
a57977
+	return &dummy_eprog;
a57977
+    }
a57977
     return bld_eprog();
a57977
 }
a57977
 
a57977
 /**/
a57977
-static int
a57977
-par_event(void)
a57977
+int
a57977
+par_event(int endtok)
a57977
 {
a57977
     int r = 0, p, c = 0;
a57977
 
a57977
     while (tok == SEPER) {
a57977
-	if (isnewlin > 0)
a57977
+	if (isnewlin > 0 && endtok == ENDINPUT)
a57977
 	    return 0;
a57977
 	zshlex();
a57977
     }
a57977
     if (tok == ENDINPUT)
a57977
 	return 0;
a57977
+    if (tok == endtok)
a57977
+	return 0;
a57977
 
a57977
     p = ecadd(0);
a57977
 
a57977
     if (par_sublist(&c)) {
a57977
-	if (tok == ENDINPUT) {
a57977
+	if (tok == ENDINPUT || tok == endtok) {
a57977
 	    set_list_code(p, Z_SYNC, c);
a57977
 	    r = 1;
a57977
 	} else if (tok == SEPER) {
a57977
 	    set_list_code(p, Z_SYNC, c);
a57977
-	    if (isnewlin <= 0)
a57977
+	    if (isnewlin <= 0 || endtok != ENDINPUT)
a57977
 		zshlex();
a57977
 	    r = 1;
a57977
 	} else if (tok == AMPER) {
a57977
@@ -509,7 +520,7 @@ par_event(void)
a57977
     } else {
a57977
 	int oec = ecused;
a57977
 
a57977
-	if (!par_event()) {
a57977
+	if (!par_event(endtok)) {
a57977
 	    ecused = oec;
a57977
 	    ecbuf[p] |= wc_bdata(Z_END);
a57977
 	}
a57977
diff --git a/Src/zsh.h b/Src/zsh.h
a57977
index 207ef18..b3391ed 100644
a57977
--- a/Src/zsh.h
a57977
+++ b/Src/zsh.h
a57977
@@ -395,6 +395,15 @@ enum {
a57977
 #define META_HEAPDUP	6
a57977
 #define META_HREALLOC	7
a57977
 
a57977
+/* Context to save and restore (bit fields) */
a57977
+enum {
a57977
+    /* History mechanism */
a57977
+    ZCONTEXT_HIST       = (1<<0),
a57977
+    /* Lexical analyser */
a57977
+    ZCONTEXT_LEX        = (1<<1),
a57977
+    /* Parser */
a57977
+    ZCONTEXT_PARSE      = (1<<2)
a57977
+};
a57977
 
a57977
 /**************************/
a57977
 /* Abstract types for zsh */
a57977
diff --git a/Test/D08cmdsubst.ztst b/Test/D08cmdsubst.ztst
a57977
index 5661b0a..a4c69a0 100644
a57977
--- a/Test/D08cmdsubst.ztst
a57977
+++ b/Test/D08cmdsubst.ztst
a57977
@@ -106,3 +106,45 @@
a57977
 >34
a57977
 >"
a57977
 >" OK
a57977
+
a57977
+ echo $(case foo in
a57977
+ foo)
a57977
+ echo This test worked.
a57977
+ ;;
a57977
+ bar)
a57977
+ echo This test failed in a rather bizarre way.
a57977
+ ;;
a57977
+ *)
a57977
+ echo This test failed.
a57977
+ ;;
a57977
+ esac)
a57977
+0:Parsing of command substitution with unmatched parentheses: case, basic
a57977
+>This test worked.
a57977
+
a57977
+ echo "$(case bar in
a57977
+ foo)
a57977
+ echo This test spoobed.
a57977
+ ;;
a57977
+ bar)
a57977
+ echo This test plurbled.
a57977
+ ;;
a57977
+ *)
a57977
+ echo This test bzonked.
a57977
+ ;;
a57977
+ esac)"
a57977
+0:Parsing of command substitution with unmatched parentheses: case with quotes
a57977
+>This test plurbled.
a57977
+
a57977
+ echo before $(
a57977
+ echo start; echo unpretentious |
a57977
+ while read line; do
a57977
+   case $line in
a57977
+   u*)
a57977
+   print Word began with u
a57977
+   print and ended with a crunch
a57977
+   ;;
a57977
+   esac
a57977
+ done | sed -e 's/Word/Universe/'; echo end
a57977
+ ) after
a57977
+0:Parsing of command substitution with ummatched parentheses: with frills
a57977
+>before start Universe began with u and ended with a crunch end after
a57977
-- 
a57977
2.4.3
a57977
a57977
a57977
From 00bc31b497525433dbaeafd3e7b92c7fe364dc8c Mon Sep 17 00:00:00 2001
a57977
From: Peter Stephenson <pws@zsh.org>
a57977
Date: Wed, 15 Apr 2015 10:20:06 +0100
a57977
Subject: [PATCH 2/4] 34892 (slightly tweaked): math evaluation fix
a57977
a57977
An empty expression resulting from substitution includes a
a57977
Nularg, which needs handling the same as an empty string.
a57977
a57977
Upstream-commit: 2ef4b38461dfb554ed2226d9de8958703bc00b98
a57977
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
a57977
---
a57977
 Src/math.c         | 15 ++++++++++++++-
a57977
 Test/C01arith.ztst |  4 ++++
a57977
 2 files changed, 18 insertions(+), 1 deletion(-)
a57977
a57977
diff --git a/Src/math.c b/Src/math.c
a57977
index e90d6a5..d6db7d3 100644
a57977
--- a/Src/math.c
a57977
+++ b/Src/math.c
a57977
@@ -1330,7 +1330,7 @@ matheval(char *s)
a57977
     if (!mlevel)
a57977
 	outputradix = 0;
a57977
 
a57977
-    if (!*s) {
a57977
+    if (!*s || *s == Nularg) {
a57977
 	x.type = MN_INTEGER;
a57977
 	x.u.l = 0;
a57977
 	return x;
a57977
@@ -1358,6 +1358,19 @@ mathevalarg(char *s, char **ss)
a57977
     mnumber x;
a57977
     int xmtok = mtok;
a57977
 
a57977
+    /*
a57977
+     * At this entry point we don't allow an empty expression,
a57977
+     * whereas we do with matheval().  I'm not sure if this
a57977
+     * difference is deliberate, but it does mean that e.g.
a57977
+     * $array[$ind] where ind hasn't been set produces an error,
a57977
+     * which is probably safe.
a57977
+     *
a57977
+     * To avoid a more opaque error further in, bail out here.
a57977
+     */
a57977
+    if (!*s || *s == Nularg) {
a57977
+       zerr("bad math expression: empty string");
a57977
+       return (zlong)0;
a57977
+    }
a57977
     x = mathevall(s, MPREC_ARG, ss);
a57977
     if (mtok == COMMA)
a57977
 	(*ss)--;
a57977
diff --git a/Test/C01arith.ztst b/Test/C01arith.ztst
a57977
index 02d1519..33b03ef 100644
a57977
--- a/Test/C01arith.ztst
a57977
+++ b/Test/C01arith.ztst
a57977
@@ -243,3 +243,7 @@
a57977
 >6000000
a57977
 >5000
a57977
 >255
a57977
+
a57977
+  print $((`:`))
a57977
+0:Null string in arithmetic evaluation after command substitution
a57977
+>0
a57977
-- 
a57977
2.4.6
a57977
a57977
a57977
From 0c1450a286e578a1cfe266bf743faf2f0719f85b Mon Sep 17 00:00:00 2001
a57977
From: "Barton E. Schaefer" <schaefer@zsh.org>
a57977
Date: Wed, 29 Jul 2015 22:36:45 -0700
a57977
Subject: [PATCH 3/4] 35953: fix handling of command substitution in math
a57977
 context
a57977
a57977
Upstream-commit: c0a80171ee615b52a15a6fc8efe83c2bb53451d2
a57977
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
a57977
---
a57977
 Src/lex.c            | 6 +++++-
a57977
 Test/A01grammar.ztst | 6 ++++++
a57977
 2 files changed, 11 insertions(+), 1 deletion(-)
a57977
a57977
diff --git a/Src/lex.c b/Src/lex.c
a57977
index bcceda6..f43b92b 100644
a57977
--- a/Src/lex.c
a57977
+++ b/Src/lex.c
a57977
@@ -1541,7 +1541,7 @@ dquote_parse(char endchar, int sub)
a57977
 {
a57977
     int pct = 0, brct = 0, bct = 0, intick = 0, err = 0;
a57977
     int c;
a57977
-    int math = endchar == ')' || endchar == ']';
a57977
+    int math = endchar == ')' || endchar == ']' || infor;
a57977
     int zlemath = math && zlemetacs > zlemetall + addedx - inbufct;
a57977
 
a57977
     while (((c = hgetc()) != endchar || bct ||
a57977
@@ -2004,7 +2004,9 @@ skipcomm(void)
a57977
 {
a57977
     char *new_tokstr, *new_bptr = bptr_raw;
a57977
     int new_len, new_bsiz, new_lexstop, new_lex_add_raw;
a57977
+    int save_infor = infor;
a57977
 
a57977
+    infor = 0;
a57977
     cmdpush(CS_CMDSUBST);
a57977
     SETPARBEGIN
a57977
     add(Inpar);
a57977
@@ -2054,6 +2056,7 @@ skipcomm(void)
a57977
     len_raw = new_len;
a57977
     bptr_raw = new_bptr;
a57977
     lex_add_raw = new_lex_add_raw;
a57977
+    dbparens = 0;	/* restored by zcontext_restore_partial() */
a57977
 
a57977
     if (!parse_event(OUTPAR) || tok != OUTPAR)
a57977
 	lexstop = 1;
a57977
@@ -2105,6 +2108,7 @@ skipcomm(void)
a57977
     if (!lexstop)
a57977
 	SETPAREND
a57977
     cmdpop();
a57977
+    infor = save_infor;
a57977
 
a57977
     return lexstop;
a57977
 }
a57977
diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst
a57977
index f04ddda..584ebd6 100644
a57977
--- a/Test/A01grammar.ztst
a57977
+++ b/Test/A01grammar.ztst
a57977
@@ -169,6 +169,12 @@
a57977
 >1
a57977
 >2
a57977
 
a57977
+  for (( $(true); ; )); do break; done
a57977
+  for (( ; $(true); )); do break; done
a57977
+  for (( ; ; $(true) )); do break; done
a57977
+  for (( ; $((1)); )); do break; done
a57977
+0:regression test, nested cmdsubst in arithmetic `for' loop
a57977
+
a57977
   for keyvar valvar in key1 val1 key2 val2; do
a57977
      print key=$keyvar val=$valvar
a57977
   done
a57977
-- 
a57977
2.4.6
a57977
a57977
a57977
From 821815bd9c24a84d8bb5796732ab6144b35e7d27 Mon Sep 17 00:00:00 2001
a57977
From: Peter Stephenson <p.w.stephenson@ntlworld.com>
a57977
Date: Sat, 10 Jan 2015 20:28:57 +0000
a57977
Subject: [PATCH 4/4] 34220: new $(...) handling needs to back up over alias
a57977
 expansion
a57977
a57977
Upstream-commit: 3b32abafdb019cfb8f29908bc3d148e01518981d
a57977
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
a57977
---
a57977
 Src/input.c | 6 ++++++
a57977
 1 file changed, 6 insertions(+)
a57977
a57977
diff --git a/Src/input.c b/Src/input.c
a57977
index 1579762..5b782dc 100644
a57977
--- a/Src/input.c
a57977
+++ b/Src/input.c
a57977
@@ -532,6 +532,12 @@ inpush(char *str, int flags, Alias inalias)
a57977
 static void
a57977
 inpoptop(void)
a57977
 {
a57977
+    if (!lexstop) {
a57977
+	inbufflags &= ~INP_ALCONT;
a57977
+	while (inbufptr > inbuf)
a57977
+	    inungetc(inbufptr[-1]);
a57977
+    }
a57977
+
a57977
     if (inbuf && (inbufflags & INP_FREE))
a57977
 	free(inbuf);
a57977
 
a57977
-- 
a57977
2.4.6
a57977