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