/*
 * $Id: parse.c,v 3.1.0.2 1996/11/24 01:23:23 hzoli Exp $
 *
 * parse.c - parser
 *
 * This file is part of zsh, the Z shell.
 *
 * Copyright (c) 1992-1996 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.h"

#define YYERROR  { tok = LEXERR; return NULL; }
#define YYERRORV { tok = LEXERR; return; }

#define make_list()     allocnode(N_LIST)
#define make_sublist()  allocnode(N_SUBLIST)
#define make_pline()    allocnode(N_PLINE)
#define make_cmd()      allocnode(N_CMD)
#define make_forcmd()   allocnode(N_FOR)
#define make_casecmd()  allocnode(N_CASE)
#define make_ifcmd()    allocnode(N_IF)
#define make_whilecmd() allocnode(N_WHILE)
#define make_varnode()  allocnode(N_VARASG)
#define make_cond()     allocnode(N_COND)

/*
 * event	: ENDINPUT
 *			| SEPER
 *			| sublist [ SEPER | AMPER | AMPERBANG ]
 */
/**/
List
parse_event(void)
{
    tok = ENDINPUT;
    incmdpos = 1;
    yylex();
    return par_event();
}

/**/
List
par_event(void)
{
    Sublist sl;
    List l = NULL;

    while (tok == SEPER) {
	if (isnewlin > 0)
	    return NULL;
	yylex();
    }
    if (tok == ENDINPUT)
	return NULL;
    if ((sl = par_sublist()))
	if (tok == ENDINPUT) {
	    l = (List) make_list();
	    l->type = Z_SYNC;
	    l->left = sl;
	} else if (tok == SEPER) {
	    l = (List) make_list();
	    l->type = Z_SYNC;
	    l->left = sl;
	    if (isnewlin <= 0)
		yylex();
	} else if (tok == AMPER) {
	    l = (List) make_list();
	    l->type = Z_ASYNC;
	    l->left = sl;
	    yylex();
	} else if (tok == AMPERBANG) {
	    l = (List) make_list();
	    l->type = Z_ASYNC | Z_DISOWN;
	    l->left = sl;
	    yylex();
	} else
	    l = NULL;
    if (!l) {
	if (errflag) {
	    yyerror();
	    return NULL;
	}
	yyerror();
	errflag = 0;
	if (isnewlin <= 0) {
	    /* Discard remaining stuff after a parse error. */
	    int c;

	    hwbegin(0);
	    while ((c = hgetc()) != '\n' && !lexstop);
	    if (c == '\n')
		hungetc('\n');
	    hwend();
	}
	errflag = 1;
	return NULL;
    } else {
	l->right = par_event();
    }
    return l;
}

/**/
List
parse_list(void)
{
    List ret;

    tok = ENDINPUT;
    incmdpos = 1;
    yylex();
    ret = par_list();
    if (tok == LEXERR) {
	yyerror();
	return NULL;
    }
    return ret;
}

/*
 * list	: { SEPER } [ sublist [ { SEPER | AMPER | AMPERBANG } list ] ]
 */

/**/
List
par_list(void)
{
    Sublist sl;
    List l = NULL;

    while (tok == SEPER)
	yylex();
    if ((sl = par_sublist()))
	if (tok == SEPER || tok == AMPER || tok == AMPERBANG) {
	    l = (List) make_list();
	    l->left = sl;
	    l->type = (tok == SEPER) ? Z_SYNC :
		(tok == AMPER) ? Z_ASYNC : Z_ASYNC | Z_DISOWN;
	    incmdpos = 1;
	    do {
		yylex();
	    } while (tok == SEPER);
	    l->right = par_list();
	} else {
	    l = (List) make_list();
	    l->left = sl;
	    l->type = Z_SYNC;
	}
    return l;
}

/**/
List
par_list1(void)
{
    Sublist sl;
    List l = NULL;

    if ((sl = par_sublist())) {
	l = (List) make_list();
	l->type = Z_SYNC;
	l->left = sl;
    }
    return l;
}

/*
 * sublist	: sublist2 [ ( DBAR | DAMPER ) { SEPER } sublist ]
 */

/**/
Sublist
par_sublist(void)
{
    Sublist sl;

    if ((sl = par_sublist2()))
	if (tok == DBAR || tok == DAMPER) {
	    int qtok = tok;

	    cmdpush(tok == DBAR ? CS_CMDOR : CS_CMDAND);
	    yylex();
	    while (tok == SEPER)
		yylex();
	    sl->right = par_sublist();
	    sl->type = (qtok == DBAR) ? ORNEXT : ANDNEXT;
	    cmdpop();
	}
    return sl;
}

/*
 * sublist2	: [ COPROC | BANG ] pline
 */

/**/
Sublist
par_sublist2(void)
{
    Sublist sl;
    Pline p;

    sl = (Sublist) make_sublist();
    if (tok == COPROC) {
	sl->flags |= PFLAG_COPROC;
	yylex();
    } else if (tok == BANG) {
	sl->flags |= PFLAG_NOT;
	yylex();
    }
    if (!(p = par_pline()) && !sl->flags)
	return NULL;
    sl->left = p;
    return sl;
}

/*
 * pline	: cmd [ ( BAR | BARAMP ) { SEPER } pline ]
 */

/**/
Pline
par_pline(void)
{
    Cmd c;
    Pline p, p2;

    if (!(c = par_cmd()))
	return NULL;
    if (tok == BAR) {
	cmdpush(CS_PIPE);
	yylex();
	while (tok == SEPER)
	    yylex();
	p2 = par_pline();
	cmdpop();
	p = (Pline) make_pline();
	p->left = c;
	p->right = p2;
	p->type = PIPE;
	return p;
    } else if (tok == BARAMP) {
	struct redir *rdr = (struct redir *)allocnode(N_REDIR);

	rdr->type = MERGEOUT;
	rdr->fd1 = 2;
	rdr->name = dupstring("1");
	addlinknode(c->redir, rdr);

	cmdpush(CS_ERRPIPE);
	yylex();
	p2 = par_pline();
	cmdpop();
	p = (Pline) make_pline();
	p->left = c;
	p->right = p2;
	p->type = PIPE;
	return p;
    } else {
	p = (Pline) make_pline();
	p->left = c;
	p->type = END;
	return p;
    }
}

/*
 * cmd	: { redir } ( for | case | if | while | repeat |
 *				subsh | funcdef | time | dinbrack | dinpar | simple ) { redir }
 */

/**/
Cmd
par_cmd(void)
{
    Cmd c;

    c = (Cmd) make_cmd();
    c->lineno = lineno;
    c->args = newlinklist();
    c->redir = newlinklist();
    c->vars = newlinklist();
    while (IS_REDIROP(tok))
	par_redir(c->redir);
    switch (tok) {
    case FOR:
	cmdpush(CS_FOR);
	par_for(c);
	cmdpop();
	break;
    case FOREACH:
	cmdpush(CS_FOREACH);
	par_for(c);
	cmdpop();
	break;
    case SELECT:
	cmdpush(CS_SELECT);
	par_for(c);
	cmdpop();
	break;
    case CASE:
	cmdpush(CS_CASE);
	par_case(c);
	cmdpop();
	break;
    case IF:
	par_if(c);
	break;
    case WHILE:
	cmdpush(CS_WHILE);
	par_while(c);
	cmdpop();
	break;
    case UNTIL:
	cmdpush(CS_UNTIL);
	par_while(c);
	cmdpop();
	break;
    case REPEAT:
	cmdpush(CS_REPEAT);
	par_repeat(c);
	cmdpop();
	break;
    case INPAR:
	cmdpush(CS_SUBSH);
	par_subsh(c);
	cmdpop();
	break;
    case INBRACE:
	cmdpush(CS_CURSH);
	par_subsh(c);
	cmdpop();
	break;
    case FUNC:
	cmdpush(CS_FUNCDEF);
	par_funcdef(c);
	cmdpop();
	break;
    case TIME:
	par_time(c);
	break;
    case DINBRACK:
	cmdpush(CS_COND);
	par_dinbrack(c);
	cmdpop();
	break;
    case DINPAR:
	cmdpush(CS_MATH);
	par_dinpar(c);
	cmdpop();
	break;
    default:
	if (!par_simple(c))
	    return NULL;
	break;
    }
    while (IS_REDIROP(tok))
	par_redir(c->redir);
    incmdpos = 1;
    incasepat = 0;
    incond = 0;
    return c;
}

/*
 * for  : ( FOR DINPAR expr SEMI expr SEMI expr DOUTPAR |
 *    ( FOR[EACH] | SELECT ) name ( "in" wordlist | INPAR wordlist OUTPAR ) )
 *	{ SEPER } ( DO list DONE | INBRACE list OUTBRACE | list ZEND | list1 )
 */

/**/
void
par_for(Cmd c)
{
    Forcmd f;
    int csh = (tok == FOREACH);

    f = (Forcmd) make_forcmd();
    c->type = (tok == SELECT) ? CSELECT : CFOR;
    incmdpos = 0;
    infor = tok == FOR;
    yylex();
    if (tok == DINPAR) {
	yylex();
	if (tok != DINPAR)
	    YYERRORV;
	f->name = tokstr;
	yylex();
	if (tok != DINPAR)
	    YYERRORV;
	f->condition = tokstr;
	yylex();
	if (tok != DOUTPAR)
	    YYERRORV;
	f->advance = tokstr;
	infor = 0;
	yylex();
    } else {
	infor = 0;
	if (tok != STRING || !isident(tokstr))
	    YYERRORV;
	f->name = tokstr;
	incmdpos = 1;
	yylex();
	if (tok == STRING && !strcmp(tokstr, "in")) {
	    f->inflag = 1;
	    incmdpos = 0;
	    yylex();
	    c->args = par_wordlist();
	    if (tok != SEPER)
		YYERRORV;
	} else if (tok == INPAR) {
	    f->inflag = 1;
	    incmdpos = 0;
	    yylex();
	    c->args = par_nl_wordlist();
	    if (tok != OUTPAR)
		YYERRORV;
	    incmdpos = 1;
	    yylex();
	}
    }
    incmdpos = 1;
    while (tok == SEPER)
	yylex();
    if (tok == DO) {
	yylex();
	f->list = par_list();
	if (tok != DONE)
	    YYERRORV;
	yylex();
    } else if (tok == INBRACE) {
	yylex();
	f->list = par_list();
	if (tok != OUTBRACE)
	    YYERRORV;
	yylex();
    } else if (csh || isset(CSHJUNKIELOOPS)) {
	f->list = par_list();
	if (tok != ZEND)
	    YYERRORV;
	yylex();
    } else if (unset(SHORTLOOPS)) {
	YYERRORV;
    } else
	f->list = par_list1();
    c->u.forcmd = f;
}

/*
 * case	: CASE STRING { SEPER } ( "in" | INBRACE )
				{ { SEPER } STRING { BAR STRING } OUTPAR list [ DSEMI ] }
				{ SEPER } ( "esac" | OUTBRACE )
 */

/**/
void
par_case(Cmd c)
{
    int brflag;
    LinkList pats, lists;
    int n = 0;
    char **pp;
    List *ll;
    LinkNode no;
    struct casecmd *cc;

    c->type = CCASE;
    incmdpos = 0;
    yylex();
    if (tok != STRING)
	YYERRORV;
    addlinknode(c->args, tokstr);
    incmdpos = 1;
    yylex();
    while (tok == SEPER)
	yylex();
    if (!(tok == STRING && !strcmp(tokstr, "in")) && tok != INBRACE)
	YYERRORV;
    brflag = (tok == INBRACE);
    incasepat = 1;
    incmdpos = 0;
    yylex();
    cc = c->u.casecmd = (struct casecmd *)make_casecmd();
    pats = newlinklist();
    lists = newlinklist();
    for (;;) {
	char *str;

	while (tok == SEPER)
	    yylex();
	if (tok == OUTBRACE) {
	    yylex();
	    break;
	}
	if (tok != STRING)
	    YYERRORV;
	if (!strcmp(tokstr, "esac")) {
	    yylex();
	    break;
	}
	str = tokstr;
	incasepat = 0;
	incmdpos = 1;
	for (;;) {
	    yylex();
	    if (tok == OUTPAR) {
		incasepat = 0;
		incmdpos = 1;
		yylex();
		break;
	    } else if (tok == BAR) {
		char *str2;
		int sl = strlen(str);

		incasepat = 1;
		incmdpos = 0;
		str2 = ncalloc(sl + 2);
		strcpy(str2, str);
		str2[sl] = Bar;
		str2[sl+1] = '\0';
		str = str2;
	    } else {
		int sl = strlen(str);

		if (str[sl - 1] != Bar) {
		    /* POSIX allows (foo*) patterns */
		    int pct;
		    char *s = str;

		    for (s = str, pct = 0; *s; s++) {
			if (*s == Inpar)
			    pct++;
			if (!pct)
			    break;
			if (pct == 1) {
			    if (*s == Bar || *s == Inpar)
				while (iblank(s[1]))
				    chuck(s+1);
			    if (*s == Bar || *s == Outpar)
				while (iblank(s[-1]) &&
				       (s < str+2 || s[-2] != Meta))
				    chuck(--s);
			}
			if (*s == Outpar)
			    pct--;
		    }
		    if (*s || pct || s == str)
			YYERRORV;
		    break;
		} else {
		    char *str2;

		    if (tok != STRING)
			YYERRORV;
		    str2 = ncalloc(sl + strlen(tokstr) + 1);
		    strcpy(str2, str);
		    strcpy(str2 + sl, tokstr);
		    str = str2;
		}
	    }
	}
	addlinknode(pats, str);
	addlinknode(lists, par_list());
	n++;
	if ((tok == ESAC && !brflag) || (tok == OUTBRACE && brflag)) {
	    yylex();
	    break;
	}
	if (tok != DSEMI)
	    YYERRORV;
	incasepat = 1;
	incmdpos = 0;
	yylex();
    }

    cc->pats = (char **)alloc((n + 1) * sizeof(char *));

    for (pp = cc->pats, no = firstnode(pats); no; incnode(no))
	*pp++ = (char *)getdata(no);
    *pp = NULL;
    cc->lists = (List *) alloc((n + 1) * sizeof(List));
    for (ll = cc->lists, no = firstnode(lists); no; incnode(no), ll++)
	if (!(*ll = (List) getdata(no)))
	    *ll = &dummy_list;
    *ll = NULL;
}

/*
 * if	: { ( IF | ELIF ) { SEPER } ( INPAR list OUTPAR | list )
			{ SEPER } ( THEN list | INBRACE list OUTBRACE | list1 ) }
			[ FI | ELSE list FI | ELSE { SEPER } INBRACE list OUTBRACE ]
			(you get the idea...?)
 */

/**/
void
par_if(Cmd c)
{
    struct ifcmd *i;
    int xtok;
    unsigned char nc;
    LinkList ifsl, thensl;
    LinkNode no;
    int ni = 0, nt = 0, usebrace = 0;
    List l, *ll;

    ifsl = newlinklist();
    thensl = newlinklist();

    c->type = CIF;
    for (;;) {
	xtok = tok;
	cmdpush(xtok == IF ? CS_IF : CS_ELIF);
	yylex();
	if (xtok == FI)
	    break;
	if (xtok == ELSE)
	    break;
	while (tok == SEPER)
	    yylex();
	if (!(xtok == IF || xtok == ELIF)) {
	    cmdpop();
	    YYERRORV;
	}
	addlinknode(ifsl, par_list());
	ni++;
	incmdpos = 1;
	while (tok == SEPER)
	    yylex();
	xtok = FI;
	nc = cmdstack[cmdsp - 1] == CS_IF ? CS_IFTHEN : CS_ELIFTHEN;
	if (tok == THEN) {
	    usebrace = 0;
	    cmdpop();
	    cmdpush(nc);
	    yylex();
	    addlinknode(thensl, par_list());
	    nt++;
	    incmdpos = 1;
	    cmdpop();
	} else {
	    if (tok == INBRACE) {
		usebrace = 1;
		cmdpop();
		cmdpush(nc);
		yylex();
		l = par_list();
		if (tok != OUTBRACE) {
		    cmdpop();
		    YYERRORV;
		}
		addlinknode(thensl, l);
		nt++;
		yylex();
		incmdpos = 1;
		if (tok == SEPER)
		    break;
		cmdpop();
	    } else if (unset(SHORTLOOPS)) {
		cmdpop();
		YYERRORV;
	    } else {
		cmdpop();
		cmdpush(nc);
		addlinknode(thensl, par_list1());
		nt++;
		incmdpos = 1;
		break;
	    }
	}
    }
    cmdpop();
    if (xtok == ELSE) {
	cmdpush(CS_ELSE);
	while (tok == SEPER)
	    yylex();
	if (tok == INBRACE && usebrace) {
	    yylex();
	    l = par_list();
	    if (tok != OUTBRACE) {
		cmdpop();
		YYERRORV;
	    }
	} else {
	    l = par_list();
	    if (tok != FI) {
		cmdpop();
		YYERRORV;
	    }
	}
	addlinknode(thensl, l);
	nt++;
	yylex();
	cmdpop();
    }
    i = (struct ifcmd *)make_ifcmd();
    i->ifls = (List *) alloc((ni + 1) * sizeof(List));
    i->thenls = (List *) alloc((nt + 1) * sizeof(List));

    for (ll = i->ifls, no = firstnode(ifsl); no; incnode(no), ll++)
	if (!(*ll = (List) getdata(no)))
	    *ll = &dummy_list;
    *ll = NULL;
    for (ll = i->thenls, no = firstnode(thensl); no; incnode(no), ll++)
	if (!(*ll = (List) getdata(no)))
	    *ll = &dummy_list;
    *ll = NULL;

    c->u.ifcmd = i;
}

/*
 * while	: ( WHILE | UNTIL ) ( INPAR list OUTPAR | list ) { SEPER }
				( DO list DONE | INBRACE list OUTBRACE | list ZEND )
 */

/**/
void
par_while(Cmd c)
{
    struct whilecmd *w;

    c->type = CWHILE;
    w = c->u.whilecmd = (struct whilecmd *)make_whilecmd();
    w->cond = (tok == UNTIL);
    yylex();
    w->cont = par_list();
    incmdpos = 1;
    while (tok == SEPER)
	yylex();
    if (tok == DO) {
	yylex();
	w->loop = par_list();
	if (tok != DONE)
	    YYERRORV;
	yylex();
    } else if (tok == INBRACE) {
	yylex();
	w->loop = par_list();
	if (tok != OUTBRACE)
	    YYERRORV;
	yylex();
    } else if (isset(CSHJUNKIELOOPS)) {
	w->loop = par_list();
	if (tok != ZEND)
	    YYERRORV;
	yylex();
    } else
	YYERRORV;
}

/*
 * repeat	: REPEAT STRING { SEPER } ( DO list DONE | list1 )
 */

/**/
void
par_repeat(Cmd c)
{
    c->type = CREPEAT;
    incmdpos = 0;
    yylex();
    if (tok != STRING)
	YYERRORV;
    addlinknode(c->args, tokstr);
    incmdpos = 1;
    yylex();
    while (tok == SEPER)
	yylex();
    if (tok == DO) {
	yylex();
	c->u.list = par_list();
	if (tok != DONE)
	    YYERRORV;
	yylex();
    } else if (tok == INBRACE) {
	yylex();
	c->u.list = par_list();
	if (tok != OUTBRACE)
	    YYERRORV;
	yylex();
    } else if (isset(CSHJUNKIELOOPS)) {
	c->u.list = par_list();
	if (tok != ZEND)
	    YYERRORV;
	yylex();
    } else if (unset(SHORTLOOPS)) {
	YYERRORV;
    } else
	c->u.list = par_list1();
}

/*
 * subsh	: ( INPAR | INBRACE ) list ( OUTPAR | OUTBRACE )
 */

/**/
void
par_subsh(Cmd c)
{
    c->type = (tok == INPAR) ? SUBSH : CURSH;
    yylex();
    c->u.list = par_list();
    if (tok != ((c->type == SUBSH) ? OUTPAR : OUTBRACE))
	YYERRORV;
    incmdpos = 1;
    yylex();
}

/*
 * funcdef	: FUNCTION wordlist [ INOUTPAR ] { SEPER }
 *					( list1 | INBRACE list OUTBRACE )
 */

/**/
void
par_funcdef(Cmd c)
{
    nocorrect = 1;
    incmdpos = 0;
    yylex();
    c->type = FUNCDEF;
    c->args = newlinklist();
    incmdpos = 1;
    while (tok == STRING) {
	if (*tokstr == Inbrace && !tokstr[1]) {
	    tok = INBRACE;
	    break;
	}
	addlinknode(c->args, tokstr);
	yylex();
    }
    nocorrect = 0;
    if (tok == INOUTPAR)
	yylex();
    while (tok == SEPER)
	yylex();
    if (tok == INBRACE) {
	yylex();
	c->u.list = par_list();
	if (tok != OUTBRACE)
	    YYERRORV;
	yylex();
    } else if (unset(SHORTLOOPS)) {
	YYERRORV;
    } else
	c->u.list = par_list1();
}

/*
 * time	: TIME sublist2
 */

/**/
void
par_time(Cmd c)
{
    yylex();
    c->type = ZCTIME;
    c->u.pline = par_sublist2();
}

/*
 * dinbrack	: DINBRACK cond DOUTBRACK
 */

/**/
void
par_dinbrack(Cmd c)
{
    c->type = COND;
    incond = 1;
    incmdpos = 0;
    yylex();
    c->u.cond = par_cond();
    if (tok != DOUTBRACK)
	YYERRORV;
    incond = 0;
    incmdpos = 1;
    yylex();
}

/*
 * dinpar : DINPAR expr DOUTPAR
 */

/**/
void
par_dinpar(Cmd c)
{
    c->type = CARITH;
    incmdpos = 0;
    yylex();
    if (tok != DOUTPAR)
	YYERRORV;
    addlinknode(c->args, tokstr);
    incmdpos = 1;
    yylex();
}

/*
 * simple	: { COMMAND | EXEC | NOGLOB | NOCORRECT | DASH }
					{ STRING | ENVSTRING | ENVARRAY wordlist OUTPAR | redir }
					[ INOUTPAR { SEPER } ( list1 | INBRACE list OUTBRACE ) ]
 */

/**/
Cmd
par_simple(Cmd c)
{
    int isnull = 1;

    c->type = SIMPLE;
    for (;;) {
	if (tok == NOCORRECT)
	    nocorrect = 1;
	else if (tok == ENVSTRING) {
	    struct varasg *v = (struct varasg *)make_varnode();

	    v->type = PM_SCALAR;
	    equalsplit(v->name = tokstr, &v->str);
	    addlinknode(c->vars, v);
	    isnull = 0;
	} else if (tok == ENVARRAY) {
	    struct varasg *v = (struct varasg *)make_varnode();
	    int oldcmdpos = incmdpos;

	    v->type = PM_ARRAY;
	    incmdpos = 0;
	    v->name = tokstr;
	    cmdpush(CS_ARRAY);
	    yylex();
	    v->arr = par_nl_wordlist();
	    cmdpop();
	    if (tok != OUTPAR)
		YYERROR;
	    incmdpos = oldcmdpos;
	    addlinknode(c->vars, v);
	    isnull = 0;
	} else
	    break;
	yylex();
    }
    if (tok == AMPER || tok == AMPERBANG)
	YYERROR;
    for (;;) {
	if (tok == STRING) {
	    incmdpos = 0;
	    addlinknode(c->args, tokstr);
	    yylex();
	} else if (IS_REDIROP(tok)) {
	    par_redir(c->redir);
	} else if (tok == INOUTPAR) {
	    incmdpos = 1;
	    cmdpush(CS_FUNCDEF);
	    yylex();
	    while (tok == SEPER)
		yylex();
	    if (tok == INBRACE) {
		yylex();
		c->u.list = par_list();
		if (tok != OUTBRACE) {
		    cmdpop();
		    YYERROR;
		}
		yylex();
	    } else
		c->u.list = (List) expandstruct((struct node *) par_cmd(), N_LIST);
	    cmdpop();
	    c->type = FUNCDEF;
	} else
	    break;
	isnull = 0;
    }
    if (isnull && empty(c->redir))
	return NULL;
    incmdpos = 1;
    return c;
}

/*
 * condlex is yylex for normal parsing, but is altered to allow
 * the test builtin to use par_cond.
 */
void (*condlex) _((void)) = yylex;

/*
 * cond	: cond_1 { SEPER } [ DBAR { SEPER } cond ]
 */

/**/
Cond
par_cond(void)
{
    Cond c, c2;

    c = par_cond_1();
    while (tok == SEPER)
	condlex();
    if (tok == DBAR) {
	condlex();
	while (tok == SEPER)
	    condlex();
	c2 = (Cond) make_cond();
	c2->left = (void *) c;
	c2->right = (void *) par_cond();
	c2->type = COND_OR;
	return c2;
    }
    return c;
}

/*
 * cond_1 : cond_2 { SEPER } [ DAMPER { SEPER } cond_1 ]
 */

/**/
Cond
par_cond_1(void)
{
    Cond c, c2;

    c = par_cond_2();
    while (tok == SEPER)
	condlex();
    if (tok == DAMPER) {
	condlex();
	while (tok == SEPER)
	    condlex();
	c2 = (Cond) make_cond();
	c2->left = (void *) c;
	c2->right = (void *) par_cond_1();
	c2->type = COND_AND;
	return c2;
    }
    return c;
}

/*
 * cond_2	: BANG cond_2
				| INPAR { SEPER } cond_2 { SEPER } OUTPAR
				| STRING STRING STRING
				| STRING STRING
				| STRING ( INANG | OUTANG ) STRING
 */

/**/
Cond
par_cond_2(void)
{
    Cond c, c2;
    char *s1, *s2, *s3;
    int dble = 0;
    extern char **testargs;

    if (condlex == testlex) {
	/* See the description of test in POSIX 1003.2 */
	if (tok == NULLTOK)
	    /* no arguments: false */
	    return par_cond_double(dupstring("-n"), dupstring(""));
	if (!*testargs) {
	    /* one argument: [ foo ] is equivalent to [ -n foo ] */
	    s1 = tokstr;
	    condlex();
	    return par_cond_double(dupstring("-n"), s1);
	}
	if (testargs[1] && !testargs[2]) {
	    /* three arguments: if the second argument is a binary operator, *
	     * perform that binary test on the first and the trird argument  */
	    if (!strcmp(*testargs, "=")  ||
		!strcmp(*testargs, "==") ||
		!strcmp(*testargs, "!=") ||
		(**testargs == '-' && get_cond_num(*testargs + 1) >= 0)) {
		s1 = tokstr;
		condlex();
		s2 = tokstr;
		condlex();
		s3 = tokstr;
		condlex();
		return par_cond_triple(s1, s2, s3);
	    }
	}
    }
    if (tok == BANG) {
	condlex();
	c = par_cond_2();
	c2 = (Cond) make_cond();
	c2->left = (void *) c;
	c2->type = COND_NOT;
	return c2;
    }
    if (tok == INPAR) {
	condlex();
	while (tok == SEPER)
	    condlex();
	c = par_cond();
	while (tok == SEPER)
	    condlex();
	if (tok != OUTPAR)
	    YYERROR;
	condlex();
	return c;
    }
    if (tok != STRING)
	if (tok && tok != LEXERR && condlex == testlex) {
	    s1 = tokstr;
	    condlex();
	    return par_cond_double("-n", s1);
	} else
	    YYERROR;
    s1 = tokstr;
    if (condlex == testlex)
	dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1
		  && !s1[2]);
    condlex();
    if (tok == INANG || tok == OUTANG) {
	int xtok = tok;
	condlex();
	if (tok != STRING)
	    YYERROR;
	s3 = tokstr;
	condlex();
	c = (Cond) make_cond();
	c->left = (void *) s1;
	c->right = (void *) s3;
	c->type = (xtok == INANG) ? COND_STRLT : COND_STRGTR;
	c->ntype = NT_SET(N_COND, 1, NT_STR, NT_STR, 0, 0);
	return c;
    }
    if (tok != STRING)
	if (tok != LEXERR && condlex == testlex) {
	    if (!dble)
		return par_cond_double("-n", s1);
	    else if (!strcmp(s1, "-t"))
		return par_cond_double(s1, "1");
	} else
	    YYERROR;
    s2 = tokstr;
    incond++;			/* parentheses do globbing */
    condlex();
    incond--;			/* parentheses do grouping */
    if (tok == STRING && !dble) {
	s3 = tokstr;
	condlex();
	return par_cond_triple(s1, s2, s3);
    } else
	return par_cond_double(s1, s2);
}

/*
 * redir	: ( OUTANG | ... | TRINANG ) STRING
 */

/**/
void
par_redir(LinkList l)
{
    struct redir *fn = (struct redir *)allocnode(N_REDIR);
    int oldcmdpos, oldnc;

    oldcmdpos = incmdpos;
    incmdpos = 0;
    oldnc = nocorrect;
    if (tok != INANG && tok != INOUTANG)
	nocorrect = 1;
    fn->type = redirtab[tok - OUTANG];
    fn->fd1 = tokfd;
    yylex();
    if (tok != STRING && tok != ENVSTRING)
	YYERRORV;
    incmdpos = oldcmdpos;
    nocorrect = oldnc;

    /* assign default fd */
    if (fn->fd1 == -1)
	fn->fd1 = IS_READFD(fn->type) ? 0 : 1;

    fn->name = tokstr;

    switch (fn->type) {
    case HEREDOC:
    case HEREDOCDASH: {
	/* <<[-] name */
	struct heredocs **hd;

	for (hd = &hdocs; *hd; hd = &(*hd)->next);
	*hd = zalloc(sizeof(struct heredocs));
	(*hd)->next = NULL;
	(*hd)->rd = fn;
	break;
    }
    case WRITE:
    case WRITENOW:
	if (tokstr[0] == Outang && tokstr[1] == Inpar)
	    /* > >(...) */
	    fn->type = OUTPIPE;
	else if (tokstr[0] == Inang && tokstr[1] == Inpar)
	    YYERRORV;
	break;
    case READ:
	if (tokstr[0] == Inang && tokstr[1] == Inpar)
	    /* < <(...) */
	    fn->type = INPIPE;
	else if (tokstr[0] == Outang && tokstr[1] == Inpar)
	    YYERRORV;
	break;
    case READWRITE:
	if ((tokstr[0] == Inang || tokstr[0] == Outang) && tokstr[1] == Inpar)
	    fn->type = tokstr[0] == Inang ? INPIPE : OUTPIPE;
	break;
    }
    yylex();
    addlinknode(l, fn);
}

/*
 * wordlist	: { STRING }
 */

/**/
LinkList
par_wordlist(void)
{
    LinkList l;

    l = newlinklist();
    while (tok == STRING) {
	addlinknode(l, tokstr);
	yylex();
    }
    return l;
}

/*
 * nl_wordlist	: { STRING | SEPER }
 */

/**/
LinkList
par_nl_wordlist(void)
{
    LinkList l;

    l = newlinklist();
    while (tok == STRING || tok == SEPER) {
	if (tok != SEPER)
	    addlinknode(l, tokstr);
	yylex();
    }
    return l;
}

/**/
Cond
par_cond_double(char *a, char *b)
{
    Cond n = (Cond) make_cond();

    if (a[0] != '-' || !a[1] || a[2]) {
	zerr("parse error: condition expected: %s", a, 0);
	return NULL;
    }
    n->left = (void *) b;
    n->type = a[1];
    n->ntype = NT_SET(N_COND, 1, NT_STR, NT_STR, 0, 0);
    return n;
}

/**/
int
get_cond_num(char *tst)
{
    static char *condstrs[] =
    {
	"nt", "ot", "ef", "eq", "ne", "lt", "gt", "le", "ge", NULL
    };
    int t0;

    for (t0 = 0; condstrs[t0]; t0++)
	if (!strcmp(condstrs[t0], tst))
	    return t0;
    return -1;
}

/**/
Cond
par_cond_triple(char *a, char *b, char *c)
{
    Cond n = (Cond) make_cond();
    int t0;

    if ((b[0] == Equals || b[0] == '=') &&
	(!b[1] || ((b[1] == Equals || b[1] == '=') && !b[2])))
	n->type = COND_STREQ;
    else if (b[0] == '!' && (b[1] == Equals || b[1] == '=') && !b[2])
	n->type = COND_STRNEQ;
    else if (b[0] == '-') {
	if ((t0 = get_cond_num(b + 1)) > -1)
	    n->type = t0 + COND_NT;
	else
	    zerr("unrecognized condition: %s", b, 0);
    } else
	zerr("condition expected: %s", b, 0);
    n->left = (void *) a;
    n->right = (void *) c;
    n->ntype = NT_SET(N_COND, 1, NT_STR, NT_STR, 0, 0);
    return n;
}

/**/
void
yyerror(void)
{
    int t0;

    for (t0 = 0; t0 != 20; t0++)
	if (!yytext || !yytext[t0] || yytext[t0] == '\n')
	    break;
    if (t0 == 20)
	zerr("parse error near `%l...'", yytext, 20);
    else if (t0)
	zerr("parse error near `%l'", yytext, t0);
    else
	zerr("parse error", NULL, 0);
}
