From d067ebcacd55472f720b2765ec686a69b25c9a90 Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Sun, 7 Dec 2014 16:24:19 +0000
Subject: 33876: etc.: Separate errors and keyboards interrupts
Combination of 12 commits from interrupt_abort branch.
Basic strategy is to introduce bits to errflag and to set and
reset them separately.
Remove interrupt status on return to main keymap.
Turn off ERRFLAG_INT for always block.
Restore bit thereafter: we probably need a new variable in order
to allow user interrupts to be reset in the always block.
Add TRY_BLOCK_INTERRUPT
This works the same as TRY_BLOCK_ERROR, but for a SIGINT, too.
Ensure propagation of SIGINT from exited job.
If received by foreground job, shell uses ERRFLAG_INT, not
ERRFLAG_ERROR, to set the new state.
Reset errflag before precmd()
Add always block in _main_completion to fix ZLS_COLORS
Ensures we get the right state of $ZLS_COLORS at the end of _main_complete
even if there's an interrupt. However, the "right state" is a bit messy
as it depends on styles.
---
Src/input.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
(limited to 'Src/input.c')
diff --git a/Src/input.c b/Src/input.c
index 4ac7e6ec8..9552331a9 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -291,7 +291,8 @@ inputline(void)
}
if (errflag) {
free(ingetcline);
- return lexstop = errflag = 1;
+ errflag |= ERRFLAG_ERROR;
+ return lexstop = 1;
}
if (isset(VERBOSE)) {
/* Output the whole line read so far. */
--
cgit v1.2.3
From c0d01a6fe0c67911650730cf13a2b9a0db16e59b Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Tue, 6 Jan 2015 17:05:17 +0000
Subject: Fix command substitutions to parse contents as they are read in.
Do this by refactoring misnamed lexsave()/lexrestore() to allow
continuity of history and input.
Add test.
---
ChangeLog | 8 +
Src/init.c | 3 +-
Src/input.c | 13 +-
Src/lex.c | 498 ++++++++++++++++++++++++++++++++------------------
Src/parse.c | 29 ++-
Src/zsh.h | 9 +
Test/D08cmdsubst.ztst | 42 +++++
7 files changed, 409 insertions(+), 193 deletions(-)
(limited to 'Src/input.c')
diff --git a/ChangeLog b/ChangeLog
index db143e5bb..af07352db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2015-01-08 Peter Stephenson
+
+ * Src/init.c, Src/input.c, Src/lex.c, Src/parse.c, Src/zsh.h,
+ Test/D08cmdsubst.ztst: fix the problem that command and similar
+ substitutions weren't properly parsed so could end prematurely.
+ Use improved resolution in context save and restore to allow
+ parsing the substitution while tracking the string.
+
2015-01-07 Barton E. Schaefer
* 34154 (tweaked per 34155): Src/builtin.c: reorder bin_print() to
diff --git a/Src/init.c b/Src/init.c
index 305908724..080fc8561 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -142,7 +142,8 @@ loop(int toplevel, int justonce)
use_exit_printed = 0;
intr(); /* interrupts on */
lexinit(); /* initialize lexical state */
- if (!(prog = parse_event())) { /* if we couldn't parse a list */
+ if (!(prog = parse_event(ENDINPUT))) {
+ /* if we couldn't parse a list */
hend(NULL);
if ((tok == ENDINPUT && !errflag) ||
(tok == LEXERR && (!isset(SHINSTDIN) || !toplevel)) ||
diff --git a/Src/input.c b/Src/input.c
index 9552331a9..04dda5acd 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -179,12 +179,12 @@ shingetline(void)
/* Get the next character from the input.
* Will call inputline() to get a new line where necessary.
*/
-
+
/**/
int
ingetc(void)
{
- int lastc;
+ int lastc = ' ';
if (lexstop)
return ' ';
@@ -196,7 +196,7 @@ ingetc(void)
continue;
if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n')
lineno++;
- return lastc;
+ break;
}
/*
@@ -208,7 +208,7 @@ ingetc(void)
*/
if (!inbufct && (strin || errflag)) {
lexstop = 1;
- return ' ';
+ break;
}
/* If the next element down the input stack is a continuation of
* this, use it.
@@ -219,8 +219,10 @@ ingetc(void)
}
/* As a last resort, get some more input */
if (inputline())
- return ' ';
+ break;
}
+ zshlex_raw_add(lastc);
+ return lastc;
}
/* Read a line from the current command stream and store it as input */
@@ -426,6 +428,7 @@ inungetc(int c)
inbufleft = 0;
inbuf = inbufptr = "";
}
+ zshlex_raw_back();
}
}
diff --git a/Src/lex.c b/Src/lex.c
index 4addf8033..d440f3d70 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -148,6 +148,16 @@ mod_export int parend;
/**/
mod_export int nocomments;
+/* add raw input characters while parsing command substitution */
+
+/**/
+static int lex_add_raw;
+
+/* variables associated with the above */
+
+static char *tokstr_raw, *bptr_raw;
+static int len_raw, bsiz_raw;
+
/* text of punctuation tokens */
/**/
@@ -216,6 +226,11 @@ struct lexstack {
char *bptr;
int bsiz;
int len;
+ int lex_add_raw;
+ char *tokstr_raw;
+ char *bptr_raw;
+ int bsiz_raw;
+ int len_raw;
short *chwords;
int chwordlen;
int chwordpos;
@@ -241,89 +256,121 @@ struct lexstack {
static struct lexstack *lstack = NULL;
-/* save the lexical state */
+/* save the context or parts thereof */
/* is this a hack or what? */
/**/
mod_export void
-lexsave(void)
+lexsave_partial(int parts)
{
struct lexstack *ls;
ls = (struct lexstack *)malloc(sizeof(struct lexstack));
- ls->incmdpos = incmdpos;
- ls->incond = incond;
- ls->incasepat = incasepat;
- ls->dbparens = dbparens;
- ls->isfirstln = isfirstln;
- ls->isfirstch = isfirstch;
- ls->histactive = histactive;
- ls->histdone = histdone;
- ls->lexflags = lexflags;
- ls->stophist = stophist;
- stophist = 0;
- if (!lstack) {
- /* top level, make this version visible to ZLE */
- zle_chline = chline;
- /* ensure line stored is NULL-terminated */
- if (hptr)
- *hptr = '\0';
+ if (parts & ZCONTEXT_LEX) {
+ ls->incmdpos = incmdpos;
+ ls->incond = incond;
+ ls->incasepat = incasepat;
+ ls->dbparens = dbparens;
+ ls->isfirstln = isfirstln;
+ ls->isfirstch = isfirstch;
+ ls->lexflags = lexflags;
+
+ ls->tok = tok;
+ ls->isnewlin = isnewlin;
+ ls->tokstr = tokstr;
+ ls->zshlextext = zshlextext;
+ ls->bptr = bptr;
+ ls->bsiz = bsiz;
+ ls->len = len;
+ ls->lex_add_raw = lex_add_raw;
+ ls->tokstr_raw = tokstr_raw;
+ ls->bptr_raw = bptr_raw;
+ ls->bsiz_raw = bsiz_raw;
+ ls->len_raw = len_raw;
+ ls->lexstop = lexstop;
+ ls->toklineno = toklineno;
+
+ tokstr = zshlextext = bptr = NULL;
+ bsiz = 256;
+ tokstr_raw = bptr_raw = NULL;
+ bsiz_raw = len_raw = lex_add_raw = 0;
+
+ inredir = 0;
+ }
+ if (parts & ZCONTEXT_HIST) {
+ if (!lstack) {
+ /* top level, make this version visible to ZLE */
+ zle_chline = chline;
+ /* ensure line stored is NULL-terminated */
+ if (hptr)
+ *hptr = '\0';
+ }
+ ls->histactive = histactive;
+ ls->histdone = histdone;
+ ls->stophist = stophist;
+ ls->hline = chline;
+ ls->hptr = hptr;
+ ls->chwords = chwords;
+ ls->chwordlen = chwordlen;
+ ls->chwordpos = chwordpos;
+ ls->hwgetword = hwgetword;
+ ls->hgetc = hgetc;
+ ls->hungetc = hungetc;
+ ls->hwaddc = hwaddc;
+ ls->hwbegin = hwbegin;
+ ls->hwend = hwend;
+ ls->addtoline = addtoline;
+ ls->hlinesz = hlinesz;
+ /*
+ * We save and restore the command stack with history
+ * as it's visible to the user interactively, so if
+ * we're preserving history state we'll continue to
+ * show the current set of commands from input.
+ */
+ ls->cstack = cmdstack;
+ ls->csp = cmdsp;
+
+ stophist = 0;
+ chline = NULL;
+ hptr = NULL;
+ histactive = 0;
+ cmdstack = (unsigned char *)zalloc(CMDSTACKSZ);
+ cmdsp = 0;
+ }
+ if (parts & ZCONTEXT_PARSE) {
+ ls->hdocs = hdocs;
+ ls->eclen = eclen;
+ ls->ecused = ecused;
+ ls->ecnpats = ecnpats;
+ ls->ecbuf = ecbuf;
+ ls->ecstrs = ecstrs;
+ ls->ecsoffs = ecsoffs;
+ ls->ecssub = ecssub;
+ ls->ecnfunc = ecnfunc;
+ ecbuf = NULL;
+ hdocs = NULL;
}
- ls->hline = chline;
- chline = NULL;
- ls->hptr = hptr;
- hptr = NULL;
- ls->hlinesz = hlinesz;
- ls->cstack = cmdstack;
- ls->csp = cmdsp;
- cmdstack = (unsigned char *)zalloc(CMDSTACKSZ);
- ls->tok = tok;
- ls->isnewlin = isnewlin;
- ls->tokstr = tokstr;
- ls->zshlextext = zshlextext;
- ls->bptr = bptr;
- tokstr = zshlextext = bptr = NULL;
- ls->bsiz = bsiz;
- bsiz = 256;
- ls->len = len;
- ls->chwords = chwords;
- ls->chwordlen = chwordlen;
- ls->chwordpos = chwordpos;
- ls->hwgetword = hwgetword;
- ls->lexstop = lexstop;
- ls->hdocs = hdocs;
- ls->hgetc = hgetc;
- ls->hungetc = hungetc;
- ls->hwaddc = hwaddc;
- ls->hwbegin = hwbegin;
- ls->hwend = hwend;
- ls->addtoline = addtoline;
- ls->eclen = eclen;
- ls->ecused = ecused;
- ls->ecnpats = ecnpats;
- ls->ecbuf = ecbuf;
- ls->ecstrs = ecstrs;
- ls->ecsoffs = ecsoffs;
- ls->ecssub = ecssub;
- ls->ecnfunc = ecnfunc;
- ls->toklineno = toklineno;
- cmdsp = 0;
- inredir = 0;
- hdocs = NULL;
- histactive = 0;
- ecbuf = NULL;
ls->next = lstack;
lstack = ls;
}
-/* restore lexical state */
+/* save context in full */
/**/
mod_export void
-lexrestore(void)
+lexsave(void)
+{
+ lexsave_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE);
+}
+
+/* restore context or part therefore */
+
+/**/
+mod_export void
+lexrestore_partial(int parts)
{
struct lexstack *ln = lstack;
@@ -332,65 +379,89 @@ lexrestore(void)
queue_signals();
lstack = lstack->next;
- if (!lstack) {
- /* Back to top level: don't need special ZLE value */
- DPUTS(ln->hline != zle_chline, "BUG: Ouch, wrong chline for ZLE");
- zle_chline = NULL;
+ if (parts & ZCONTEXT_LEX) {
+ incmdpos = ln->incmdpos;
+ incond = ln->incond;
+ incasepat = ln->incasepat;
+ dbparens = ln->dbparens;
+ isfirstln = ln->isfirstln;
+ isfirstch = ln->isfirstch;
+ lexflags = ln->lexflags;
+ tok = ln->tok;
+ isnewlin = ln->isnewlin;
+ tokstr = ln->tokstr;
+ zshlextext = ln->zshlextext;
+ bptr = ln->bptr;
+ bsiz = ln->bsiz;
+ len = ln->len;
+ lex_add_raw = ln->lex_add_raw;
+ tokstr_raw = ln->tokstr_raw;
+ bptr_raw = ln->bptr_raw;
+ bsiz_raw = ln->bsiz_raw;
+ len_raw = ln->len_raw;
+ lexstop = ln->lexstop;
+ toklineno = ln->toklineno;
+ }
+
+ if (parts & ZCONTEXT_HIST) {
+ if (!lstack) {
+ /* Back to top level: don't need special ZLE value */
+ DPUTS(ln->hline != zle_chline, "BUG: Ouch, wrong chline for ZLE");
+ zle_chline = NULL;
+ }
+ histactive = ln->histactive;
+ histdone = ln->histdone;
+ stophist = ln->stophist;
+ chline = ln->hline;
+ hptr = ln->hptr;
+ chwords = ln->chwords;
+ chwordlen = ln->chwordlen;
+ chwordpos = ln->chwordpos;
+ hwgetword = ln->hwgetword;
+ hgetc = ln->hgetc;
+ hungetc = ln->hungetc;
+ hwaddc = ln->hwaddc;
+ hwbegin = ln->hwbegin;
+ hwend = ln->hwend;
+ addtoline = ln->addtoline;
+ hlinesz = ln->hlinesz;
+ if (cmdstack)
+ zfree(cmdstack, CMDSTACKSZ);
+ cmdstack = ln->cstack;
+ cmdsp = ln->csp;
+ }
+
+ if (parts & ZCONTEXT_PARSE) {
+ if (ecbuf)
+ zfree(ecbuf, eclen);
+
+ hdocs = ln->hdocs;
+ eclen = ln->eclen;
+ ecused = ln->ecused;
+ ecnpats = ln->ecnpats;
+ ecbuf = ln->ecbuf;
+ ecstrs = ln->ecstrs;
+ ecsoffs = ln->ecsoffs;
+ ecssub = ln->ecssub;
+ ecnfunc = ln->ecnfunc;
+
+ errflag &= ~ERRFLAG_ERROR;
}
- incmdpos = ln->incmdpos;
- incond = ln->incond;
- incasepat = ln->incasepat;
- dbparens = ln->dbparens;
- isfirstln = ln->isfirstln;
- isfirstch = ln->isfirstch;
- histactive = ln->histactive;
- histdone = ln->histdone;
- lexflags = ln->lexflags;
- stophist = ln->stophist;
- chline = ln->hline;
- hptr = ln->hptr;
- if (cmdstack)
- zfree(cmdstack, CMDSTACKSZ);
- cmdstack = ln->cstack;
- cmdsp = ln->csp;
- tok = ln->tok;
- isnewlin = ln->isnewlin;
- tokstr = ln->tokstr;
- zshlextext = ln->zshlextext;
- bptr = ln->bptr;
- bsiz = ln->bsiz;
- len = ln->len;
- chwords = ln->chwords;
- chwordlen = ln->chwordlen;
- chwordpos = ln->chwordpos;
- hwgetword = ln->hwgetword;
- lexstop = ln->lexstop;
- hdocs = ln->hdocs;
- hgetc = ln->hgetc;
- hungetc = ln->hungetc;
- hwaddc = ln->hwaddc;
- hwbegin = ln->hwbegin;
- hwend = ln->hwend;
- addtoline = ln->addtoline;
- if (ecbuf)
- zfree(ecbuf, eclen);
- eclen = ln->eclen;
- ecused = ln->ecused;
- ecnpats = ln->ecnpats;
- ecbuf = ln->ecbuf;
- ecstrs = ln->ecstrs;
- ecsoffs = ln->ecsoffs;
- ecssub = ln->ecssub;
- ecnfunc = ln->ecnfunc;
- hlinesz = ln->hlinesz;
- toklineno = ln->toklineno;
- errflag &= ~ERRFLAG_ERROR;
free(ln);
unqueue_signals();
}
+/* complete restore context */
+
+/**/
+mod_export void
+lexrestore(void)
+{
+ lexrestore_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE);
+}
+
/**/
void
zshlex(void)
@@ -1905,80 +1976,151 @@ exalias(void)
return 0;
}
-/* skip (...) */
+/**/
+void
+zshlex_raw_add(int c)
+{
+ if (!lex_add_raw)
+ return;
+
+ *bptr_raw++ = c;
+ if (bsiz_raw == ++len_raw) {
+ int newbsiz = bsiz_raw * 2;
+
+ tokstr_raw = (char *)hrealloc(tokstr_raw, bsiz_raw, newbsiz);
+ bptr_raw = tokstr_raw + len_raw;
+ memset(bptr_raw, 0, newbsiz - bsiz_raw);
+ bsiz_raw = newbsiz;
+ }
+}
+
+/**/
+void
+zshlex_raw_back(void)
+{
+ if (!lex_add_raw)
+ return;
+ bptr_raw--;
+ len_raw--;
+}
+
+/*
+ * Skip (...) for command-style substitutions: $(...), <(...), >(...)
+ *
+ * In order to ensure we don't stop at closing parentheses with
+ * some other syntactic significance, we'll parse the input until
+ * we find an unmatched closing parenthesis. However, we'll throw
+ * away the result of the parsing and just keep the string we've built
+ * up on the way.
+ */
/**/
static int
skipcomm(void)
{
- int pct = 1, c, start = 1;
+ char *new_tokstr, *new_bptr = bptr_raw;
+ int new_len, new_bsiz, new_lexstop, new_lex_add_raw;
cmdpush(CS_CMDSUBST);
SETPARBEGIN
- c = Inpar;
- do {
- int iswhite;
- add(c);
- c = hgetc();
- if (itok(c) || lexstop)
- break;
- iswhite = inblank(c);
- switch (c) {
- case '(':
- pct++;
- break;
- case ')':
- pct--;
- break;
- case '\\':
- add(c);
- c = hgetc();
- break;
- case '\'': {
- int strquote = bptr[-1] == '$';
- add(c);
- STOPHIST
- while ((c = hgetc()) != '\'' && !lexstop) {
- if (c == '\\' && strquote) {
- add(c);
- c = hgetc();
- }
- add(c);
- }
- ALLOWHIST
- break;
- }
- case '\"':
- add(c);
- while ((c = hgetc()) != '\"' && !lexstop)
- if (c == '\\') {
- add(c);
- add(hgetc());
- } else
- add(c);
- break;
- case '`':
- add(c);
- while ((c = hgetc()) != '`' && !lexstop)
- if (c == '\\')
- add(c), add(hgetc());
- else
- add(c);
- break;
- case '#':
- if (start) {
- add(c);
- while ((c = hgetc()) != '\n' && !lexstop)
- add(c);
- iswhite = 1;
- }
- break;
+ add(Inpar);
+
+ new_lex_add_raw = lex_add_raw + 1;
+ if (!lex_add_raw) {
+ /*
+ * We'll combine the string so far with the input
+ * read in for the command substitution. To do this
+ * we'll just propagate the current tokstr etc. as the
+ * variables used for adding raw input, and
+ * ensure we swap those for the real tokstr etc. at the end.
+ *
+ * However, we need to save and restore the rest of the
+ * lexical and parse state as we're effectively parsing
+ * an internal string. Because we're still parsing it from
+ * the original input source (we have to --- we don't know
+ * when to stop inputting it otherwise and can't rely on
+ * the input being recoverable until we've read it) we need
+ * to keep the same history context.
+ */
+ new_tokstr = tokstr;
+ new_bptr = bptr;
+ new_len = len;
+ new_bsiz = bsiz;
+
+ lexsave_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
+ } else {
+ /*
+ * Set up for nested command subsitution, however
+ * we don't actually need the string until we get
+ * back to the top level and recover the lot.
+ * The $() body just appears empty.
+ *
+ * We do need to propagate the raw variables which would
+ * otherwise by cleared, though.
+ */
+ new_tokstr = tokstr_raw;
+ new_bptr = bptr_raw;
+ new_len = len_raw;
+ new_bsiz = bsiz_raw;
+
+ lexsave_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
+ }
+ tokstr_raw = new_tokstr;
+ bsiz_raw = new_bsiz;
+ len_raw = new_len;
+ bptr_raw = new_bptr;
+ lex_add_raw = new_lex_add_raw;
+
+ if (!parse_event(OUTPAR) || tok != OUTPAR)
+ lexstop = 1;
+ /* Outpar lexical token gets added in caller if present */
+
+ /*
+ * We're going to keep the full raw input string
+ * as the current token string after popping the stack.
+ */
+ new_tokstr = tokstr_raw;
+ new_bptr = bptr_raw;
+ new_len = len_raw;
+ new_bsiz = bsiz_raw;
+ /*
+ * We're also going to propagate the lexical state:
+ * if we couldn't parse the command substitution we
+ * can't continue.
+ */
+ new_lexstop = lexstop;
+
+ lexrestore_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
+
+ if (lex_add_raw) {
+ /*
+ * Keep going, so retain the raw variables.
+ */
+ tokstr_raw = new_tokstr;
+ bptr_raw = new_bptr;
+ len_raw = new_len;
+ bsiz_raw = new_bsiz;
+ } else {
+ if (!new_lexstop) {
+ /* Ignore the ')' added on input */
+ new_len--;
+ *--new_bptr = '\0';
}
- start = iswhite;
+
+ /*
+ * Convince the rest of lex.c we were examining a string
+ * all along.
+ */
+ tokstr = new_tokstr;
+ bptr = new_bptr;
+ len = new_len;
+ bsiz = new_bsiz;
+ lexstop = new_lexstop;
}
- while (pct);
+
if (!lexstop)
SETPAREND
cmdpop();
+
return lexstop;
}
diff --git a/Src/parse.c b/Src/parse.c
index c1709e03a..fa37ca3d9 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -361,7 +361,8 @@ ecstrcode(char *s)
/* Initialise wordcode buffer. */
-static void
+/**/
+void
init_parse(void)
{
if (ecbuf) zfree(ecbuf, eclen);
@@ -443,11 +444,15 @@ clear_hdocs()
* event : ENDINPUT
* | SEPER
* | sublist [ SEPER | AMPER | AMPERBANG ]
+ *
+ * cmdsubst indicates our event is part of a command-style
+ * substitution terminated by the token indicationg, usual closing
+ * parenthesis. In other cases endtok is ENDINPUT.
*/
/**/
Eprog
-parse_event(void)
+parse_event(int endtok)
{
tok = ENDINPUT;
incmdpos = 1;
@@ -455,36 +460,42 @@ parse_event(void)
zshlex();
init_parse();
- if (!par_event()) {
+ if (!par_event(endtok)) {
clear_hdocs();
return NULL;
}
+ if (endtok != ENDINPUT) {
+ /* don't need to build an eprog for this */
+ return &dummy_eprog;
+ }
return bld_eprog(1);
}
/**/
-static int
-par_event(void)
+int
+par_event(int endtok)
{
int r = 0, p, c = 0;
while (tok == SEPER) {
- if (isnewlin > 0)
+ if (isnewlin > 0 && endtok == ENDINPUT)
return 0;
zshlex();
}
if (tok == ENDINPUT)
return 0;
+ if (tok == endtok)
+ return 0;
p = ecadd(0);
if (par_sublist(&c)) {
- if (tok == ENDINPUT) {
+ if (tok == ENDINPUT || tok == endtok) {
set_list_code(p, Z_SYNC, c);
r = 1;
} else if (tok == SEPER) {
set_list_code(p, Z_SYNC, c);
- if (isnewlin <= 0)
+ if (isnewlin <= 0 || endtok != ENDINPUT)
zshlex();
r = 1;
} else if (tok == AMPER) {
@@ -513,7 +524,7 @@ par_event(void)
} else {
int oec = ecused;
- if (!par_event()) {
+ if (!par_event(endtok)) {
ecused = oec;
ecbuf[p] |= wc_bdata(Z_END);
}
diff --git a/Src/zsh.h b/Src/zsh.h
index b366e0ff4..475b7824f 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -421,6 +421,15 @@ enum {
#define META_HEAPDUP 6
#define META_HREALLOC 7
+/* Context to save and restore (bit fields) */
+enum {
+ /* History mechanism */
+ ZCONTEXT_HIST = (1<<0),
+ /* Lexical analyser */
+ ZCONTEXT_LEX = (1<<1),
+ /* Parser */
+ ZCONTEXT_PARSE = (1<<2)
+};
/**************************/
/* Abstract types for zsh */
diff --git a/Test/D08cmdsubst.ztst b/Test/D08cmdsubst.ztst
index 5661b0aaa..a4c69a010 100644
--- a/Test/D08cmdsubst.ztst
+++ b/Test/D08cmdsubst.ztst
@@ -106,3 +106,45 @@
>34
>"
>" OK
+
+ echo $(case foo in
+ foo)
+ echo This test worked.
+ ;;
+ bar)
+ echo This test failed in a rather bizarre way.
+ ;;
+ *)
+ echo This test failed.
+ ;;
+ esac)
+0:Parsing of command substitution with unmatched parentheses: case, basic
+>This test worked.
+
+ echo "$(case bar in
+ foo)
+ echo This test spoobed.
+ ;;
+ bar)
+ echo This test plurbled.
+ ;;
+ *)
+ echo This test bzonked.
+ ;;
+ esac)"
+0:Parsing of command substitution with unmatched parentheses: case with quotes
+>This test plurbled.
+
+ echo before $(
+ echo start; echo unpretentious |
+ while read line; do
+ case $line in
+ u*)
+ print Word began with u
+ print and ended with a crunch
+ ;;
+ esac
+ done | sed -e 's/Word/Universe/'; echo end
+ ) after
+0:Parsing of command substitution with ummatched parentheses: with frills
+>before start Universe began with u and ended with a crunch end after
--
cgit v1.2.3
From 3b32abafdb019cfb8f29908bc3d148e01518981d Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Sat, 10 Jan 2015 20:28:57 +0000
Subject: 34220: new $(...) handling needs to back up over alias expansion
---
ChangeLog | 3 +++
Src/input.c | 6 ++++++
2 files changed, 9 insertions(+)
(limited to 'Src/input.c')
diff --git a/ChangeLog b/ChangeLog
index e06c2f1b4..83b90c0cc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
2015-01-10 Peter Stephenson
+ * 34220: Src/input.c: new $(...) parsing didn't back up over
+ alias expansions.
+
* unposted: Src/context.c: update copyright.
* 34195: Thomas Mitterfellner: Completion/Linux/Command/_qdbus:
diff --git a/Src/input.c b/Src/input.c
index 04dda5acd..2ecac7bdc 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -537,6 +537,12 @@ inpush(char *str, int flags, Alias inalias)
static void
inpoptop(void)
{
+ if (!lexstop) {
+ inbufflags &= ~INP_ALCONT;
+ while (inbufptr > inbuf)
+ inungetc(inbufptr[-1]);
+ }
+
if (inbuf && (inbufflags & INP_FREE))
free(inbuf);
--
cgit v1.2.3
From 2c13d9fb0da0ec513e577c2589ec545df665326e Mon Sep 17 00:00:00 2001
From: "Barton E. Schaefer"
Date: Sat, 14 Feb 2015 10:43:10 -0800
Subject: 34543: Prevent crash on garbage bytes inside $(...)
Garbage input (nul bytes, etc.) can cause the $(...) parser to become
confused during look-ahead and attempt to back up the input too far.
This commit catches the error but does not fix the underlying cause.
---
ChangeLog | 5 +++++
Src/input.c | 6 ++++--
Src/lex.c | 6 ++++--
3 files changed, 13 insertions(+), 4 deletions(-)
(limited to 'Src/input.c')
diff --git a/ChangeLog b/ChangeLog
index b5fc3a7f7..5307eaa6d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2015-02-14 Barton E. Schaefer
+
+ * 34543: Src/input.c, Src/lex.c: Fix crash on garbage bytes
+ inside $(...)
+
2015-02-14 Mikael Magnusson
* unposted: Doc/Zsh/prompt.yo: Fix typo from 28487.
diff --git a/Src/input.c b/Src/input.c
index 2ecac7bdc..9520fdd6d 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -393,12 +393,14 @@ inungetc(int c)
if (((inbufflags & INP_LINENO) || !strin) && c == '\n')
lineno--;
}
-#ifdef DEBUG
else if (!(inbufflags & INP_CONT)) {
+#ifdef DEBUG
/* Just for debugging */
fprintf(stderr, "Attempt to inungetc() at start of input.\n");
- }
#endif
+ zerr("Garbled input at %c (binary file as commands?)", c);
+ return;
+ }
else {
/*
* The character is being backed up from a previous input stack
diff --git a/Src/lex.c b/Src/lex.c
index 433c27fbb..91628d4c2 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -503,13 +503,15 @@ cmd_or_math(int cs_type)
/* else unsuccessful: unget the whole thing */
hungetc(c);
lexstop = 0;
- while (lexbuf.len > oldlen) {
+ while (lexbuf.len > oldlen && !errflag) {
lexbuf.len--;
hungetc(itok(*--lexbuf.ptr) ?
ztokens[*lexbuf.ptr - Pound] : *lexbuf.ptr);
}
+ if (errflag)
+ return 2;
hungetc('(');
- return 0;
+ return errflag ? 2 : 0;
}
--
cgit v1.2.3
From 126fb61c7c48edb19b9d771e4e517cef710f8bf1 Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Mon, 16 Feb 2015 17:16:57 +0000
Subject: 34560: Fix $(( that's actually a multiline cmd subst.
---
ChangeLog | 2 ++
Src/input.c | 33 +++++++++++++++++++++++++++++++--
Src/lex.c | 4 ++++
Src/zsh.h | 1 +
Test/C01arith.ztst | 35 +++++++++++++++++++++++++++++++++++
5 files changed, 73 insertions(+), 2 deletions(-)
(limited to 'Src/input.c')
diff --git a/ChangeLog b/ChangeLog
index f37c218c7..509faffe6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,7 @@
2015-02-16 Peter Stephenson
+ * 34560: Src/input. Src/lex.c, Src/zsh.h, Test/C01arith.ztst:
+
* 34558: Doc/Zsh/func.yo: preexec doc erroneously claimed $1
was empty if line removed from history.
diff --git a/Src/input.c b/Src/input.c
index 9520fdd6d..f919e5757 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -330,8 +330,37 @@ inputline(void)
}
}
isfirstch = 1;
- /* Put this into the input channel. */
- inputsetline(ingetcline, INP_FREE);
+ if ((inbufflags & INP_APPEND) && inbuf) {
+ /*
+ * We need new input but need to be able to back up
+ * over the old input, so append this line.
+ * Pushing the line onto the stack doesn't have the right
+ * effect.
+ *
+ * This is quite a simple and inefficient fix, but currently
+ * we only need it when backing up over a multi-line $((...
+ * that turned out to be a command substitution rather than
+ * a math substitution, which is a very special case.
+ * So it's not worth rewriting.
+ */
+ char *oinbuf = inbuf;
+ int newlen = strlen(ingetcline);
+ int oldlen = (int)(inbufptr - inbuf) + inbufleft;
+ if (inbufflags & INP_FREE) {
+ inbuf = realloc(inbuf, oldlen + newlen + 1);
+ inbufptr += inbuf - oinbuf;
+ strcpy(inbuf + oldlen, ingetcline);
+ } else {
+ /* Paranoia: don't think this is used */
+ DPUTS(1, "Appending to unallocated input line.");
+ }
+ inbufleft += newlen;
+ inbufct += newlen;
+ inbufflags |= INP_FREE;
+ } else {
+ /* Put this into the input channel. */
+ inputsetline(ingetcline, INP_FREE);
+ }
return 0;
}
diff --git a/Src/lex.c b/Src/lex.c
index 91628d4c2..006848543 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -483,9 +483,13 @@ cmd_or_math(int cs_type)
{
int oldlen = lexbuf.len;
int c;
+ int oinflags = inbufflags;
cmdpush(cs_type);
+ inbufflags |= INP_APPEND;
c = dquote_parse(')', 0);
+ if (!(oinflags & INP_APPEND))
+ inbufflags &= ~INP_APPEND;
cmdpop();
*lexbuf.ptr = '\0';
if (!c) {
diff --git a/Src/zsh.h b/Src/zsh.h
index 94e9ffc9f..dd946d214 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -410,6 +410,7 @@ enum {
#define INP_CONT (1<<3) /* continue onto previously stacked input */
#define INP_ALCONT (1<<4) /* stack is continued from alias expn. */
#define INP_LINENO (1<<5) /* update line number */
+#define INP_APPEND (1<<6) /* Append new lines to allow backup */
/* Flags for metafy */
#define META_REALLOC 0
diff --git a/Test/C01arith.ztst b/Test/C01arith.ztst
index ea87af257..09c08224e 100644
--- a/Test/C01arith.ztst
+++ b/Test/C01arith.ztst
@@ -318,3 +318,38 @@
# 0.75 is exactly representable, don't expect rounding error.
>0
>0.75
+
+ # The following tests for a bug that only happens when
+ # backing up over input read a line at a time, so we'll
+ # read the input from stdin.
+ $ZTST_testdir/../Src/zsh -f <<<'
+ print $((echo first command
+ ); echo second command)
+ print third command
+ '
+0:Backing up a line of input when finding out it's not arithmetic
+>first command second command
+>third command
+
+ $ZTST_testdir/../Src/zsh -f <<<'
+ print $((3 +
+ 4))
+ print next line
+ '
+0:Not needing to back up a line when reading multiline arithmetic
+>7
+>next line
+
+ $ZTST_testdir/../Src/zsh -f <<<'
+ print $((case foo in
+ bar)
+ echo not this no, no
+ ;;
+ foo)
+ echo yes, this one
+ ;;
+ esac)
+ print after case in subshell)
+ '
+0:Non-arithmetic subst with command subsitution parse from hell
+>yes, this one after case in subshell
--
cgit v1.2.3
From 9b21dcada9d678da6d23c7e03c68eca926e3ff3e Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Tue, 17 Feb 2015 09:56:09 +0000
Subject: Fix up memory allocation for previous patch
---
ChangeLog | 8 +++++++-
Src/input.c | 9 +++++----
2 files changed, 12 insertions(+), 5 deletions(-)
(limited to 'Src/input.c')
diff --git a/ChangeLog b/ChangeLog
index 509faffe6..df483c30d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,12 @@
+2015-02-17 Peter Stephenson
+
+ * 34563: Src/input.c: Fix up memory allocation in 34560.
+
2015-02-16 Peter Stephenson
- * 34560: Src/input. Src/lex.c, Src/zsh.h, Test/C01arith.ztst:
+ * 34560: Src/input. Src/lex.c, Src/zsh.h, Test/C01arith.ztst:
+ case of $(( that turned into a $(...) and a (...) with
+ multiple lines read before it found out.
* 34558: Doc/Zsh/func.yo: preexec doc erroneously claimed $1
was empty if line removed from history.
diff --git a/Src/input.c b/Src/input.c
index f919e5757..92b1ad1f7 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -348,12 +348,13 @@ inputline(void)
int oldlen = (int)(inbufptr - inbuf) + inbufleft;
if (inbufflags & INP_FREE) {
inbuf = realloc(inbuf, oldlen + newlen + 1);
- inbufptr += inbuf - oinbuf;
- strcpy(inbuf + oldlen, ingetcline);
} else {
- /* Paranoia: don't think this is used */
- DPUTS(1, "Appending to unallocated input line.");
+ inbuf = zalloc(oldlen + newlen + 1);
+ memcpy(inbuf, oinbuf, oldlen);
}
+ inbufptr += inbuf - oinbuf;
+ strcpy(inbuf + oldlen, ingetcline);
+ free(ingetcline);
inbufleft += newlen;
inbufct += newlen;
inbufflags |= INP_FREE;
--
cgit v1.2.3
From 89aca2d0a0f1b9a28149b6c1640a7e73feef5b39 Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Fri, 20 Mar 2015 11:02:23 +0000
Subject: 34752: another fix for history expansion in cmd subst
---
ChangeLog | 6 ++++++
Src/input.c | 16 ++++++++++++++--
2 files changed, 20 insertions(+), 2 deletions(-)
(limited to 'Src/input.c')
diff --git a/ChangeLog b/ChangeLog
index 91709629a..1711a5087 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2015-03-20 Peter Stephenson
+
+ * 34752: Src/input.c: history expansion in command substitution
+ *still* didn't work although the command put back into the
+ history was correct.
+
2015-03-19 Peter Stephenson
* 34742: Src/hist.c: history expansion in command substitution
diff --git a/Src/input.c b/Src/input.c
index 92b1ad1f7..30970a060 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -571,8 +571,20 @@ inpoptop(void)
{
if (!lexstop) {
inbufflags &= ~INP_ALCONT;
- while (inbufptr > inbuf)
- inungetc(inbufptr[-1]);
+ while (inbufptr > inbuf) {
+ inbufptr--;
+ inbufct++;
+ inbufleft++;
+ /*
+ * As elsewhere in input and history mechanisms:
+ * unwinding aliases and unwinding history have different
+ * implications as aliases are after the lexer while
+ * history is before, but they're both pushed onto
+ * the input stack.
+ */
+ if ((inbufflags & (INP_ALIAS|INP_HIST)) == INP_ALIAS)
+ zshlex_raw_back();
+ }
}
if (inbuf && (inbufflags & INP_FREE))
--
cgit v1.2.3
From 3774bae034560252756ec76b91ce03198dd8daf0 Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Thu, 26 Mar 2015 20:28:13 +0000
Subject: 34784: fix old bug with history word selection
---
ChangeLog | 5 +++++
Src/input.c | 27 ++++++++++++++++++---------
Src/zsh.h | 5 +++--
3 files changed, 26 insertions(+), 11 deletions(-)
(limited to 'Src/input.c')
diff --git a/ChangeLog b/ChangeLog
index 3cb847f69..e4f409959 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2015-03-26 Peter Stephenson
+
+ * 34784: Src/input.c, Src/zsh.h: fix old bug with history
+ selection.
+
2015-03-25 Barton E. Schaefer
* 34779: Test/A02alias.ztst: tests for 34776
diff --git a/Src/input.c b/Src/input.c
index 30970a060..4a5bf89c6 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -56,8 +56,9 @@
* inpop(), which effectively flushes any unread input as well as restoring
* the previous input state.
*
- * The internal flag INP_ALCONT shows that the stack element was pushed
- * by an alias expansion; it should not be needed elsewhere.
+ * The internal flags INP_ALCONT and INP_HISTCONT show that the stack
+ * element was pushed by an alias or history expansion; they should not
+ * be needed elsewhere.
*
* The global variable inalmore is set to indicate aliases should
* continue to be expanded because the last alias expansion ended
@@ -446,7 +447,8 @@ inungetc(int c)
* we may need to restore an alias popped from the stack.
* Note this may be a dummy (history expansion) entry.
*/
- if (inbufptr == inbufpush && inbufflags & INP_ALCONT) {
+ if (inbufptr == inbufpush &&
+ (inbufflags & (INP_ALCONT|INP_HISTCONT))) {
/*
* Go back up the stack over all entries which were alias
* expansions and were pushed with nothing remaining to read.
@@ -455,8 +457,12 @@ inungetc(int c)
if (instacktop->alias)
instacktop->alias->inuse = 1;
instacktop++;
- } while ((instacktop->flags & INP_ALCONT) && !instacktop->bufleft);
- inbufflags = INP_CONT|INP_ALIAS;
+ } while ((instacktop->flags & (INP_ALCONT|INP_HISTCONT))
+ && !instacktop->bufleft);
+ if (inbufflags & INP_HISTCONT)
+ inbufflags = INP_CONT|INP_ALIAS|INP_HIST;
+ else
+ inbufflags = INP_CONT|INP_ALIAS;
inbufleft = 0;
inbuf = inbufptr = "";
}
@@ -522,7 +528,7 @@ inpush(char *str, int flags, Alias inalias)
instacktop->bufptr = inbufptr;
instacktop->bufleft = inbufleft;
instacktop->bufct = inbufct;
- inbufflags &= ~INP_ALCONT;
+ inbufflags &= ~(INP_ALCONT|INP_HISTCONT);
if (flags & (INP_ALIAS|INP_HIST)) {
/*
* Text is expansion for history or alias, so continue
@@ -531,7 +537,10 @@ inpush(char *str, int flags, Alias inalias)
* and mark alias as in use.
*/
flags |= INP_CONT|INP_ALIAS;
- instacktop->flags = inbufflags | INP_ALCONT;
+ if (flags & INP_HIST)
+ instacktop->flags = inbufflags | INP_HISTCONT;
+ else
+ instacktop->flags = inbufflags | INP_ALCONT;
if ((instacktop->alias = inalias))
inalias->inuse = 1;
} else {
@@ -570,7 +579,7 @@ static void
inpoptop(void)
{
if (!lexstop) {
- inbufflags &= ~INP_ALCONT;
+ inbufflags &= ~(INP_ALCONT|INP_HISTCONT);
while (inbufptr > inbuf) {
inbufptr--;
inbufct++;
@@ -598,7 +607,7 @@ inpoptop(void)
inbufct = instacktop->bufct;
inbufflags = instacktop->flags;
- if (!(inbufflags & INP_ALCONT))
+ if (!(inbufflags & (INP_ALCONT|INP_HISTCONT)))
return;
if (instacktop->alias) {
diff --git a/Src/zsh.h b/Src/zsh.h
index 403e8d3b9..486ad800a 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -411,8 +411,9 @@ enum {
#define INP_HIST (1<<2) /* expanding history */
#define INP_CONT (1<<3) /* continue onto previously stacked input */
#define INP_ALCONT (1<<4) /* stack is continued from alias expn. */
-#define INP_LINENO (1<<5) /* update line number */
-#define INP_APPEND (1<<6) /* Append new lines to allow backup */
+#define INP_HISTCONT (1<<5) /* stack is continued from history expn. */
+#define INP_LINENO (1<<6) /* update line number */
+#define INP_APPEND (1<<7) /* Append new lines to allow backup */
/* Flags for metafy */
#define META_REALLOC 0
--
cgit v1.2.3