about summary refs log tree commit diff
path: root/Src/lex.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/lex.c')
-rw-r--r--Src/lex.c1489
1 files changed, 1489 insertions, 0 deletions
diff --git a/Src/lex.c b/Src/lex.c
new file mode 100644
index 000000000..6f4f2dd20
--- /dev/null
+++ b/Src/lex.c
@@ -0,0 +1,1489 @@
+/*
+ * lex.c - lexical analysis
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Paul Falstad or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zsh.mdh"
+#include "lex.pro"
+
+/* tokens */
+
+/**/
+char ztokens[] = "#$^*()$=|{}[]`<>?~`,'\"\\";
+
+/* parts of the current token */
+
+/**/
+char *yytext, *tokstr;
+/**/
+int tok, tokfd;
+
+/* lexical analyzer error flag */
+ 
+/**/
+int lexstop;
+
+/* if != 0, this is the first line of the command */
+ 
+/**/
+int isfirstln;
+ 
+/* if != 0, this is the first char of the command (not including white space) */
+ 
+/**/
+int isfirstch;
+
+/* flag that an alias should be expanded after expansion ending in space */
+
+/**/
+int inalmore;
+
+/* don't do spelling correction */
+ 
+/**/
+int nocorrect;
+
+/* the line buffer */
+
+/**/
+unsigned char *line;
+
+/* cursor position and line length */
+
+/**/
+int cs, ll;
+
+/* inwhat says what exactly we are in     *
+ * (its value is one of the IN_* things). */
+
+/**/
+int inwhat;
+
+/* 1 if x added to complete in a blank between words */
+
+/**/
+int addedx;
+
+/* 1 if aliases should not be expanded */
+ 
+/**/
+int noaliases;
+
+/* we are parsing a line sent to use by the editor */
+ 
+/**/
+int zleparse;
+ 
+/**/
+int wordbeg;
+ 
+/**/
+int parbegin;
+
+/**/
+int parend;
+ 
+/* text of puctuation tokens */
+
+static char *tokstrings[WHILE + 1] = {
+    NULL,	/* NULLTOK	  0  */
+    ";",	/* SEPER	     */
+    "\\n",	/* NEWLIN	     */
+    ";",	/* SEMI		     */
+    ";;",	/* DSEMI	     */
+    "&",	/* AMPER	  5  */
+    "(",	/* INPAR	     */
+    ")",	/* OUTPAR	     */
+    "||",	/* DBAR		     */
+    "&&",	/* DAMPER	     */
+    ")",	/* OUTANG	  10 */
+    ">|",	/* OUTANGBANG	     */
+    ">>",	/* DOUTANG	     */
+    ">>|",	/* DOUTANGBANG	     */
+    "<",	/* INANG	     */
+    "<>",	/* INOUTANG	  15 */
+    "<<",	/* DINANG	     */
+    "<<-",	/* DINANGDASH	     */
+    "<&",	/* INANGAMP	     */
+    ">&",	/* OUTANGAMP	     */
+    "&>",	/* AMPOUTANG	  20 */
+    "&>|",	/* OUTANGAMPBANG     */
+    ">>&",	/* DOUTANGAMP	     */
+    ">>&|",	/* DOUTANGAMPBANG    */
+    "<<<",	/* TRINANG	     */
+    "|",	/* BAR		  25 */
+    "|&",	/* BARAMP	     */
+    "()",	/* INOUTPAR	     */
+    "((",	/* DINPAR	     */
+    "))",	/* DOUTPAR	     */
+    "&|",	/* AMPERBANG	  30 */
+    ";&",	/* SEMIAMP	     */
+};
+
+/* lexical state */
+
+static int dbparens;
+static int len = 0, bsiz = 256;
+static char *bptr;
+
+struct lexstack {
+    struct lexstack *next;
+
+    int incmdpos;
+    int incond;
+    int incasepat;
+    int dbparens;
+    int isfirstln;
+    int isfirstch;
+    int histactive;
+    int histdone;
+    int spaceflag;
+    int stophist;
+    int hlinesz;
+    char *hline;
+    char *hptr;
+    int tok;
+    int isnewlin;
+    char *tokstr;
+    char *yytext;
+    char *bptr;
+    int bsiz;
+    short *chwords;
+    int chwordlen;
+    int chwordpos;
+    int hwgetword;
+    int lexstop;
+    struct heredocs *hdocs;
+
+    unsigned char *cstack;
+    int csp;
+};
+
+static struct lexstack *lstack = NULL;
+
+/* save the lexical state */
+
+/* is this a hack or what? */
+
+/**/
+void
+lexsave(void)
+{
+    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->spaceflag = spaceflag;
+    ls->stophist = stophist;
+    ls->hline = chline;
+    ls->hptr = hptr;
+    ls->hlinesz = hlinesz;
+    ls->cstack = cmdstack;
+    ls->csp = cmdsp;
+    cmdstack = (unsigned char *)zalloc(256);
+    ls->tok = tok;
+    ls->isnewlin = isnewlin;
+    ls->tokstr = tokstr;
+    ls->yytext = yytext;
+    ls->bptr = bptr;
+    ls->bsiz = bsiz;
+    ls->chwords = chwords;
+    ls->chwordlen = chwordlen;
+    ls->chwordpos = chwordpos;
+    ls->hwgetword = hwgetword;
+    ls->lexstop = lexstop;
+    ls->hdocs = hdocs;
+    cmdsp = 0;
+    inredir = 0;
+    hdocs = NULL;
+
+    ls->next = lstack;
+    lstack = ls;
+}
+
+/* restore lexical state */
+
+/**/
+void
+lexrestore(void)
+{
+    struct lexstack *ln;
+
+    DPUTS(!lstack, "BUG: lexrestore() without lexsave()");
+    incmdpos = lstack->incmdpos;
+    incond = lstack->incond;
+    incasepat = lstack->incasepat;
+    dbparens = lstack->dbparens;
+    isfirstln = lstack->isfirstln;
+    isfirstch = lstack->isfirstch;
+    histactive = lstack->histactive;
+    histdone = lstack->histdone;
+    spaceflag = lstack->spaceflag;
+    stophist = lstack->stophist;
+    chline = lstack->hline;
+    hptr = lstack->hptr;
+    if (cmdstack)
+	free(cmdstack);
+    cmdstack = lstack->cstack;
+    cmdsp = lstack->csp;
+    tok = lstack->tok;
+    isnewlin = lstack->isnewlin;
+    tokstr = lstack->tokstr;
+    yytext = lstack->yytext;
+    bptr = lstack->bptr;
+    bsiz = lstack->bsiz;
+    chwords = lstack->chwords;
+    chwordlen = lstack->chwordlen;
+    chwordpos = lstack->chwordpos;
+    hwgetword = lstack->hwgetword;
+    lexstop = lstack->lexstop;
+    hdocs = lstack->hdocs;
+    hlinesz = lstack->hlinesz;
+    errflag = 0;
+
+    ln = lstack->next;
+    free(lstack);
+    lstack = ln;
+}
+
+/**/
+void
+yylex(void)
+{
+    if (tok == LEXERR)
+	return;
+    do
+	tok = gettok();
+    while (tok != ENDINPUT && exalias());
+    if (tok == NEWLIN || tok == ENDINPUT) {
+	while (hdocs) {
+	    struct heredocs *next = hdocs->next;
+
+	    hwbegin(0);
+	    cmdpush(hdocs->rd->type == HEREDOC ? CS_HEREDOC : CS_HEREDOCD);
+	    STOPHIST
+	    hdocs->rd->name = gethere(hdocs->rd->name, hdocs->rd->type);
+	    ALLOWHIST
+	    cmdpop();
+	    hwend();
+	    hdocs->rd->type = HERESTR;
+	    zfree(hdocs, sizeof(struct heredocs));
+	    hdocs = next;
+	}
+    }
+    if (tok != NEWLIN)
+	isnewlin = 0;
+    else
+	isnewlin = (inbufct) ? -1 : 1;
+    if (tok == SEMI || tok == NEWLIN)
+	tok = SEPER;
+}
+
+/**/
+void
+ctxtlex(void)
+{
+    static int oldpos;
+
+    yylex();
+    switch (tok) {
+    case SEPER:
+    case NEWLIN:
+    case SEMI:
+    case DSEMI:
+    case SEMIAMP:
+    case AMPER:
+    case AMPERBANG:
+    case INPAR:
+    case INBRACE:
+    case DBAR:
+    case DAMPER:
+    case BAR:
+    case BARAMP:
+    case INOUTPAR:
+    case DO:
+    case THEN:
+    case ELIF:
+    case ELSE:
+    case DOUTBRACK:
+	incmdpos = 1;
+	break;
+    case STRING:
+ /* case ENVSTRING: */
+    case ENVARRAY:
+    case OUTPAR:
+    case CASE:
+    case DINBRACK:
+	incmdpos = 0;
+	break;
+    }
+    if (tok != DINPAR)
+	infor = tok == FOR ? 2 : 0;
+    if (IS_REDIROP(tok) || tok == FOR || tok == FOREACH || tok == SELECT) {
+	inredir = 1;
+	oldpos = incmdpos;
+	incmdpos = 0;
+    } else if (inredir) {
+	incmdpos = oldpos;
+	inredir = 0;
+    }
+}
+
+#define LX1_BKSLASH 0
+#define LX1_COMMENT 1
+#define LX1_NEWLIN 2
+#define LX1_SEMI 3
+#define LX1_AMPER 5
+#define LX1_BAR 6
+#define LX1_INPAR 7
+#define LX1_OUTPAR 8
+#define LX1_INANG 13
+#define LX1_OUTANG 14
+#define LX1_OTHER 15
+
+#define LX2_BREAK 0
+#define LX2_OUTPAR 1
+#define LX2_BAR 2
+#define LX2_STRING 3
+#define LX2_INBRACK 4
+#define LX2_OUTBRACK 5
+#define LX2_TILDE 6
+#define LX2_INPAR 7
+#define LX2_INBRACE 8
+#define LX2_OUTBRACE 9
+#define LX2_OUTANG 10
+#define LX2_INANG 11
+#define LX2_EQUALS 12
+#define LX2_BKSLASH 13
+#define LX2_QUOTE 14
+#define LX2_DQUOTE 15
+#define LX2_BQUOTE 16
+#define LX2_COMMA 17
+#define LX2_OTHER 18
+#define LX2_META 19
+
+static unsigned char lexact1[256], lexact2[256], lextok2[256];
+
+/**/
+void
+initlextabs(void)
+{
+    int t0;
+    static char *lx1 = "\\q\n;!&|(){}[]<>";
+    static char *lx2 = ";)|$[]~({}><=\\\'\"`,";
+
+    for (t0 = 0; t0 != 256; t0++) {
+	lexact1[t0] = LX1_OTHER;
+	lexact2[t0] = LX2_OTHER;
+	lextok2[t0] = t0;
+    }
+    for (t0 = 0; lx1[t0]; t0++)
+	lexact1[(int)lx1[t0]] = t0;
+    for (t0 = 0; lx2[t0]; t0++)
+	lexact2[(int)lx2[t0]] = t0;
+    lexact2['&'] = LX2_BREAK;
+    lexact2[STOUC(Meta)] = LX2_META;
+    lextok2['*'] = Star;
+    lextok2['?'] = Quest;
+    lextok2['{'] = Inbrace;
+    lextok2['['] = Inbrack;
+    lextok2['$'] = String;
+    lextok2['~'] = Tilde;
+    lextok2['#'] = Pound;
+    lextok2['^'] = Hat;
+}
+
+/* initialize lexical state */
+
+/**/
+void
+lexinit(void)
+{
+    incond = incasepat = nocorrect =
+    infor = dbparens = lexstop = 0;
+    incmdpos = 1;
+    tok = ENDINPUT;
+}
+
+/* add a char to the string buffer */
+
+/**/
+void
+add(int c)
+{
+    *bptr++ = c;
+    if (bsiz == ++len) {
+	int newbsiz;
+
+	newbsiz = bsiz * 8;
+	while (newbsiz < inbufct)
+	    newbsiz *= 2;
+	bptr = len + (tokstr = (char *)hrealloc(tokstr, bsiz, newbsiz));
+	bsiz = newbsiz;
+    }
+}
+
+#define SETPARBEGIN {if (zleparse && !(inbufflags & INP_ALIAS) && cs >= ll+1-inbufct) parbegin = inbufct;}
+#define SETPAREND {\
+	    if (zleparse && !(inbufflags & INP_ALIAS) && parbegin != -1 && parend == -1)\
+		if (cs >= ll + 1 - inbufct)\
+		    parbegin = -1;\
+		else\
+		    parend = inbufct;}
+
+static int
+cmd_or_math(int cs_type)
+{
+    int oldlen = len;
+    int c;
+
+    cmdpush(cs_type);
+    c = dquote_parse(')', 0);
+    cmdpop();
+    *bptr = '\0';
+    if (!c) {
+	c = hgetc();
+	if (c == ')')
+	    return 1;
+	hungetc(c);
+	lexstop = 0;
+	c = ')';
+    }
+    hungetc(c);
+    lexstop = 0;
+    while (len > oldlen) {
+	len--;
+	hungetc(itok(*--bptr) ? ztokens[*bptr - Pound] : *bptr);
+    }
+    hungetc('(');
+    return 0;
+}
+
+static int
+cmd_or_math_sub(void)
+{
+    int c = hgetc();
+
+    if (c == '(') {
+	add(Inpar);
+	add('(');
+	if (cmd_or_math(CS_MATHSUBST)) {
+	    add(')');
+	    return 0;
+	}
+	bptr -= 2;
+	len -= 2;
+    } else {
+	hungetc(c);
+	lexstop = 0;
+    }
+    return skipcomm();
+}
+
+/**/
+int
+gettok(void)
+{
+    int c, d;
+    int peekfd = -1, peek;
+
+    MUSTUSEHEAP("gettok");
+  beginning:
+    tokstr = NULL;
+    while (iblank(c = hgetc()) && !lexstop);
+    if (lexstop)
+	return (errflag) ? LEXERR : ENDINPUT;
+    isfirstln = 0;
+    wordbeg = inbufct - (qbang && c == bangchar);
+    hwbegin(-1-(qbang && c == bangchar));
+    /* word includes the last character read and possibly \ before ! */
+    if (dbparens) {
+	len = 0;
+	bptr = tokstr = (char *)ncalloc(bsiz = 256);
+	hungetc(c);
+	cmdpush(CS_MATH);
+	c = dquote_parse(infor ? ';' : ')', 0);
+	cmdpop();
+	*bptr = '\0';
+	if (!c && infor) {
+	    infor--;
+	    return DINPAR;
+	}
+	if (c || (c = hgetc()) != ')') {
+	    hungetc(c);
+	    return LEXERR;
+	}
+	dbparens = 0;
+	return DOUTPAR;
+    } else if (idigit(c)) {	/* handle 1< foo */
+	d = hgetc();
+	if (d == '>' || d == '<') {
+	    peekfd = c - '0';
+	    c = d;
+	} else {
+	    hungetc(d);
+	    lexstop = 0;
+	}
+    }
+
+    /* chars in initial position in word */
+
+    if (c == hashchar &&
+	(isset(INTERACTIVECOMMENTS) ||
+	 (!zleparse && !expanding &&
+	  (!interact || unset(SHINSTDIN) || strin)))) {
+	/* History is handled here to prevent extra  *
+	 * newlines being inserted into the history. */
+
+	while ((c = ingetc()) != '\n' && !lexstop) {
+	    hwaddc(c);
+	    addtoline(c);
+	}
+
+	if (errflag)
+	    peek = LEXERR;
+	else {
+	    hwend();
+	    hwbegin(0);
+	    hwaddc('\n');
+	    addtoline('\n');
+	    peek = NEWLIN;
+	}
+	return peek;
+    }
+    switch (lexact1[STOUC(c)]) {
+    case LX1_BKSLASH:
+	d = hgetc();
+	if (d == '\n')
+	    goto beginning;
+	hungetc(d);
+	lexstop = 0;
+	break;
+    case LX1_NEWLIN:
+	return NEWLIN;
+    case LX1_SEMI:
+	d = hgetc();
+	if(d == ';')
+	    return DSEMI;
+	else if(d == '&')
+	    return SEMIAMP;
+	hungetc(d);
+	lexstop = 0;
+	return SEMI;
+    case LX1_AMPER:
+	d = hgetc();
+	if (d == '&')
+	    return DAMPER;
+	else if (d == '!' || d == '|')
+	    return AMPERBANG;
+	else if (d == '>') {
+	    d = hgetc();
+	    if (d == '!' || d == '|')
+		return OUTANGAMPBANG;
+	    else if (d == '>') {
+		d = hgetc();
+		if (d == '!' || d == '|')
+		    return DOUTANGAMPBANG;
+		hungetc(d);
+		lexstop = 0;
+		return DOUTANGAMP;
+	    }
+	    hungetc(d);
+	    lexstop = 0;
+	    return AMPOUTANG;
+	}
+	hungetc(d);
+	lexstop = 0;
+	return AMPER;
+    case LX1_BAR:
+	d = hgetc();
+	if (d == '|')
+	    return DBAR;
+	else if (d == '&')
+	    return BARAMP;
+	hungetc(d);
+	lexstop = 0;
+	return BAR;
+    case LX1_INPAR:
+	d = hgetc();
+	if (d == '(') {
+	    if (infor) {
+		dbparens = 1;
+		return DINPAR;
+	    }
+	    if (incmdpos) {
+		len = 0;
+		bptr = tokstr = (char *)ncalloc(bsiz = 256);
+		return cmd_or_math(CS_MATH) ? DINPAR : INPAR;
+	    }
+	} else if (d == ')')
+	    return INOUTPAR;
+	hungetc(d);
+	lexstop = 0;
+	if (!(incond == 1 || incmdpos))
+	    break;
+	return INPAR;
+    case LX1_OUTPAR:
+	return OUTPAR;
+    case LX1_INANG:
+	d = hgetc();
+	if (!incmdpos && d == '(') {
+	    hungetc(d);
+	    lexstop = 0;
+	    break;
+	}
+	if (d == '>')
+	    peek = INOUTANG;
+	else if (idigit(d) || d == '-') {
+	    int tbs = 256, n = 0, nc;
+	    char *tbuf, *tbp, *ntb;
+
+	    tbuf = tbp = (char *)zalloc(tbs);
+	    hungetc(d);
+
+	    while ((nc = hgetc()) && !lexstop) {
+		if (!idigit(nc) && nc != '-')
+		    break;
+		*tbp++ = (char)nc;
+		if (++n == tbs) {
+		    ntb = (char *)realloc(tbuf, tbs *= 2);
+		    tbp += ntb - tbuf;
+		    tbuf = ntb;
+		}
+	    }
+	    if (nc == '>' && !lexstop) {
+		hungetc(nc);
+		while (n--)
+		    hungetc(*--tbp);
+		zfree(tbuf, tbs);
+		break;
+	    }
+	    if (nc && !lexstop)
+		hungetc(nc);
+	    lexstop = 0;
+	    while (n--)
+		hungetc(*--tbp);
+	    zfree(tbuf, tbs);
+	    peek = INANG;
+	} else if (d == '<') {
+	    int e = hgetc();
+
+	    if (e == '(') {
+		hungetc(e);
+		hungetc(d);
+		peek = INANG;
+	    } else if (e == '<')
+		peek = TRINANG;
+	    else if (e == '-')
+		peek = DINANGDASH;
+	    else {
+		hungetc(e);
+		lexstop = 0;
+		peek = DINANG;
+	    }
+	} else if (d == '&')
+	    peek = INANGAMP;
+	else {
+	    peek = INANG;
+	    hungetc(d);
+	    lexstop = 0;
+	}
+	tokfd = peekfd;
+	return peek;
+    case LX1_OUTANG:
+	d = hgetc();
+	if (d == '(') {
+	    hungetc(d);
+	    break;
+	} else if (d == '&') {
+	    d = hgetc();
+	    if (d == '!' || d == '|')
+		peek = OUTANGAMPBANG;
+	    else {
+		hungetc(d);
+		lexstop = 0;
+		peek = OUTANGAMP;
+	    }
+	} else if (d == '!' || d == '|')
+	    peek = OUTANGBANG;
+	else if (d == '>') {
+	    d = hgetc();
+	    if (d == '&') {
+		d = hgetc();
+		if (d == '!' || d == '|')
+		    peek = DOUTANGAMPBANG;
+		else {
+		    hungetc(d);
+		    lexstop = 0;
+		    peek = DOUTANGAMP;
+		}
+	    } else if (d == '!' || d == '|')
+		peek = DOUTANGBANG;
+	    else if (d == '(') {
+		hungetc(d);
+		hungetc('>');
+		peek = OUTANG;
+	    } else {
+		hungetc(d);
+		lexstop = 0;
+		peek = DOUTANG;
+		if (isset(HISTALLOWCLOBBER))
+		    hwaddc('|');
+	    }
+	} else {
+	    hungetc(d);
+	    lexstop = 0;
+	    peek = OUTANG;
+	    if (!incond && isset(HISTALLOWCLOBBER))
+		hwaddc('|');
+	}
+	tokfd = peekfd;
+	return peek;
+    }
+
+    /* we've started a string, now get the *
+     * rest of it, performing tokenization */
+    return gettokstr(c, 0);
+}
+
+/**/
+static int
+gettokstr(int c, int sub)
+{
+    int bct = 0, pct = 0, brct = 0;
+    int intpos = 1, in_brace_param = 0;
+    int peek, inquote;
+#ifdef DEBUG
+    int ocmdsp = cmdsp;
+#endif
+
+    peek = STRING;
+    if (!sub) {
+	len = 0;
+	bptr = tokstr = (char *)ncalloc(bsiz = 256);
+    }
+    for (;;) {
+	int act;
+	int e;
+
+	if (inblank(c) && !in_brace_param && !pct)
+	    act = LX2_BREAK;
+	else {
+	    act = lexact2[STOUC(c)];
+	    c = lextok2[STOUC(c)];
+	}
+	switch (act) {
+	case LX2_BREAK:
+	    if (!in_brace_param && !sub)
+		goto brk;
+	    break;
+	case LX2_META:
+	    c = hgetc();
+#ifdef DEBUG
+	    if (lexstop) {
+		fputs("BUG: input terminated by Meta\n", stderr);
+		fflush(stderr);
+		goto brk;
+	    }
+#endif
+	    add(Meta);
+	    break;
+	case LX2_OUTPAR:
+	    if ((sub || in_brace_param) && isset(SHGLOB))
+		break;
+	    if (!in_brace_param && !pct--)
+		if (sub) {
+		    pct = 0;
+		    break;
+		} else
+		    goto brk;
+	    c = Outpar;
+	    break;
+	case LX2_BAR:
+	    if (!pct && !in_brace_param)
+		if (sub)
+		    break;
+		else
+		    goto brk;
+	    if (unset(SHGLOB) || (!sub && !in_brace_param))
+		c = Bar;
+	    break;
+	case LX2_STRING:
+	    e = hgetc();
+	    if (e == '[') {
+		cmdpush(CS_MATHSUBST);
+		add(String);
+		add(Inbrack);
+		c = dquote_parse(']', sub);
+		cmdpop();
+		if (c) {
+		    peek = LEXERR;
+		    goto brk;
+		}
+		c = Outbrack;
+	    } else if (e == '(') {
+		add(String);
+		c = cmd_or_math_sub();
+		if (c) {
+		    peek = LEXERR;
+		    goto brk;
+		}
+		c = Outpar;
+	    } else {
+		if (e == '{') {
+		    add(c);
+		    c = Inbrace;
+		    ++bct;
+		    cmdpush(CS_BRACEPAR);
+		    if (!in_brace_param)
+			in_brace_param = bct;
+		} else {
+		    hungetc(e);
+		    lexstop = 0;
+		}
+	    }
+	    break;
+	case LX2_INBRACK:
+	    if (!in_brace_param)
+		brct++;
+	    c = Inbrack;
+	    break;
+	case LX2_OUTBRACK:
+	    if (!in_brace_param)
+		brct--;
+	    if (brct < 0)
+		brct = 0;
+	    c = Outbrack;
+	    break;
+	case LX2_INPAR:
+	    if ((sub || in_brace_param) && isset(SHGLOB))
+		break;
+	    if (!in_brace_param) {
+		if (!sub) {
+		    e = hgetc();
+		    hungetc(e);
+		    lexstop = 0;
+		    if (e == ')' ||
+			(incmdpos && !brct && peek != ENVSTRING))
+			goto brk;
+		}
+		pct++;
+	    }
+	    c = Inpar;
+	    break;
+	case LX2_INBRACE:
+	    if (isset(IGNOREBRACES) || sub)
+		c = '{';
+	    else {
+		if (!len && incmdpos) {
+		    add('{');
+		    *bptr = '\0';
+		    return STRING;
+		}
+		if (in_brace_param)
+		    cmdpush(CS_BRACE);
+		bct++;
+	    }
+	    break;
+	case LX2_OUTBRACE:
+	    if ((isset(IGNOREBRACES) || sub) && !in_brace_param)
+		break;
+	    if (!bct)
+		break;
+	    if (in_brace_param)
+		cmdpop();
+	    if (bct-- == in_brace_param)
+		in_brace_param = 0;
+	    c = Outbrace;
+	    break;
+	case LX2_COMMA:
+	    if (unset(IGNOREBRACES) && !sub && bct > in_brace_param)
+		c = Comma;
+	    break;
+	case LX2_OUTANG:
+	    if (!intpos)
+		if (in_brace_param || sub)
+		    break;
+		else
+		    goto brk;
+	    e = hgetc();
+	    if (e != '(') {
+		hungetc(e);
+		lexstop = 0;
+		goto brk;
+	    }
+	    add(Outang);
+	    if (skipcomm()) {
+		peek = LEXERR;
+		goto brk;
+	    }
+	    c = Outpar;
+	    break;
+	case LX2_INANG:
+	    if (isset(SHGLOB) && sub)
+		break;
+	    e = hgetc();
+	    if (!(idigit(e) || e == '-' || (e == '(' && intpos))) {
+		hungetc(e);
+		lexstop = 0;
+		if (in_brace_param || sub)
+		    break;
+		goto brk;
+	    }
+	    c = Inang;
+	    if (e == '(') {
+		add(c);
+		if (skipcomm()) {
+		    peek = LEXERR;
+		    goto brk;
+		}
+		c = Outpar;
+	    } else {
+		add(c);
+		c = e;
+		while (c != '>' && !lexstop)
+		    add(c), c = hgetc();
+		c = Outang;
+	    }
+	    break;
+	case LX2_EQUALS:
+	    if (intpos) {
+		e = hgetc();
+		if (e != '(') {
+		    hungetc(e);
+		    lexstop = 0;
+		    c = Equals;
+		} else {
+		    add(Equals);
+		    if (skipcomm()) {
+			peek = LEXERR;
+			goto brk;
+		    }
+		    c = Outpar;
+		}
+	    } else if (!sub && peek != ENVSTRING &&
+		       incmdpos && !bct && !brct) {
+		char *t = tokstr;
+		if (idigit(*t))
+		    while (++t < bptr && idigit(*t));
+		else {
+		    while (iident(*t) && ++t < bptr);
+		    if (t < bptr) {
+			*bptr = '\0';
+			skipparens(Inbrack, Outbrack, &t);
+		    }
+		}
+		if (t == bptr) {
+		    e = hgetc();
+		    if (e == '(' && incmdpos) {
+			*bptr = '\0';
+			return ENVARRAY;
+		    }
+		    hungetc(e);
+		    lexstop = 0;
+		    peek = ENVSTRING;
+		    intpos = 2;
+		} else
+		    c = Equals;
+	    } else
+		c = Equals;
+	    break;
+	case LX2_BKSLASH:
+	    c = hgetc();
+	    if (c == '\n') {
+		c = hgetc();
+		if (!lexstop)
+		    continue;
+	    } else
+		add(Bnull);
+	    if (lexstop)
+		goto brk;
+	    break;
+	case LX2_QUOTE: {
+	    int strquote = (len && bptr[-1] == String);
+
+	    add(Snull);
+	    cmdpush(CS_QUOTE);
+	    for (;;) {
+		STOPHIST
+		while ((c = hgetc()) != '\'' && !lexstop) {
+		    if (strquote && c == '\\') {
+			add(c);
+			c = hgetc();
+			if (lexstop)
+			    break;
+		    } else if (!sub && isset(CSHJUNKIEQUOTES) && c == '\n') {
+			if (bptr[-1] == '\\')
+			    bptr--, len--;
+			else
+			    break;
+		    }
+		    add(c);
+		}
+		ALLOWHIST
+		if (c != '\'') {
+		    zerr("unmatched \'", NULL, 0);
+		    peek = LEXERR;
+		    cmdpop();
+		    goto brk;
+		}
+		e = hgetc();
+		if (e != '\'' || unset(RCQUOTES))
+		    break;
+		add(c);
+	    }
+	    cmdpop();
+	    hungetc(e);
+	    lexstop = 0;
+	    c = Snull;
+	    break;
+	}
+	case LX2_DQUOTE:
+	    add(Dnull);
+	    cmdpush(CS_DQUOTE);
+	    c = dquote_parse('"', sub);
+	    cmdpop();
+	    if (c) {
+		zerr("unmatched \"", NULL, 0);
+		peek = LEXERR;
+		goto brk;
+	    }
+	    c = Dnull;
+	    break;
+	case LX2_BQUOTE:
+	    add(Tick);
+	    cmdpush(CS_BQUOTE);
+	    SETPARBEGIN
+	    inquote = 0;
+	    while ((c = hgetc()) != '`' && !lexstop)
+		if (c == '\\') {
+		    c = hgetc();
+		    if (c != '\n') {
+			add(c == '`' || c == '\\' || c == '$' ? Bnull : '\\');
+			add(c);
+		    }
+		    else if (!sub && isset(CSHJUNKIEQUOTES))
+			add(c);
+		} else {
+		    if (!sub && isset(CSHJUNKIEQUOTES) && c == '\n') {
+			break;
+		    }
+		    add(c);
+		    if (c == '\'')
+			if ((inquote = !inquote))
+			    STOPHIST
+			else
+			    ALLOWHIST
+		}
+	    if (inquote)
+		ALLOWHIST
+	    cmdpop();
+	    if (c != '`') {
+		zerr("unmatched `", NULL, 0);
+		peek = LEXERR;
+		goto brk;
+	    }
+	    c = Tick;
+	    SETPAREND
+	    break;
+	}
+	add(c);
+	c = hgetc();
+	if (intpos)
+	    intpos--;
+	if (lexstop)
+	    break;
+    }
+  brk:
+    hungetc(c);
+    if (in_brace_param) {
+	while(bct-- >= in_brace_param)
+	    cmdpop();
+	zerr("closing brace expected", NULL, 0);
+    } else if (unset(IGNOREBRACES) && !sub && len > 1 &&
+	       peek == STRING && bptr[-1] == '}' && bptr[-2] != Bnull) {
+	/* hack to get {foo} command syntax work */
+	bptr--;
+	len--;
+	lexstop = 0;
+	hungetc('}');
+    }
+    *bptr = '\0';
+    DPUTS(cmdsp != ocmdsp, "BUG: gettok: cmdstack changed.");
+    return peek;
+}
+
+/**/
+static int
+dquote_parse(char endchar, int sub)
+{
+    int pct = 0, brct = 0, bct = 0, intick = 0, err = 0;
+    int c;
+    int math = endchar == ')' || endchar == ']';
+    int zlemath = math && cs > ll + addedx - inbufct;
+
+    while (((c = hgetc()) != endchar || bct ||
+	    (math && ((pct > 0) || (brct > 0))) ||
+	    intick) && !lexstop) {
+      cont:
+	switch (c) {
+	case '\\':
+	    c = hgetc();
+	    if (c != '\n') {
+		if (c == '$' || c == '\\' || (c == '}' && !intick && bct) ||
+		    c == endchar || c == '`')
+		    add(Bnull);
+		else {
+		    /* lexstop is implicitely handled here */
+		    add('\\');
+		    goto cont;
+		}
+	    } else if (sub || unset(CSHJUNKIEQUOTES) || endchar != '"')
+		continue;
+	    break;
+	case '\n':
+	    err = !sub && isset(CSHJUNKIEQUOTES) && endchar == '"';
+	    break;
+	case '$':
+	    if (intick)
+		break;
+	    c = hgetc();
+	    if (c == '(') {
+		add(Qstring);
+		err = cmd_or_math_sub();
+		c = Outpar;
+	    } else if (c == '[') {
+		add(String);
+		add(Inbrack);
+		cmdpush(CS_MATHSUBST);
+		err = dquote_parse(']', sub);
+		cmdpop();
+		c = Outbrack;
+	    } else if (c == '{') {
+		add(Qstring);
+		c = Inbrace;
+		cmdpush(CS_BRACEPAR);
+		bct++;
+	    } else if (c == '$')
+		add(Qstring);
+	    else {
+		hungetc(c);
+		lexstop = 0;
+		c = Qstring;
+	    }
+	    break;
+	case '}':
+	    if (intick || !bct)
+		break;
+	    c = Outbrace;
+	    bct--;
+	    cmdpop();
+	    break;
+	case '`':
+	    c = Qtick;
+	    if (intick == 2)
+		ALLOWHIST
+	    if ((intick = !intick)) {
+		SETPARBEGIN
+		cmdpush(CS_BQUOTE);
+	    } else {
+		SETPAREND
+	        cmdpop();
+	    }
+	    break;
+	case '\'':
+	    if (!intick)
+		break;
+	    if (intick == 1)
+		intick = 2, STOPHIST
+	    else
+		intick = 1, ALLOWHIST
+	    break;
+	case '(':
+	    pct++;
+	    break;
+	case ')':
+	    err = (!pct-- && math);
+	    break;
+	case '[':
+	    brct++;
+	    break;
+	case ']':
+	    err = (!brct-- && math);
+	    break;
+	case '"':
+	    if (intick || (!endchar && !bct))
+		break;
+	    if (bct) {
+		add(Dnull);
+		err = dquote_parse('"', sub);
+		c = Dnull;
+	    } else
+		err = 1;
+	    break;
+	}
+	if (err || lexstop)
+	    break;
+	add(c);
+    }
+    if (intick == 2)
+	ALLOWHIST
+    if (intick)
+	cmdpop();
+    while (bct--)
+	cmdpop();
+    if (lexstop)
+	err = intick || endchar || err;
+    else if (err == 1)
+	err = c;
+    if (zlemath && cs <= ll + 1 - inbufct)
+	inwhat = IN_MATH;
+    return err;
+}
+
+/* Tokenize a string given in s. Parsing is done as in double *
+ * quotes.  This is usually called before singsub().          */
+
+/**/
+int
+parsestr(char *s)
+{
+    int l = strlen(s), err;
+
+    HEAPALLOC {
+	lexsave();
+	untokenize(s);
+	inpush(dupstring(s), 0, NULL);
+	strinbeg();
+	stophist = 2;
+	len = 0;
+	bptr = tokstr = s;
+	bsiz = l + 1;
+	err = dquote_parse('\0', 1);
+	*bptr = '\0';
+	strinend();
+	inpop();
+	DPUTS(cmdsp, "BUG: parsestr: cmdstack not empty.");
+	lexrestore();
+	if (err) {
+	    untokenize(s);
+	    if (err > 32 && err < 127)
+		zerr("parse error near `%c'", NULL, err);
+	    else
+		zerr("parse error", NULL, 0);
+	}
+    } LASTALLOC;
+    return err;
+}
+
+/* Tokenize a string given in s. Parsing is done as if s were a normal *
+ * command-line argument but it may contain separators.  This is used  *
+ * to parse the right-hand side of ${...%...} substitutions.           */
+
+/**/
+int
+parse_subst_string(char *s)
+{
+    int c, l = strlen(s), err;
+
+    if (! *s)
+	return 0;
+    lexsave();
+    untokenize(s);
+    inpush(dupstring(s), 0, NULL);
+    strinbeg();
+    stophist = 2;
+    len = 0;
+    bptr = tokstr = s;
+    bsiz = l + 1;
+    c = hgetc();
+    c = gettokstr(c, 1);
+    err = errflag;
+    strinend();
+    inpop();
+    DPUTS(cmdsp, "BUG: parse_subst_string: cmdstack not empty.");
+    lexrestore();
+    errflag = err;
+    if (c == LEXERR) {
+	untokenize(s);
+	return 1;
+    }
+#ifdef DEBUG
+    if (c != STRING || len != l || errflag) {
+	fprintf(stderr, "Oops. Bug in parse_subst_string: %s\n",
+		len < l ? "len < l" : errflag ? "errflag" : "c != STRING");
+	fflush(stderr);
+	untokenize(s);
+	return 1;
+    }
+#endif
+    return 0;
+}
+
+/* expand aliases and reserved words */
+
+/**/
+int
+exalias(void)
+{
+    Alias an;
+    Reswd rw;
+
+    hwend();
+    if (interact && isset(SHINSTDIN) && !strin && !incasepat &&
+	tok == STRING && !nocorrect && !(inbufflags & INP_ALIAS) &&
+	(isset(CORRECTALL) || (isset(CORRECT) && incmdpos)))
+	spckword(&tokstr, 1, incmdpos, 1);
+
+    if (!tokstr) {
+	yytext = tokstrings[tok];
+	if (yytext)
+	    yytext = dupstring(yytext);
+	return 0;
+    }
+
+    if (has_token(tokstr)) {
+	char *p, *t;
+
+	yytext = p = ncalloc(strlen(tokstr) + 1);
+	for (t = tokstr; (*p++ = itok(*t) ? ztokens[*t++ - Pound] : *t++););
+    } else
+	yytext = tokstr;
+
+    if (zleparse && !(inbufflags & INP_ALIAS)) {
+	int zp = zleparse;
+
+	gotword();
+	if (zp == 1 && !zleparse) {
+	    return 0;
+	}
+    }
+
+    if (tok == STRING) {
+	/* Check for an alias */
+	an = noaliases ? NULL : (Alias) aliastab->getnode(aliastab, yytext);
+	if (an && !an->inuse && ((an->flags & ALIAS_GLOBAL) || incmdpos ||
+	     inalmore)) {
+	    inpush(an->text, INP_ALIAS, an);
+	    /* remove from history if it begins with space */
+	    if (isset(HISTIGNORESPACE) && an->text[0] == ' ')
+		remhist();
+	    lexstop = 0;
+	    return 1;
+	}
+
+	/* Then check for a reserved word */
+	if ((incmdpos ||
+	     (unset(IGNOREBRACES) && yytext[0] == '}' && !yytext[1])) &&
+	    (rw = (Reswd) reswdtab->getnode(reswdtab, yytext))) {
+	    tok = rw->token;
+	    if (tok == DINBRACK)
+		incond = 1;
+	} else if (incond && !strcmp(yytext, "]]")) {
+	    tok = DOUTBRACK;
+	    incond = 0;
+	} else if (incond && yytext[0] == '!' && !yytext[1])
+	    tok = BANG;
+    }
+    inalmore = 0;
+    return 0;
+}
+
+/* skip (...) */
+
+/**/
+static int
+skipcomm(void)
+{
+    int pct = 1, c;
+
+    cmdpush(CS_CMDSUBST);
+    SETPARBEGIN
+    c = Inpar;
+    do {
+	add(c);
+	c = hgetc();
+	if (itok(c) || lexstop)
+	    break;
+	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;
+	}
+    }
+    while (pct);
+    if (!lexstop)
+	SETPAREND
+    cmdpop();
+    return lexstop;
+}