parser.c revision 8855
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 3. All advertising materials mentioning features or use of this software
171556Srgrimes *    must display the following acknowledgement:
181556Srgrimes *	This product includes software developed by the University of
191556Srgrimes *	California, Berkeley and its contributors.
201556Srgrimes * 4. Neither the name of the University nor the names of its contributors
211556Srgrimes *    may be used to endorse or promote products derived from this software
221556Srgrimes *    without specific prior written permission.
231556Srgrimes *
241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341556Srgrimes * SUCH DAMAGE.
353044Sdg *
368855Srgrimes *	$Id: parser.c,v 1.5 1995/01/11 07:07:00 paul Exp $
371556Srgrimes */
381556Srgrimes
391556Srgrimes#ifndef lint
401556Srgrimesstatic char sccsid[] = "@(#)parser.c	8.1 (Berkeley) 5/31/93";
411556Srgrimes#endif /* not lint */
421556Srgrimes
431556Srgrimes#include "shell.h"
441556Srgrimes#include "parser.h"
451556Srgrimes#include "nodes.h"
461556Srgrimes#include "expand.h"	/* defines rmescapes() */
471556Srgrimes#include "redir.h"	/* defines copyfd() */
481556Srgrimes#include "syntax.h"
491556Srgrimes#include "options.h"
501556Srgrimes#include "input.h"
511556Srgrimes#include "output.h"
521556Srgrimes#include "var.h"
531556Srgrimes#include "error.h"
541556Srgrimes#include "memalloc.h"
551556Srgrimes#include "mystring.h"
561556Srgrimes#include "alias.h"
571556Srgrimes#include "myhistedit.h"
581556Srgrimes
591556Srgrimes
601556Srgrimes/*
611556Srgrimes * Shell command parser.
621556Srgrimes */
631556Srgrimes
641556Srgrimes#define EOFMARKLEN 79
651556Srgrimes
661556Srgrimes/* values returned by readtoken */
671556Srgrimes#include "token.def"
681556Srgrimes
691556Srgrimes
701556Srgrimes
711556Srgrimesstruct heredoc {
721556Srgrimes	struct heredoc *next;	/* next here document in list */
731556Srgrimes	union node *here;		/* redirection node */
741556Srgrimes	char *eofmark;		/* string indicating end of input */
751556Srgrimes	int striptabs;		/* if set, strip leading tabs */
761556Srgrimes};
771556Srgrimes
781556Srgrimes
791556Srgrimes
801556Srgrimesstruct heredoc *heredoclist;	/* list of here documents to read */
811556Srgrimesint parsebackquote;		/* nonzero if we are inside backquotes */
821556Srgrimesint doprompt;			/* if set, prompt the user */
831556Srgrimesint needprompt;			/* true if interactive and at start of line */
841556Srgrimesint lasttoken;			/* last token read */
851556SrgrimesMKINIT int tokpushback;		/* last token pushed back */
861556Srgrimeschar *wordtext;			/* text of last word returned by readtoken */
871556SrgrimesMKINIT int checkkwd;            /* 1 == check for kwds, 2 == also eat newlines */
881556Srgrimesstruct nodelist *backquotelist;
891556Srgrimesunion node *redirnode;
901556Srgrimesstruct heredoc *heredoc;
911556Srgrimesint quoteflag;			/* set if (part of) last token was quoted */
921556Srgrimesint startlinno;			/* line # where last token started */
931556Srgrimes
941556Srgrimes
951556Srgrimes#define GDB_HACK 1 /* avoid local declarations which gdb can't handle */
961556Srgrimes#ifdef GDB_HACK
971556Srgrimesstatic const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'};
981556Srgrimesstatic const char types[] = "}-+?=";
991556Srgrimes#endif
1001556Srgrimes
1011556Srgrimes
1021556SrgrimesSTATIC union node *list __P((int));
1031556SrgrimesSTATIC union node *andor __P((void));
1041556SrgrimesSTATIC union node *pipeline __P((void));
1051556SrgrimesSTATIC union node *command __P((void));
1061556SrgrimesSTATIC union node *simplecmd __P((union node **, union node *));
1071556SrgrimesSTATIC void parsefname __P((void));
1081556SrgrimesSTATIC void parseheredoc __P((void));
1091556SrgrimesSTATIC int readtoken __P((void));
1101556SrgrimesSTATIC int readtoken1 __P((int, char const *, char *, int));
1111556SrgrimesSTATIC void attyline __P((void));
1121556SrgrimesSTATIC int noexpand __P((char *));
1131556SrgrimesSTATIC void synexpect __P((int));
1141556SrgrimesSTATIC void synerror __P((char *));
1151556SrgrimesSTATIC void setprompt __P((int));
1161556Srgrimes
1171556Srgrimes/*
1181556Srgrimes * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
1191556Srgrimes * valid parse tree indicating a blank line.)
1201556Srgrimes */
1211556Srgrimes
1221556Srgrimesunion node *
1231556Srgrimesparsecmd(interact) {
1241556Srgrimes	int t;
1251556Srgrimes
1261556Srgrimes	doprompt = interact;
1271556Srgrimes	if (doprompt)
1281556Srgrimes		setprompt(1);
1291556Srgrimes	else
1301556Srgrimes		setprompt(0);
1311556Srgrimes	needprompt = 0;
1321556Srgrimes	t = readtoken();
1331556Srgrimes	if (t == TEOF)
1341556Srgrimes		return NEOF;
1351556Srgrimes	if (t == TNL)
1361556Srgrimes		return NULL;
1371556Srgrimes	tokpushback++;
1381556Srgrimes	return list(1);
1391556Srgrimes}
1401556Srgrimes
1411556Srgrimes
1421556SrgrimesSTATIC union node *
1431556Srgrimeslist(nlflag) {
1441556Srgrimes	union node *n1, *n2, *n3;
1451556Srgrimes
1461556Srgrimes	checkkwd = 2;
1471556Srgrimes	if (nlflag == 0 && tokendlist[peektoken()])
1481556Srgrimes		return NULL;
1491556Srgrimes	n1 = andor();
1501556Srgrimes	for (;;) {
1511556Srgrimes		switch (readtoken()) {
1521556Srgrimes		case TBACKGND:
1531556Srgrimes			if (n1->type == NCMD || n1->type == NPIPE) {
1541556Srgrimes				n1->ncmd.backgnd = 1;
1551556Srgrimes			} else if (n1->type == NREDIR) {
1561556Srgrimes				n1->type = NBACKGND;
1571556Srgrimes			} else {
1581556Srgrimes				n3 = (union node *)stalloc(sizeof (struct nredir));
1591556Srgrimes				n3->type = NBACKGND;
1601556Srgrimes				n3->nredir.n = n1;
1611556Srgrimes				n3->nredir.redirect = NULL;
1621556Srgrimes				n1 = n3;
1631556Srgrimes			}
1641556Srgrimes			goto tsemi;
1651556Srgrimes		case TNL:
1661556Srgrimes			tokpushback++;
1671556Srgrimes			/* fall through */
1681556Srgrimestsemi:	    case TSEMI:
1691556Srgrimes			if (readtoken() == TNL) {
1701556Srgrimes				parseheredoc();
1711556Srgrimes				if (nlflag)
1721556Srgrimes					return n1;
1731556Srgrimes			} else {
1741556Srgrimes				tokpushback++;
1751556Srgrimes			}
1761556Srgrimes			checkkwd = 2;
1771556Srgrimes			if (tokendlist[peektoken()])
1781556Srgrimes				return n1;
1791556Srgrimes			n2 = andor();
1801556Srgrimes			n3 = (union node *)stalloc(sizeof (struct nbinary));
1811556Srgrimes			n3->type = NSEMI;
1821556Srgrimes			n3->nbinary.ch1 = n1;
1831556Srgrimes			n3->nbinary.ch2 = n2;
1841556Srgrimes			n1 = n3;
1851556Srgrimes			break;
1861556Srgrimes		case TEOF:
1871556Srgrimes			if (heredoclist)
1881556Srgrimes				parseheredoc();
1891556Srgrimes			else
1901556Srgrimes				pungetc();		/* push back EOF on input */
1911556Srgrimes			return n1;
1921556Srgrimes		default:
1931556Srgrimes			if (nlflag)
1941556Srgrimes				synexpect(-1);
1951556Srgrimes			tokpushback++;
1961556Srgrimes			return n1;
1971556Srgrimes		}
1981556Srgrimes	}
1991556Srgrimes}
2001556Srgrimes
2011556Srgrimes
2021556Srgrimes
2031556SrgrimesSTATIC union node *
2041556Srgrimesandor() {
2051556Srgrimes	union node *n1, *n2, *n3;
2061556Srgrimes	int t;
2071556Srgrimes
2081556Srgrimes	n1 = pipeline();
2091556Srgrimes	for (;;) {
2101556Srgrimes		if ((t = readtoken()) == TAND) {
2111556Srgrimes			t = NAND;
2121556Srgrimes		} else if (t == TOR) {
2131556Srgrimes			t = NOR;
2141556Srgrimes		} else {
2151556Srgrimes			tokpushback++;
2161556Srgrimes			return n1;
2171556Srgrimes		}
2181556Srgrimes		n2 = pipeline();
2191556Srgrimes		n3 = (union node *)stalloc(sizeof (struct nbinary));
2201556Srgrimes		n3->type = t;
2211556Srgrimes		n3->nbinary.ch1 = n1;
2221556Srgrimes		n3->nbinary.ch2 = n2;
2231556Srgrimes		n1 = n3;
2241556Srgrimes	}
2251556Srgrimes}
2261556Srgrimes
2271556Srgrimes
2281556Srgrimes
2291556SrgrimesSTATIC union node *
2301556Srgrimespipeline() {
2311556Srgrimes	union node *n1, *pipenode, *notnode;
2321556Srgrimes	struct nodelist *lp, *prev;
2331556Srgrimes	int negate = 0;
2341556Srgrimes
2351556Srgrimes	TRACE(("pipeline: entered\n"));
2361556Srgrimes	while (readtoken() == TNOT) {
2371556Srgrimes		TRACE(("pipeline: TNOT recognized\n"));
2381556Srgrimes		negate = !negate;
2391556Srgrimes	}
2401556Srgrimes	tokpushback++;
2411556Srgrimes	n1 = command();
2421556Srgrimes	if (readtoken() == TPIPE) {
2431556Srgrimes		pipenode = (union node *)stalloc(sizeof (struct npipe));
2441556Srgrimes		pipenode->type = NPIPE;
2451556Srgrimes		pipenode->npipe.backgnd = 0;
2461556Srgrimes		lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
2471556Srgrimes		pipenode->npipe.cmdlist = lp;
2481556Srgrimes		lp->n = n1;
2491556Srgrimes		do {
2501556Srgrimes			prev = lp;
2511556Srgrimes			lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
2521556Srgrimes			lp->n = command();
2531556Srgrimes			prev->next = lp;
2541556Srgrimes		} while (readtoken() == TPIPE);
2551556Srgrimes		lp->next = NULL;
2561556Srgrimes		n1 = pipenode;
2571556Srgrimes	}
2581556Srgrimes	tokpushback++;
2591556Srgrimes	if (negate) {
2601556Srgrimes		notnode = (union node *)stalloc(sizeof (struct nnot));
2611556Srgrimes		notnode->type = NNOT;
2621556Srgrimes		notnode->nnot.com = n1;
2631556Srgrimes		n1 = notnode;
2641556Srgrimes	}
2651556Srgrimes	return n1;
2661556Srgrimes}
2671556Srgrimes
2681556Srgrimes
2691556Srgrimes
2701556SrgrimesSTATIC union node *
2711556Srgrimescommand() {
2721556Srgrimes	union node *n1, *n2;
2731556Srgrimes	union node *ap, **app;
2741556Srgrimes	union node *cp, **cpp;
2751556Srgrimes	union node *redir, **rpp;
2761556Srgrimes	int t;
2771556Srgrimes
2781556Srgrimes	checkkwd = 2;
2791556Srgrimes	redir = 0;
2801556Srgrimes	rpp = &redir;
2811556Srgrimes	/* Check for redirection which may precede command */
2821556Srgrimes	while (readtoken() == TREDIR) {
2831556Srgrimes		*rpp = n2 = redirnode;
2841556Srgrimes		rpp = &n2->nfile.next;
2851556Srgrimes		parsefname();
2861556Srgrimes	}
2871556Srgrimes	tokpushback++;
2881556Srgrimes
2891556Srgrimes	switch (readtoken()) {
2901556Srgrimes	case TIF:
2911556Srgrimes		n1 = (union node *)stalloc(sizeof (struct nif));
2921556Srgrimes		n1->type = NIF;
2931556Srgrimes		n1->nif.test = list(0);
2941556Srgrimes		if (readtoken() != TTHEN)
2951556Srgrimes			synexpect(TTHEN);
2961556Srgrimes		n1->nif.ifpart = list(0);
2971556Srgrimes		n2 = n1;
2981556Srgrimes		while (readtoken() == TELIF) {
2991556Srgrimes			n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
3001556Srgrimes			n2 = n2->nif.elsepart;
3011556Srgrimes			n2->type = NIF;
3021556Srgrimes			n2->nif.test = list(0);
3031556Srgrimes			if (readtoken() != TTHEN)
3041556Srgrimes				synexpect(TTHEN);
3051556Srgrimes			n2->nif.ifpart = list(0);
3061556Srgrimes		}
3071556Srgrimes		if (lasttoken == TELSE)
3081556Srgrimes			n2->nif.elsepart = list(0);
3091556Srgrimes		else {
3101556Srgrimes			n2->nif.elsepart = NULL;
3111556Srgrimes			tokpushback++;
3121556Srgrimes		}
3131556Srgrimes		if (readtoken() != TFI)
3141556Srgrimes			synexpect(TFI);
3151556Srgrimes		checkkwd = 1;
3161556Srgrimes		break;
3171556Srgrimes	case TWHILE:
3181556Srgrimes	case TUNTIL: {
3191556Srgrimes		int got;
3201556Srgrimes		n1 = (union node *)stalloc(sizeof (struct nbinary));
3211556Srgrimes		n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
3221556Srgrimes		n1->nbinary.ch1 = list(0);
3231556Srgrimes		if ((got=readtoken()) != TDO) {
3241556SrgrimesTRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
3251556Srgrimes			synexpect(TDO);
3261556Srgrimes		}
3271556Srgrimes		n1->nbinary.ch2 = list(0);
3281556Srgrimes		if (readtoken() != TDONE)
3291556Srgrimes			synexpect(TDONE);
3301556Srgrimes		checkkwd = 1;
3311556Srgrimes		break;
3321556Srgrimes	}
3331556Srgrimes	case TFOR:
3341556Srgrimes		if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
3351556Srgrimes			synerror("Bad for loop variable");
3361556Srgrimes		n1 = (union node *)stalloc(sizeof (struct nfor));
3371556Srgrimes		n1->type = NFOR;
3381556Srgrimes		n1->nfor.var = wordtext;
3391556Srgrimes		if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
3401556Srgrimes			app = ≈
3411556Srgrimes			while (readtoken() == TWORD) {
3421556Srgrimes				n2 = (union node *)stalloc(sizeof (struct narg));
3431556Srgrimes				n2->type = NARG;
3441556Srgrimes				n2->narg.text = wordtext;
3451556Srgrimes				n2->narg.backquote = backquotelist;
3461556Srgrimes				*app = n2;
3471556Srgrimes				app = &n2->narg.next;
3481556Srgrimes			}
3491556Srgrimes			*app = NULL;
3501556Srgrimes			n1->nfor.args = ap;
3511556Srgrimes			if (lasttoken != TNL && lasttoken != TSEMI)
3521556Srgrimes				synexpect(-1);
3531556Srgrimes		} else {
3541556Srgrimes#ifndef GDB_HACK
3551556Srgrimes			static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
3561556Srgrimes								   '@', '=', '\0'};
3571556Srgrimes#endif
3581556Srgrimes			n2 = (union node *)stalloc(sizeof (struct narg));
3591556Srgrimes			n2->type = NARG;
3601556Srgrimes			n2->narg.text = (char *)argvars;
3611556Srgrimes			n2->narg.backquote = NULL;
3621556Srgrimes			n2->narg.next = NULL;
3631556Srgrimes			n1->nfor.args = n2;
3641556Srgrimes			/*
3651556Srgrimes			 * Newline or semicolon here is optional (but note
3661556Srgrimes			 * that the original Bourne shell only allowed NL).
3671556Srgrimes			 */
3681556Srgrimes			if (lasttoken != TNL && lasttoken != TSEMI)
3691556Srgrimes				tokpushback++;
3701556Srgrimes		}
3711556Srgrimes		checkkwd = 2;
3721556Srgrimes		if ((t = readtoken()) == TDO)
3731556Srgrimes			t = TDONE;
3741556Srgrimes		else if (t == TBEGIN)
3751556Srgrimes			t = TEND;
3761556Srgrimes		else
3771556Srgrimes			synexpect(-1);
3781556Srgrimes		n1->nfor.body = list(0);
3791556Srgrimes		if (readtoken() != t)
3801556Srgrimes			synexpect(t);
3811556Srgrimes		checkkwd = 1;
3821556Srgrimes		break;
3831556Srgrimes	case TCASE:
3841556Srgrimes		n1 = (union node *)stalloc(sizeof (struct ncase));
3851556Srgrimes		n1->type = NCASE;
3861556Srgrimes		if (readtoken() != TWORD)
3871556Srgrimes			synexpect(TWORD);
3881556Srgrimes		n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
3891556Srgrimes		n2->type = NARG;
3901556Srgrimes		n2->narg.text = wordtext;
3911556Srgrimes		n2->narg.backquote = backquotelist;
3921556Srgrimes		n2->narg.next = NULL;
3931556Srgrimes		while (readtoken() == TNL);
3941556Srgrimes		if (lasttoken != TWORD || ! equal(wordtext, "in"))
3951556Srgrimes			synerror("expecting \"in\"");
3961556Srgrimes		cpp = &n1->ncase.cases;
3972760Ssef		checkkwd = 2, readtoken();
3982760Ssef		do {
3991556Srgrimes			*cpp = cp = (union node *)stalloc(sizeof (struct nclist));
4001556Srgrimes			cp->type = NCLIST;
4011556Srgrimes			app = &cp->nclist.pattern;
4021556Srgrimes			for (;;) {
4031556Srgrimes				*app = ap = (union node *)stalloc(sizeof (struct narg));
4041556Srgrimes				ap->type = NARG;
4051556Srgrimes				ap->narg.text = wordtext;
4061556Srgrimes				ap->narg.backquote = backquotelist;
4072760Ssef				if (checkkwd = 2, readtoken() != TPIPE)
4081556Srgrimes					break;
4091556Srgrimes				app = &ap->narg.next;
4102760Ssef				readtoken();
4111556Srgrimes			}
4121556Srgrimes			ap->narg.next = NULL;
4131556Srgrimes			if (lasttoken != TRP)
4141556Srgrimes				synexpect(TRP);
4151556Srgrimes			cp->nclist.body = list(0);
4162760Ssef
4172760Ssef			checkkwd = 2;
4182760Ssef			if ((t = readtoken()) != TESAC) {
4192760Ssef				if (t != TENDCASE)
4202760Ssef					synexpect(TENDCASE);
4212760Ssef				else
4222760Ssef					checkkwd = 2, readtoken();
4232760Ssef			}
4241556Srgrimes			cpp = &cp->nclist.next;
4252760Ssef		} while(lasttoken != TESAC);
4261556Srgrimes		*cpp = NULL;
4271556Srgrimes		checkkwd = 1;
4281556Srgrimes		break;
4291556Srgrimes	case TLP:
4301556Srgrimes		n1 = (union node *)stalloc(sizeof (struct nredir));
4311556Srgrimes		n1->type = NSUBSHELL;
4321556Srgrimes		n1->nredir.n = list(0);
4331556Srgrimes		n1->nredir.redirect = NULL;
4341556Srgrimes		if (readtoken() != TRP)
4351556Srgrimes			synexpect(TRP);
4361556Srgrimes		checkkwd = 1;
4371556Srgrimes		break;
4381556Srgrimes	case TBEGIN:
4391556Srgrimes		n1 = list(0);
4401556Srgrimes		if (readtoken() != TEND)
4411556Srgrimes			synexpect(TEND);
4421556Srgrimes		checkkwd = 1;
4431556Srgrimes		break;
4441556Srgrimes	/* Handle an empty command like other simple commands.  */
4451556Srgrimes	case TNL:
4461556Srgrimes	case TWORD:
4471556Srgrimes		tokpushback++;
4481556Srgrimes		return simplecmd(rpp, redir);
4491556Srgrimes	default:
4501556Srgrimes		synexpect(-1);
4511556Srgrimes	}
4521556Srgrimes
4531556Srgrimes	/* Now check for redirection which may follow command */
4541556Srgrimes	while (readtoken() == TREDIR) {
4551556Srgrimes		*rpp = n2 = redirnode;
4561556Srgrimes		rpp = &n2->nfile.next;
4571556Srgrimes		parsefname();
4581556Srgrimes	}
4591556Srgrimes	tokpushback++;
4601556Srgrimes	*rpp = NULL;
4611556Srgrimes	if (redir) {
4621556Srgrimes		if (n1->type != NSUBSHELL) {
4631556Srgrimes			n2 = (union node *)stalloc(sizeof (struct nredir));
4641556Srgrimes			n2->type = NREDIR;
4651556Srgrimes			n2->nredir.n = n1;
4661556Srgrimes			n1 = n2;
4671556Srgrimes		}
4681556Srgrimes		n1->nredir.redirect = redir;
4691556Srgrimes	}
4701556Srgrimes	return n1;
4711556Srgrimes}
4721556Srgrimes
4731556Srgrimes
4741556SrgrimesSTATIC union node *
4758855Srgrimessimplecmd(rpp, redir)
4761556Srgrimes	union node **rpp, *redir;
4771556Srgrimes	{
4781556Srgrimes	union node *args, **app;
4791556Srgrimes	union node **orig_rpp = rpp;
4801556Srgrimes	union node *n;
4811556Srgrimes
4821556Srgrimes	/* If we don't have any redirections already, then we must reset */
4831556Srgrimes	/* rpp to be the address of the local redir variable.  */
4841556Srgrimes	if (redir == 0)
4851556Srgrimes		rpp = &redir;
4861556Srgrimes
4871556Srgrimes	args = NULL;
4881556Srgrimes	app = &args;
4898855Srgrimes	/*
4901556Srgrimes	 * We save the incoming value, because we need this for shell
4911556Srgrimes	 * functions.  There can not be a redirect or an argument between
4928855Srgrimes	 * the function name and the open parenthesis.
4931556Srgrimes	 */
4941556Srgrimes	orig_rpp = rpp;
4951556Srgrimes
4961556Srgrimes	for (;;) {
4971556Srgrimes		if (readtoken() == TWORD) {
4981556Srgrimes			n = (union node *)stalloc(sizeof (struct narg));
4991556Srgrimes			n->type = NARG;
5001556Srgrimes			n->narg.text = wordtext;
5011556Srgrimes			n->narg.backquote = backquotelist;
5021556Srgrimes			*app = n;
5031556Srgrimes			app = &n->narg.next;
5041556Srgrimes		} else if (lasttoken == TREDIR) {
5051556Srgrimes			*rpp = n = redirnode;
5061556Srgrimes			rpp = &n->nfile.next;
5071556Srgrimes			parsefname();	/* read name of redirection file */
5081556Srgrimes		} else if (lasttoken == TLP && app == &args->narg.next
5091556Srgrimes					    && rpp == orig_rpp) {
5101556Srgrimes			/* We have a function */
5111556Srgrimes			if (readtoken() != TRP)
5121556Srgrimes				synexpect(TRP);
5131556Srgrimes#ifdef notdef
5141556Srgrimes			if (! goodname(n->narg.text))
5151556Srgrimes				synerror("Bad function name");
5161556Srgrimes#endif
5171556Srgrimes			n->type = NDEFUN;
5181556Srgrimes			n->narg.next = command();
5191556Srgrimes			return n;
5201556Srgrimes		} else {
5211556Srgrimes			tokpushback++;
5221556Srgrimes			break;
5231556Srgrimes		}
5241556Srgrimes	}
5251556Srgrimes	*app = NULL;
5261556Srgrimes	*rpp = NULL;
5271556Srgrimes	n = (union node *)stalloc(sizeof (struct ncmd));
5281556Srgrimes	n->type = NCMD;
5291556Srgrimes	n->ncmd.backgnd = 0;
5301556Srgrimes	n->ncmd.args = args;
5311556Srgrimes	n->ncmd.redirect = redir;
5321556Srgrimes	return n;
5331556Srgrimes}
5341556Srgrimes
5351556Srgrimes
5361556SrgrimesSTATIC void
5371556Srgrimesparsefname() {
5381556Srgrimes	union node *n = redirnode;
5391556Srgrimes
5401556Srgrimes	if (readtoken() != TWORD)
5411556Srgrimes		synexpect(-1);
5421556Srgrimes	if (n->type == NHERE) {
5431556Srgrimes		struct heredoc *here = heredoc;
5441556Srgrimes		struct heredoc *p;
5451556Srgrimes		int i;
5461556Srgrimes
5471556Srgrimes		if (quoteflag == 0)
5481556Srgrimes			n->type = NXHERE;
5491556Srgrimes		TRACE(("Here document %d\n", n->type));
5501556Srgrimes		if (here->striptabs) {
5511556Srgrimes			while (*wordtext == '\t')
5521556Srgrimes				wordtext++;
5531556Srgrimes		}
5541556Srgrimes		if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
5551556Srgrimes			synerror("Illegal eof marker for << redirection");
5561556Srgrimes		rmescapes(wordtext);
5571556Srgrimes		here->eofmark = wordtext;
5581556Srgrimes		here->next = NULL;
5591556Srgrimes		if (heredoclist == NULL)
5601556Srgrimes			heredoclist = here;
5611556Srgrimes		else {
5621556Srgrimes			for (p = heredoclist ; p->next ; p = p->next);
5631556Srgrimes			p->next = here;
5641556Srgrimes		}
5651556Srgrimes	} else if (n->type == NTOFD || n->type == NFROMFD) {
5661556Srgrimes		if (is_digit(wordtext[0]))
5671556Srgrimes			n->ndup.dupfd = digit_val(wordtext[0]);
5681556Srgrimes		else if (wordtext[0] == '-')
5691556Srgrimes			n->ndup.dupfd = -1;
5701556Srgrimes		else
5711556Srgrimes			goto bad;
5721556Srgrimes		if (wordtext[1] != '\0') {
5731556Srgrimesbad:
5741556Srgrimes			synerror("Bad fd number");
5751556Srgrimes		}
5761556Srgrimes	} else {
5771556Srgrimes		n->nfile.fname = (union node *)stalloc(sizeof (struct narg));
5781556Srgrimes		n = n->nfile.fname;
5791556Srgrimes		n->type = NARG;
5801556Srgrimes		n->narg.next = NULL;
5811556Srgrimes		n->narg.text = wordtext;
5821556Srgrimes		n->narg.backquote = backquotelist;
5831556Srgrimes	}
5841556Srgrimes}
5851556Srgrimes
5861556Srgrimes
5871556Srgrimes/*
5881556Srgrimes * Input any here documents.
5891556Srgrimes */
5901556Srgrimes
5911556SrgrimesSTATIC void
5921556Srgrimesparseheredoc() {
5931556Srgrimes	struct heredoc *here;
5941556Srgrimes	union node *n;
5951556Srgrimes
5961556Srgrimes	while (heredoclist) {
5971556Srgrimes		here = heredoclist;
5981556Srgrimes		heredoclist = here->next;
5991556Srgrimes		if (needprompt) {
6001556Srgrimes			setprompt(2);
6011556Srgrimes			needprompt = 0;
6021556Srgrimes		}
6031556Srgrimes		readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
6041556Srgrimes				here->eofmark, here->striptabs);
6051556Srgrimes		n = (union node *)stalloc(sizeof (struct narg));
6061556Srgrimes		n->narg.type = NARG;
6071556Srgrimes		n->narg.next = NULL;
6081556Srgrimes		n->narg.text = wordtext;
6091556Srgrimes		n->narg.backquote = backquotelist;
6101556Srgrimes		here->here->nhere.doc = n;
6111556Srgrimes	}
6121556Srgrimes}
6131556Srgrimes
6141556SrgrimesSTATIC int
6151556Srgrimespeektoken() {
6161556Srgrimes	int t;
6171556Srgrimes
6181556Srgrimes	t = readtoken();
6191556Srgrimes	tokpushback++;
6201556Srgrimes	return (t);
6211556Srgrimes}
6221556Srgrimes
6231556SrgrimesSTATIC int xxreadtoken();
6241556Srgrimes
6251556SrgrimesSTATIC int
6261556Srgrimesreadtoken() {
6271556Srgrimes	int t;
6281556Srgrimes	int savecheckkwd = checkkwd;
6291556Srgrimes	struct alias *ap;
6301556Srgrimes#ifdef DEBUG
6311556Srgrimes	int alreadyseen = tokpushback;
6321556Srgrimes#endif
6338855Srgrimes
6341556Srgrimes	top:
6351556Srgrimes	t = xxreadtoken();
6361556Srgrimes
6371556Srgrimes	if (checkkwd) {
6381556Srgrimes		/*
6391556Srgrimes		 * eat newlines
6401556Srgrimes		 */
6411556Srgrimes		if (checkkwd == 2) {
6421556Srgrimes			checkkwd = 0;
6431556Srgrimes			while (t == TNL) {
6441556Srgrimes				parseheredoc();
6451556Srgrimes				t = xxreadtoken();
6461556Srgrimes			}
6471556Srgrimes		} else
6481556Srgrimes			checkkwd = 0;
6491556Srgrimes		/*
6501556Srgrimes		 * check for keywords and aliases
6511556Srgrimes		 */
6521556Srgrimes		if (t == TWORD && !quoteflag) {
6531556Srgrimes			register char * const *pp, *s;
6541556Srgrimes
6551556Srgrimes			for (pp = (char **)parsekwd; *pp; pp++) {
6561556Srgrimes				if (**pp == *wordtext && equal(*pp, wordtext)) {
6571556Srgrimes					lasttoken = t = pp - parsekwd + KWDOFFSET;
6581556Srgrimes					TRACE(("keyword %s recognized\n", tokname[t]));
6591556Srgrimes					goto out;
6601556Srgrimes				}
6611556Srgrimes			}
6621556Srgrimes			if (ap = lookupalias(wordtext, 1)) {
6631556Srgrimes				pushstring(ap->val, strlen(ap->val), ap);
6641556Srgrimes				checkkwd = savecheckkwd;
6651556Srgrimes				goto top;
6661556Srgrimes			}
6671556Srgrimes		}
6681556Srgrimesout:
6691556Srgrimes		checkkwd = 0;
6701556Srgrimes	}
6711556Srgrimes#ifdef DEBUG
6721556Srgrimes	if (!alreadyseen)
6731556Srgrimes	    TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
6741556Srgrimes	else
6751556Srgrimes	    TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
6761556Srgrimes#endif
6771556Srgrimes	return (t);
6781556Srgrimes}
6791556Srgrimes
6801556Srgrimes
6811556Srgrimes/*
6821556Srgrimes * Read the next input token.
6831556Srgrimes * If the token is a word, we set backquotelist to the list of cmds in
6841556Srgrimes *	backquotes.  We set quoteflag to true if any part of the word was
6851556Srgrimes *	quoted.
6861556Srgrimes * If the token is TREDIR, then we set redirnode to a structure containing
6871556Srgrimes *	the redirection.
6881556Srgrimes * In all cases, the variable startlinno is set to the number of the line
6891556Srgrimes *	on which the token starts.
6901556Srgrimes *
6911556Srgrimes * [Change comment:  here documents and internal procedures]
6921556Srgrimes * [Readtoken shouldn't have any arguments.  Perhaps we should make the
6931556Srgrimes *  word parsing code into a separate routine.  In this case, readtoken
6941556Srgrimes *  doesn't need to have any internal procedures, but parseword does.
6951556Srgrimes *  We could also make parseoperator in essence the main routine, and
6961556Srgrimes *  have parseword (readtoken1?) handle both words and redirection.]
6971556Srgrimes */
6981556Srgrimes
6991556Srgrimes#define RETURN(token)	return lasttoken = token
7001556Srgrimes
7011556SrgrimesSTATIC int
7021556Srgrimesxxreadtoken() {
7031556Srgrimes	register c;
7041556Srgrimes
7051556Srgrimes	if (tokpushback) {
7061556Srgrimes		tokpushback = 0;
7071556Srgrimes		return lasttoken;
7081556Srgrimes	}
7091556Srgrimes	if (needprompt) {
7101556Srgrimes		setprompt(2);
7111556Srgrimes		needprompt = 0;
7121556Srgrimes	}
7131556Srgrimes	startlinno = plinno;
7141556Srgrimes	for (;;) {	/* until token or start of word found */
7151556Srgrimes		c = pgetc_macro();
7161556Srgrimes		if (c == ' ' || c == '\t')
7171556Srgrimes			continue;		/* quick check for white space first */
7181556Srgrimes		switch (c) {
7191556Srgrimes		case ' ': case '\t':
7201556Srgrimes			continue;
7211556Srgrimes		case '#':
7221556Srgrimes			while ((c = pgetc()) != '\n' && c != PEOF);
7231556Srgrimes			pungetc();
7241556Srgrimes			continue;
7251556Srgrimes		case '\\':
7261556Srgrimes			if (pgetc() == '\n') {
7271556Srgrimes				startlinno = ++plinno;
7281556Srgrimes				if (doprompt)
7291556Srgrimes					setprompt(2);
7301556Srgrimes				else
7311556Srgrimes					setprompt(0);
7321556Srgrimes				continue;
7331556Srgrimes			}
7341556Srgrimes			pungetc();
7351556Srgrimes			goto breakloop;
7361556Srgrimes		case '\n':
7371556Srgrimes			plinno++;
7381556Srgrimes			needprompt = doprompt;
7391556Srgrimes			RETURN(TNL);
7401556Srgrimes		case PEOF:
7411556Srgrimes			RETURN(TEOF);
7421556Srgrimes		case '&':
7431556Srgrimes			if (pgetc() == '&')
7441556Srgrimes				RETURN(TAND);
7451556Srgrimes			pungetc();
7461556Srgrimes			RETURN(TBACKGND);
7471556Srgrimes		case '|':
7481556Srgrimes			if (pgetc() == '|')
7491556Srgrimes				RETURN(TOR);
7501556Srgrimes			pungetc();
7511556Srgrimes			RETURN(TPIPE);
7521556Srgrimes		case ';':
7531556Srgrimes			if (pgetc() == ';')
7541556Srgrimes				RETURN(TENDCASE);
7551556Srgrimes			pungetc();
7561556Srgrimes			RETURN(TSEMI);
7571556Srgrimes		case '(':
7581556Srgrimes			RETURN(TLP);
7591556Srgrimes		case ')':
7601556Srgrimes			RETURN(TRP);
7611556Srgrimes		default:
7621556Srgrimes			goto breakloop;
7631556Srgrimes		}
7641556Srgrimes	}
7651556Srgrimesbreakloop:
7661556Srgrimes	return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
7671556Srgrimes#undef RETURN
7681556Srgrimes}
7691556Srgrimes
7701556Srgrimes
7711556Srgrimes
7721556Srgrimes/*
7731556Srgrimes * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
7741556Srgrimes * is not NULL, read a here document.  In the latter case, eofmark is the
7751556Srgrimes * word which marks the end of the document and striptabs is true if
7761556Srgrimes * leading tabs should be stripped from the document.  The argument firstc
7771556Srgrimes * is the first character of the input token or document.
7781556Srgrimes *
7791556Srgrimes * Because C does not have internal subroutines, I have simulated them
7801556Srgrimes * using goto's to implement the subroutine linkage.  The following macros
7811556Srgrimes * will run code that appears at the end of readtoken1.
7821556Srgrimes */
7831556Srgrimes
7841556Srgrimes#define CHECKEND()	{goto checkend; checkend_return:;}
7851556Srgrimes#define PARSEREDIR()	{goto parseredir; parseredir_return:;}
7861556Srgrimes#define PARSESUB()	{goto parsesub; parsesub_return:;}
7871556Srgrimes#define PARSEBACKQOLD()	{oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
7881556Srgrimes#define PARSEBACKQNEW()	{oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
7891556Srgrimes#define	PARSEARITH()	{goto parsearith; parsearith_return:;}
7901556Srgrimes
7911556SrgrimesSTATIC int
7921556Srgrimesreadtoken1(firstc, syntax, eofmark, striptabs)
7931556Srgrimes	int firstc;
7941556Srgrimes	char const *syntax;
7951556Srgrimes	char *eofmark;
7961556Srgrimes	int striptabs;
7971556Srgrimes	{
7981556Srgrimes	register c = firstc;
7991556Srgrimes	register char *out;
8001556Srgrimes	int len;
8011556Srgrimes	char line[EOFMARKLEN + 1];
8021556Srgrimes	struct nodelist *bqlist;
8031556Srgrimes	int quotef;
8041556Srgrimes	int dblquote;
8051556Srgrimes	int varnest;	/* levels of variables expansion */
8061556Srgrimes	int arinest;	/* levels of arithmetic expansion */
8071556Srgrimes	int parenlevel;	/* levels of parens in arithmetic */
8081556Srgrimes	int oldstyle;
8091556Srgrimes	char const *prevsyntax;	/* syntax before arithmetic */
8101556Srgrimes
8111556Srgrimes	startlinno = plinno;
8121556Srgrimes	dblquote = 0;
8131556Srgrimes	if (syntax == DQSYNTAX)
8141556Srgrimes		dblquote = 1;
8151556Srgrimes	quotef = 0;
8161556Srgrimes	bqlist = NULL;
8171556Srgrimes	varnest = 0;
8181556Srgrimes	arinest = 0;
8191556Srgrimes	parenlevel = 0;
8201556Srgrimes
8211556Srgrimes	STARTSTACKSTR(out);
8221556Srgrimes	loop: {	/* for each line, until end of word */
8231556Srgrimes#if ATTY
8241556Srgrimes		if (c == '\034' && doprompt
8251556Srgrimes		 && attyset() && ! equal(termval(), "emacs")) {
8261556Srgrimes			attyline();
8271556Srgrimes			if (syntax == BASESYNTAX)
8281556Srgrimes				return readtoken();
8291556Srgrimes			c = pgetc();
8301556Srgrimes			goto loop;
8311556Srgrimes		}
8321556Srgrimes#endif
8331556Srgrimes		CHECKEND();	/* set c to PEOF if at end of here document */
8341556Srgrimes		for (;;) {	/* until end of line or end of word */
8351556Srgrimes			CHECKSTRSPACE(3, out);	/* permit 3 calls to USTPUTC */
8361556Srgrimes			if (parsebackquote && c == '\\') {
8371556Srgrimes				c = pgetc();	/* XXX - compat with old /bin/sh */
8385507Spaul				if (/*c != '\\' && */c != '`' && c != '$') {
8391556Srgrimes					pungetc();
8401556Srgrimes					c = '\\';
8411556Srgrimes				}
8421556Srgrimes			}
8431556Srgrimes			switch(syntax[c]) {
8441556Srgrimes			case CNL:	/* '\n' */
8451556Srgrimes				if (syntax == BASESYNTAX)
8461556Srgrimes					goto endword;	/* exit outer loop */
8471556Srgrimes				USTPUTC(c, out);
8481556Srgrimes				plinno++;
8491556Srgrimes				if (doprompt)
8501556Srgrimes					setprompt(2);
8511556Srgrimes				else
8521556Srgrimes					setprompt(0);
8531556Srgrimes				c = pgetc();
8541556Srgrimes				goto loop;		/* continue outer loop */
8551556Srgrimes			case CWORD:
8561556Srgrimes				USTPUTC(c, out);
8571556Srgrimes				break;
8581556Srgrimes			case CCTL:
8591556Srgrimes				if (eofmark == NULL || dblquote)
8601556Srgrimes					USTPUTC(CTLESC, out);
8611556Srgrimes				USTPUTC(c, out);
8621556Srgrimes				break;
8631556Srgrimes			case CBACK:	/* backslash */
8641556Srgrimes				c = pgetc();
8651556Srgrimes				if (c == PEOF) {
8661556Srgrimes					USTPUTC('\\', out);
8671556Srgrimes					pungetc();
8681556Srgrimes				} else if (c == '\n') {
8691556Srgrimes					if (doprompt)
8701556Srgrimes						setprompt(2);
8711556Srgrimes					else
8721556Srgrimes						setprompt(0);
8731556Srgrimes				} else {
8741556Srgrimes					if (dblquote && c != '\\' && c != '`' && c != '$'
8751556Srgrimes							 && (c != '"' || eofmark != NULL))
8761556Srgrimes						USTPUTC('\\', out);
8771556Srgrimes					if (SQSYNTAX[c] == CCTL)
8781556Srgrimes						USTPUTC(CTLESC, out);
8791556Srgrimes					USTPUTC(c, out);
8801556Srgrimes					quotef++;
8811556Srgrimes				}
8821556Srgrimes				break;
8831556Srgrimes			case CSQUOTE:
8841556Srgrimes				syntax = SQSYNTAX;
8851556Srgrimes				break;
8861556Srgrimes			case CDQUOTE:
8871556Srgrimes				syntax = DQSYNTAX;
8881556Srgrimes				dblquote = 1;
8891556Srgrimes				break;
8901556Srgrimes			case CENDQUOTE:
8911556Srgrimes				if (eofmark) {
8921556Srgrimes					USTPUTC(c, out);
8931556Srgrimes				} else {
8941556Srgrimes					if (arinest)
8951556Srgrimes						syntax = ARISYNTAX;
8961556Srgrimes					else
8971556Srgrimes						syntax = BASESYNTAX;
8981556Srgrimes					quotef++;
8991556Srgrimes					dblquote = 0;
9001556Srgrimes				}
9011556Srgrimes				break;
9021556Srgrimes			case CVAR:	/* '$' */
9031556Srgrimes				PARSESUB();		/* parse substitution */
9041556Srgrimes				break;
9051556Srgrimes			case CENDVAR:	/* '}' */
9061556Srgrimes				if (varnest > 0) {
9071556Srgrimes					varnest--;
9081556Srgrimes					USTPUTC(CTLENDVAR, out);
9091556Srgrimes				} else {
9101556Srgrimes					USTPUTC(c, out);
9111556Srgrimes				}
9121556Srgrimes				break;
9131556Srgrimes			case CLP:	/* '(' in arithmetic */
9141556Srgrimes				parenlevel++;
9151556Srgrimes				USTPUTC(c, out);
9161556Srgrimes				break;
9171556Srgrimes			case CRP:	/* ')' in arithmetic */
9181556Srgrimes				if (parenlevel > 0) {
9191556Srgrimes					USTPUTC(c, out);
9201556Srgrimes					--parenlevel;
9211556Srgrimes				} else {
9221556Srgrimes					if (pgetc() == ')') {
9231556Srgrimes						if (--arinest == 0) {
9241556Srgrimes							USTPUTC(CTLENDARI, out);
9251556Srgrimes							syntax = prevsyntax;
9261556Srgrimes						} else
9271556Srgrimes							USTPUTC(')', out);
9281556Srgrimes					} else {
9298855Srgrimes						/*
9301556Srgrimes						 * unbalanced parens
9311556Srgrimes						 *  (don't 2nd guess - no error)
9321556Srgrimes						 */
9331556Srgrimes						pungetc();
9341556Srgrimes						USTPUTC(')', out);
9351556Srgrimes					}
9361556Srgrimes				}
9371556Srgrimes				break;
9381556Srgrimes			case CBQUOTE:	/* '`' */
9391556Srgrimes				PARSEBACKQOLD();
9401556Srgrimes				break;
9411556Srgrimes			case CEOF:
9421556Srgrimes				goto endword;		/* exit outer loop */
9431556Srgrimes			default:
9441556Srgrimes				if (varnest == 0)
9451556Srgrimes					goto endword;	/* exit outer loop */
9461556Srgrimes				USTPUTC(c, out);
9471556Srgrimes			}
9481556Srgrimes			c = pgetc_macro();
9491556Srgrimes		}
9501556Srgrimes	}
9511556Srgrimesendword:
9521556Srgrimes	if (syntax == ARISYNTAX)
9531556Srgrimes		synerror("Missing '))'");
9541556Srgrimes	if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
9551556Srgrimes		synerror("Unterminated quoted string");
9561556Srgrimes	if (varnest != 0) {
9571556Srgrimes		startlinno = plinno;
9581556Srgrimes		synerror("Missing '}'");
9591556Srgrimes	}
9601556Srgrimes	USTPUTC('\0', out);
9611556Srgrimes	len = out - stackblock();
9621556Srgrimes	out = stackblock();
9631556Srgrimes	if (eofmark == NULL) {
9641556Srgrimes		if ((c == '>' || c == '<')
9651556Srgrimes		 && quotef == 0
9661556Srgrimes		 && len <= 2
9671556Srgrimes		 && (*out == '\0' || is_digit(*out))) {
9681556Srgrimes			PARSEREDIR();
9691556Srgrimes			return lasttoken = TREDIR;
9701556Srgrimes		} else {
9711556Srgrimes			pungetc();
9721556Srgrimes		}
9731556Srgrimes	}
9741556Srgrimes	quoteflag = quotef;
9751556Srgrimes	backquotelist = bqlist;
9761556Srgrimes	grabstackblock(len);
9771556Srgrimes	wordtext = out;
9781556Srgrimes	return lasttoken = TWORD;
9791556Srgrimes/* end of readtoken routine */
9801556Srgrimes
9811556Srgrimes
9821556Srgrimes
9831556Srgrimes/*
9841556Srgrimes * Check to see whether we are at the end of the here document.  When this
9851556Srgrimes * is called, c is set to the first character of the next input line.  If
9861556Srgrimes * we are at the end of the here document, this routine sets the c to PEOF.
9871556Srgrimes */
9881556Srgrimes
9891556Srgrimescheckend: {
9901556Srgrimes	if (eofmark) {
9911556Srgrimes		if (striptabs) {
9921556Srgrimes			while (c == '\t')
9931556Srgrimes				c = pgetc();
9941556Srgrimes		}
9951556Srgrimes		if (c == *eofmark) {
9961556Srgrimes			if (pfgets(line, sizeof line) != NULL) {
9971556Srgrimes				register char *p, *q;
9981556Srgrimes
9991556Srgrimes				p = line;
10001556Srgrimes				for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
10011556Srgrimes				if (*p == '\n' && *q == '\0') {
10021556Srgrimes					c = PEOF;
10031556Srgrimes					plinno++;
10041556Srgrimes					needprompt = doprompt;
10051556Srgrimes				} else {
10061556Srgrimes					pushstring(line, strlen(line), NULL);
10071556Srgrimes				}
10081556Srgrimes			}
10091556Srgrimes		}
10101556Srgrimes	}
10111556Srgrimes	goto checkend_return;
10121556Srgrimes}
10131556Srgrimes
10141556Srgrimes
10151556Srgrimes/*
10161556Srgrimes * Parse a redirection operator.  The variable "out" points to a string
10171556Srgrimes * specifying the fd to be redirected.  The variable "c" contains the
10181556Srgrimes * first character of the redirection operator.
10191556Srgrimes */
10201556Srgrimes
10211556Srgrimesparseredir: {
10221556Srgrimes	char fd = *out;
10231556Srgrimes	union node *np;
10241556Srgrimes
10251556Srgrimes	np = (union node *)stalloc(sizeof (struct nfile));
10261556Srgrimes	if (c == '>') {
10271556Srgrimes		np->nfile.fd = 1;
10281556Srgrimes		c = pgetc();
10291556Srgrimes		if (c == '>')
10301556Srgrimes			np->type = NAPPEND;
10311556Srgrimes		else if (c == '&')
10321556Srgrimes			np->type = NTOFD;
10331556Srgrimes		else {
10341556Srgrimes			np->type = NTO;
10351556Srgrimes			pungetc();
10361556Srgrimes		}
10371556Srgrimes	} else {	/* c == '<' */
10381556Srgrimes		np->nfile.fd = 0;
10391556Srgrimes		c = pgetc();
10401556Srgrimes		if (c == '<') {
10411556Srgrimes			if (sizeof (struct nfile) != sizeof (struct nhere)) {
10421556Srgrimes				np = (union node *)stalloc(sizeof (struct nhere));
10431556Srgrimes				np->nfile.fd = 0;
10441556Srgrimes			}
10451556Srgrimes			np->type = NHERE;
10461556Srgrimes			heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
10471556Srgrimes			heredoc->here = np;
10481556Srgrimes			if ((c = pgetc()) == '-') {
10491556Srgrimes				heredoc->striptabs = 1;
10501556Srgrimes			} else {
10511556Srgrimes				heredoc->striptabs = 0;
10521556Srgrimes				pungetc();
10531556Srgrimes			}
10541556Srgrimes		} else if (c == '&')
10551556Srgrimes			np->type = NFROMFD;
10561556Srgrimes		else {
10571556Srgrimes			np->type = NFROM;
10581556Srgrimes			pungetc();
10591556Srgrimes		}
10601556Srgrimes	}
10611556Srgrimes	if (fd != '\0')
10621556Srgrimes		np->nfile.fd = digit_val(fd);
10631556Srgrimes	redirnode = np;
10641556Srgrimes	goto parseredir_return;
10651556Srgrimes}
10661556Srgrimes
10671556Srgrimes
10681556Srgrimes/*
10691556Srgrimes * Parse a substitution.  At this point, we have read the dollar sign
10701556Srgrimes * and nothing else.
10711556Srgrimes */
10721556Srgrimes
10731556Srgrimesparsesub: {
10741556Srgrimes	int subtype;
10751556Srgrimes	int typeloc;
10761556Srgrimes	int flags;
10771556Srgrimes	char *p;
10781556Srgrimes#ifndef GDB_HACK
10791556Srgrimes	static const char types[] = "}-+?=";
10801556Srgrimes#endif
10811556Srgrimes
10821556Srgrimes	c = pgetc();
10831556Srgrimes	if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) {
10841556Srgrimes		USTPUTC('$', out);
10851556Srgrimes		pungetc();
10861556Srgrimes	} else if (c == '(') {	/* $(command) or $((arith)) */
10871556Srgrimes		if (pgetc() == '(') {
10881556Srgrimes			PARSEARITH();
10891556Srgrimes		} else {
10901556Srgrimes			pungetc();
10911556Srgrimes			PARSEBACKQNEW();
10921556Srgrimes		}
10931556Srgrimes	} else {
10941556Srgrimes		USTPUTC(CTLVAR, out);
10951556Srgrimes		typeloc = out - stackblock();
10961556Srgrimes		USTPUTC(VSNORMAL, out);
10971556Srgrimes		subtype = VSNORMAL;
10981556Srgrimes		if (c == '{') {
10991556Srgrimes			c = pgetc();
11001556Srgrimes			subtype = 0;
11011556Srgrimes		}
11021556Srgrimes		if (is_name(c)) {
11031556Srgrimes			do {
11041556Srgrimes				STPUTC(c, out);
11051556Srgrimes				c = pgetc();
11061556Srgrimes			} while (is_in_name(c));
11071556Srgrimes		} else {
11081556Srgrimes			if (! is_special(c))
11091556Srgrimesbadsub:				synerror("Bad substitution");
11101556Srgrimes			USTPUTC(c, out);
11111556Srgrimes			c = pgetc();
11121556Srgrimes		}
11131556Srgrimes		STPUTC('=', out);
11141556Srgrimes		flags = 0;
11151556Srgrimes		if (subtype == 0) {
11161556Srgrimes			if (c == ':') {
11171556Srgrimes				flags = VSNUL;
11181556Srgrimes				c = pgetc();
11191556Srgrimes			}
11201556Srgrimes			p = strchr(types, c);
11211556Srgrimes			if (p == NULL)
11221556Srgrimes				goto badsub;
11231556Srgrimes			subtype = p - types + VSNORMAL;
11241556Srgrimes		} else {
11251556Srgrimes			pungetc();
11261556Srgrimes		}
11271556Srgrimes		if (dblquote || arinest)
11281556Srgrimes			flags |= VSQUOTE;
11291556Srgrimes		*(stackblock() + typeloc) = subtype | flags;
11301556Srgrimes		if (subtype != VSNORMAL)
11311556Srgrimes			varnest++;
11321556Srgrimes	}
11331556Srgrimes	goto parsesub_return;
11341556Srgrimes}
11351556Srgrimes
11361556Srgrimes
11371556Srgrimes/*
11381556Srgrimes * Called to parse command substitutions.  Newstyle is set if the command
11391556Srgrimes * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11401556Srgrimes * list of commands (passed by reference), and savelen is the number of
11411556Srgrimes * characters on the top of the stack which must be preserved.
11421556Srgrimes */
11431556Srgrimes
11441556Srgrimesparsebackq: {
11451556Srgrimes	struct nodelist **nlpp;
11461556Srgrimes	int savepbq;
11471556Srgrimes	union node *n;
11481556Srgrimes	char *volatile str;
11491556Srgrimes	struct jmploc jmploc;
11501556Srgrimes	struct jmploc *volatile savehandler;
11511556Srgrimes	int savelen;
11521556Srgrimes
11531556Srgrimes	savepbq = parsebackquote;
11541556Srgrimes	if (setjmp(jmploc.loc)) {
11551556Srgrimes		if (str)
11561556Srgrimes			ckfree(str);
11571556Srgrimes		parsebackquote = 0;
11581556Srgrimes		handler = savehandler;
11591556Srgrimes		longjmp(handler->loc, 1);
11601556Srgrimes	}
11611556Srgrimes	INTOFF;
11621556Srgrimes	str = NULL;
11631556Srgrimes	savelen = out - stackblock();
11641556Srgrimes	if (savelen > 0) {
11651556Srgrimes		str = ckmalloc(savelen);
11661556Srgrimes		bcopy(stackblock(), str, savelen);
11671556Srgrimes	}
11681556Srgrimes	savehandler = handler;
11691556Srgrimes	handler = &jmploc;
11701556Srgrimes	INTON;
11711556Srgrimes        if (oldstyle) {
11721556Srgrimes                /* We must read until the closing backquote, giving special
11731556Srgrimes                   treatment to some slashes, and then push the string and
11741556Srgrimes                   reread it as input, interpreting it normally.  */
11751556Srgrimes                register char *out;
11761556Srgrimes                register c;
11771556Srgrimes                int savelen;
11781556Srgrimes                char *str;
11798855Srgrimes
11801556Srgrimes                STARTSTACKSTR(out);
11811556Srgrimes                while ((c = pgetc ()) != '`') {
11821556Srgrimes                       if (c == '\\') {
11831556Srgrimes                                c = pgetc ();
11845507Spaul                                if ( c != '\\' && c != '`' && c != '$'
11851556Srgrimes                                    && (!dblquote || c != '"'))
11861556Srgrimes                                        STPUTC('\\', out);
11871556Srgrimes                       }
11881556Srgrimes                       STPUTC(c, out);
11891556Srgrimes                }
11901556Srgrimes                STPUTC('\0', out);
11911556Srgrimes                savelen = out - stackblock();
11921556Srgrimes                if (savelen > 0) {
11931556Srgrimes                        str = ckmalloc(savelen);
11941556Srgrimes                        bcopy(stackblock(), str, savelen);
11951556Srgrimes                }
11961556Srgrimes                setinputstring(str, 1);
11971556Srgrimes        }
11981556Srgrimes	nlpp = &bqlist;
11991556Srgrimes	while (*nlpp)
12001556Srgrimes		nlpp = &(*nlpp)->next;
12011556Srgrimes	*nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
12021556Srgrimes	(*nlpp)->next = NULL;
12031556Srgrimes	parsebackquote = oldstyle;
12041556Srgrimes	n = list(0);
12051556Srgrimes        if (!oldstyle && (readtoken() != TRP))
12061556Srgrimes                synexpect(TRP);
12071556Srgrimes	(*nlpp)->n = n;
12081556Srgrimes        /* Start reading from old file again.  */
12091556Srgrimes        if (oldstyle)
12101556Srgrimes                popfile();
12111556Srgrimes	while (stackblocksize() <= savelen)
12121556Srgrimes		growstackblock();
12131556Srgrimes	STARTSTACKSTR(out);
12141556Srgrimes	if (str) {
12151556Srgrimes		bcopy(str, out, savelen);
12161556Srgrimes		STADJUST(savelen, out);
12171556Srgrimes		INTOFF;
12181556Srgrimes		ckfree(str);
12191556Srgrimes		str = NULL;
12201556Srgrimes		INTON;
12211556Srgrimes	}
12221556Srgrimes	parsebackquote = savepbq;
12231556Srgrimes	handler = savehandler;
12241556Srgrimes	if (arinest || dblquote)
12251556Srgrimes		USTPUTC(CTLBACKQ | CTLQUOTE, out);
12261556Srgrimes	else
12271556Srgrimes		USTPUTC(CTLBACKQ, out);
12281556Srgrimes	if (oldstyle)
12291556Srgrimes		goto parsebackq_oldreturn;
12301556Srgrimes	else
12311556Srgrimes		goto parsebackq_newreturn;
12321556Srgrimes}
12331556Srgrimes
12341556Srgrimes/*
12351556Srgrimes * Parse an arithmetic expansion (indicate start of one and set state)
12361556Srgrimes */
12371556Srgrimesparsearith: {
12381556Srgrimes
12391556Srgrimes	if (++arinest == 1) {
12401556Srgrimes		prevsyntax = syntax;
12411556Srgrimes		syntax = ARISYNTAX;
12421556Srgrimes		USTPUTC(CTLARI, out);
12431556Srgrimes	} else {
12441556Srgrimes		/*
12451556Srgrimes		 * we collapse embedded arithmetic expansion to
12461556Srgrimes		 * parenthesis, which should be equivalent
12471556Srgrimes		 */
12481556Srgrimes		USTPUTC('(', out);
12491556Srgrimes	}
12501556Srgrimes	goto parsearith_return;
12511556Srgrimes}
12521556Srgrimes
12531556Srgrimes} /* end of readtoken */
12541556Srgrimes
12551556Srgrimes
12561556Srgrimes
12571556Srgrimes#ifdef mkinit
12581556SrgrimesRESET {
12591556Srgrimes	tokpushback = 0;
12601556Srgrimes	checkkwd = 0;
12611556Srgrimes}
12621556Srgrimes#endif
12631556Srgrimes
12641556Srgrimes/*
12651556Srgrimes * Returns true if the text contains nothing to expand (no dollar signs
12661556Srgrimes * or backquotes).
12671556Srgrimes */
12681556Srgrimes
12691556SrgrimesSTATIC int
12701556Srgrimesnoexpand(text)
12711556Srgrimes	char *text;
12721556Srgrimes	{
12731556Srgrimes	register char *p;
12741556Srgrimes	register char c;
12751556Srgrimes
12761556Srgrimes	p = text;
12771556Srgrimes	while ((c = *p++) != '\0') {
12781556Srgrimes		if (c == CTLESC)
12791556Srgrimes			p++;
12801556Srgrimes		else if (BASESYNTAX[c] == CCTL)
12811556Srgrimes			return 0;
12821556Srgrimes	}
12831556Srgrimes	return 1;
12841556Srgrimes}
12851556Srgrimes
12861556Srgrimes
12871556Srgrimes/*
12881556Srgrimes * Return true if the argument is a legal variable name (a letter or
12891556Srgrimes * underscore followed by zero or more letters, underscores, and digits).
12901556Srgrimes */
12911556Srgrimes
12921556Srgrimesint
12931556Srgrimesgoodname(name)
12941556Srgrimes	char *name;
12951556Srgrimes	{
12961556Srgrimes	register char *p;
12971556Srgrimes
12981556Srgrimes	p = name;
12991556Srgrimes	if (! is_name(*p))
13001556Srgrimes		return 0;
13011556Srgrimes	while (*++p) {
13021556Srgrimes		if (! is_in_name(*p))
13031556Srgrimes			return 0;
13041556Srgrimes	}
13051556Srgrimes	return 1;
13061556Srgrimes}
13071556Srgrimes
13081556Srgrimes
13091556Srgrimes/*
13101556Srgrimes * Called when an unexpected token is read during the parse.  The argument
13111556Srgrimes * is the token that is expected, or -1 if more than one type of token can
13121556Srgrimes * occur at this point.
13131556Srgrimes */
13141556Srgrimes
13151556SrgrimesSTATIC void
13161556Srgrimessynexpect(token) {
13171556Srgrimes	char msg[64];
13181556Srgrimes
13191556Srgrimes	if (token >= 0) {
13201556Srgrimes		fmtstr(msg, 64, "%s unexpected (expecting %s)",
13211556Srgrimes			tokname[lasttoken], tokname[token]);
13221556Srgrimes	} else {
13231556Srgrimes		fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
13241556Srgrimes	}
13251556Srgrimes	synerror(msg);
13261556Srgrimes}
13271556Srgrimes
13281556Srgrimes
13291556SrgrimesSTATIC void
13301556Srgrimessynerror(msg)
13311556Srgrimes	char *msg;
13321556Srgrimes	{
13331556Srgrimes	if (commandname)
13341556Srgrimes		outfmt(&errout, "%s: %d: ", commandname, startlinno);
13351556Srgrimes	outfmt(&errout, "Syntax error: %s\n", msg);
13361556Srgrimes	error((char *)NULL);
13371556Srgrimes}
13381556Srgrimes
13391556SrgrimesSTATIC void
13401556Srgrimessetprompt(which)
13411556Srgrimes	int which;
13421556Srgrimes	{
13431556Srgrimes	whichprompt = which;
13441556Srgrimes
13451556Srgrimes	if (!el)
13461556Srgrimes		out2str(getprompt(NULL));
13471556Srgrimes}
13481556Srgrimes
13491556Srgrimes/*
13501556Srgrimes * called by editline -- any expansions to the prompt
13511556Srgrimes *    should be added here.
13521556Srgrimes */
13531556Srgrimeschar *
13541556Srgrimesgetprompt(unused)
13551556Srgrimes	void *unused;
13561556Srgrimes	{
13571556Srgrimes	switch (whichprompt) {
13581556Srgrimes	case 0:
13591556Srgrimes		return "";
13601556Srgrimes	case 1:
13611556Srgrimes		return ps1val();
13621556Srgrimes	case 2:
13631556Srgrimes		return ps2val();
13641556Srgrimes	default:
13651556Srgrimes		return "<internal prompt error>";
13661556Srgrimes	}
13671556Srgrimes}
1368