parser.c revision 59436
192108Sphk/*-
292108Sphk * Copyright (c) 1991, 1993
392108Sphk *	The Regents of the University of California.  All rights reserved.
492108Sphk *
592108Sphk * This code is derived from software contributed to Berkeley by
692108Sphk * Kenneth Almquist.
792108Sphk *
892108Sphk * Redistribution and use in source and binary forms, with or without
992108Sphk * modification, are permitted provided that the following conditions
1092108Sphk * are met:
1192108Sphk * 1. Redistributions of source code must retain the above copyright
1292108Sphk *    notice, this list of conditions and the following disclaimer.
1392108Sphk * 2. Redistributions in binary form must reproduce the above copyright
1492108Sphk *    notice, this list of conditions and the following disclaimer in the
1592108Sphk *    documentation and/or other materials provided with the distribution.
1692108Sphk * 3. All advertising materials mentioning features or use of this software
1792108Sphk *    must display the following acknowledgement:
1892108Sphk *	This product includes software developed by the University of
1992108Sphk *	California, Berkeley and its contributors.
2092108Sphk * 4. Neither the name of the University nor the names of its contributors
2192108Sphk *    may be used to endorse or promote products derived from this software
2292108Sphk *    without specific prior written permission.
2392108Sphk *
2492108Sphk * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2592108Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2692108Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2792108Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2892108Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2992108Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3092108Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3192108Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3292108Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3392108Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3492108Sphk * SUCH DAMAGE.
3592108Sphk */
3692108Sphk
3792108Sphk#ifndef lint
3895276Sphk#if 0
3995276Sphkstatic char sccsid[] = "@(#)parser.c	8.7 (Berkeley) 5/16/95";
4095276Sphk#endif
4192108Sphkstatic const char rcsid[] =
4292108Sphk  "$FreeBSD: head/bin/sh/parser.c 59436 2000-04-20 09:49:16Z cracauer $";
4392108Sphk#endif /* not lint */
4492108Sphk
4595323Sphk#include <stdlib.h>
4695323Sphk
4792108Sphk#include "shell.h"
4895362Sphk#include "parser.h"
4992108Sphk#include "nodes.h"
5092108Sphk#include "expand.h"	/* defines rmescapes() */
5192108Sphk#include "redir.h"	/* defines copyfd() */
5292108Sphk#include "syntax.h"
5392514Sphk#include "options.h"
5492108Sphk#include "input.h"
5592108Sphk#include "output.h"
5693248Sphk#include "var.h"
5792108Sphk#include "error.h"
5892108Sphk#include "memalloc.h"
5992108Sphk#include "mystring.h"
6092108Sphk#include "alias.h"
6192108Sphk#include "show.h"
6292108Sphk#include "eval.h"
6392108Sphk#ifndef NO_HISTORY
64105092Sphk#include "myhistedit.h"
6592108Sphk#endif
66105092Sphk
6793248Sphk/*
6893250Sphk * Shell command parser.
6992108Sphk */
7092108Sphk
7192108Sphk#define EOFMARKLEN 79
7292108Sphk
7392108Sphk/* values returned by readtoken */
7493250Sphk#include "token.h"
7592108Sphk
7692108Sphk
7792108Sphk
7892108Sphkstruct heredoc {
7992108Sphk	struct heredoc *next;	/* next here document in list */
8092108Sphk	union node *here;		/* redirection node */
8192108Sphk	char *eofmark;		/* string indicating end of input */
8293248Sphk	int striptabs;		/* if set, strip leading tabs */
8393248Sphk};
8493248Sphk
8593248Sphk
8692108Sphk
8792108Sphkstruct heredoc *heredoclist;	/* list of here documents to read */
8893248Sphkint parsebackquote;		/* nonzero if we are inside backquotes */
8992108Sphkint doprompt;			/* if set, prompt the user */
9092108Sphkint needprompt;			/* true if interactive and at start of line */
9192108Sphkint lasttoken;			/* last token read */
9293776SphkMKINIT int tokpushback;		/* last token pushed back */
9393776Sphkchar *wordtext;			/* text of last word returned by readtoken */
9498066SphkMKINIT int checkkwd;            /* 1 == check for kwds, 2 == also eat newlines */
9593776Sphkstruct nodelist *backquotelist;
9693248Sphkunion node *redirnode;
9792108Sphkstruct heredoc *heredoc;
9892108Sphkint quoteflag;			/* set if (part of) last token was quoted */
9995310Sphkint startlinno;			/* line # where last token started */
10092108Sphk
10192108Sphk/* XXX When 'noaliases' is set to one, no alias expansion takes place. */
10298066Sphkstatic int noaliases = 0;
10393776Sphk
10492108Sphk#define GDB_HACK 1 /* avoid local declarations which gdb can't handle */
10593248Sphk#ifdef GDB_HACK
10692108Sphkstatic const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'};
10792108Sphkstatic const char types[] = "}-+?=";
10895310Sphk#endif
10992108Sphk
11093248Sphk
11192108SphkSTATIC union node *list __P((int));
11292108SphkSTATIC union node *andor __P((void));
11392108SphkSTATIC union node *pipeline __P((void));
11492108SphkSTATIC union node *command __P((void));
11592108SphkSTATIC union node *simplecmd __P((union node **, union node *));
11692108SphkSTATIC union node *makename __P((void));
11792108SphkSTATIC void parsefname __P((void));
11892108SphkSTATIC void parseheredoc __P((void));
11993776SphkSTATIC int peektoken __P((void));
12093776SphkSTATIC int readtoken __P((void));
12192108SphkSTATIC int xxreadtoken __P((void));
12292108SphkSTATIC int readtoken1 __P((int, char const *, char *, int));
12392108SphkSTATIC int noexpand __P((char *));
12492108SphkSTATIC void synexpect __P((int));
12592108SphkSTATIC void synerror __P((char *));
12692108SphkSTATIC void setprompt __P((int));
12792108Sphk
12892108Sphk
12992108Sphk/*
13092108Sphk * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
13192108Sphk * valid parse tree indicating a blank line.)
13292108Sphk */
13392108Sphk
13492108Sphkunion node *
13592108Sphkparsecmd(interact)
13692108Sphk	int interact;
13792108Sphk{
13892108Sphk	int t;
13992108Sphk
14092108Sphk	doprompt = interact;
14192108Sphk	if (doprompt)
14292108Sphk		setprompt(1);
14392108Sphk	else
14492108Sphk		setprompt(0);
14595310Sphk	needprompt = 0;
14692108Sphk	t = readtoken();
14792108Sphk	if (t == TEOF)
14892108Sphk		return NEOF;
14992108Sphk	if (t == TNL)
15092108Sphk		return NULL;
15192108Sphk	tokpushback++;
15292108Sphk	return list(1);
15392108Sphk}
15492108Sphk
15592108Sphk
15692108SphkSTATIC union node *
15792108Sphklist(nlflag)
15892108Sphk	int nlflag;
15992108Sphk{
16092108Sphk	union node *n1, *n2, *n3;
16195310Sphk	int tok;
16292108Sphk
16392108Sphk	checkkwd = 2;
16492108Sphk	if (nlflag == 0 && tokendlist[peektoken()])
16592108Sphk		return NULL;
16692108Sphk	n1 = NULL;
16792108Sphk	for (;;) {
16892108Sphk		n2 = andor();
16992108Sphk		tok = readtoken();
17092108Sphk		if (tok == TBACKGND) {
17193778Sphk			if (n2->type == NCMD || n2->type == NPIPE) {
17292108Sphk				n2->ncmd.backgnd = 1;
17392108Sphk			} else if (n2->type == NREDIR) {
174105092Sphk				n2->type = NBACKGND;
175105092Sphk			} else {
176105092Sphk				n3 = (union node *)stalloc(sizeof (struct nredir));
177105092Sphk				n3->type = NBACKGND;
178105092Sphk				n3->nredir.n = n2;
179105092Sphk				n3->nredir.redirect = NULL;
180105092Sphk				n2 = n3;
181105092Sphk			}
182105092Sphk		}
183105092Sphk		if (n1 == NULL) {
184105092Sphk			n1 = n2;
185105092Sphk		}
186105092Sphk		else {
187105092Sphk			n3 = (union node *)stalloc(sizeof (struct nbinary));
188105092Sphk			n3->type = NSEMI;
189105092Sphk			n3->nbinary.ch1 = n1;
190105092Sphk			n3->nbinary.ch2 = n2;
191105092Sphk			n1 = n3;
192105092Sphk		}
193105092Sphk		switch (tok) {
19492108Sphk		case TBACKGND:
19592108Sphk		case TSEMI:
19692108Sphk			tok = readtoken();
19792108Sphk			/* fall through */
19892108Sphk		case TNL:
19992108Sphk			if (tok == TNL) {
20092108Sphk				parseheredoc();
20193090Sphk				if (nlflag)
20292108Sphk					return n1;
203104056Sphk			} else {
204104056Sphk				tokpushback++;
20592108Sphk			}
206104056Sphk			checkkwd = 2;
20798066Sphk			if (tokendlist[peektoken()])
20892108Sphk				return n1;
20992108Sphk			break;
21092108Sphk		case TEOF:
21192108Sphk			if (heredoclist)
21293248Sphk				parseheredoc();
21392108Sphk			else
21492108Sphk				pungetc();		/* push back EOF on input */
21592108Sphk			return n1;
21692108Sphk		default:
21798066Sphk			if (nlflag)
21892108Sphk				synexpect(-1);
21994284Sphk			tokpushback++;
22094284Sphk			return n1;
22198066Sphk		}
22298066Sphk	}
22398066Sphk}
22493248Sphk
22592108Sphk
22693248Sphk
22792108SphkSTATIC union node *
22895310Sphkandor() {
22992108Sphk	union node *n1, *n2, *n3;
23092108Sphk	int t;
23192108Sphk
23292108Sphk	n1 = pipeline();
233105092Sphk	for (;;) {
234105092Sphk		if ((t = readtoken()) == TAND) {
235105092Sphk			t = NAND;
23692108Sphk		} else if (t == TOR) {
237105092Sphk			t = NOR;
23892108Sphk		} else {
23992108Sphk			tokpushback++;
24092108Sphk			return n1;
241104195Sphk		}
24294283Sphk		n2 = pipeline();
24392108Sphk		n3 = (union node *)stalloc(sizeof (struct nbinary));
24494283Sphk		n3->type = t;
24592108Sphk		n3->nbinary.ch1 = n1;
24692108Sphk		n3->nbinary.ch2 = n2;
247104194Sphk		n1 = n3;
24892108Sphk	}
24992108Sphk}
25092108Sphk
251104602Sphk
252104602Sphk
253104602SphkSTATIC union node *
254104602Sphkpipeline() {
255104602Sphk	union node *n1, *pipenode, *notnode;
25692403Sphk	struct nodelist *lp, *prev;
25792403Sphk	int negate = 0;
25892403Sphk
25992403Sphk	TRACE(("pipeline: entered\n"));
26092403Sphk	while (readtoken() == TNOT) {
261104602Sphk		TRACE(("pipeline: TNOT recognized\n"));
262104602Sphk		negate = !negate;
26392403Sphk	}
26492108Sphk	tokpushback++;
26592108Sphk	n1 = command();
26692108Sphk	if (readtoken() == TPIPE) {
26795038Sphk		pipenode = (union node *)stalloc(sizeof (struct npipe));
26895038Sphk		pipenode->type = NPIPE;
26995038Sphk		pipenode->npipe.backgnd = 0;
27095038Sphk		lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
27195038Sphk		pipenode->npipe.cmdlist = lp;
27292108Sphk		lp->n = n1;
27392108Sphk		do {
27492108Sphk			prev = lp;
27592108Sphk			lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
27692108Sphk			lp->n = command();
27792108Sphk			prev->next = lp;
27892108Sphk		} while (readtoken() == TPIPE);
27992108Sphk		lp->next = NULL;
28095310Sphk		n1 = pipenode;
28195310Sphk	}
28292108Sphk	tokpushback++;
28392108Sphk	if (negate) {
28492108Sphk		notnode = (union node *)stalloc(sizeof(struct nnot));
28592108Sphk		notnode->type = NNOT;
28692108Sphk		notnode->nnot.com = n1;
28792108Sphk		n1 = notnode;
28895310Sphk	}
28995310Sphk	return n1;
29092108Sphk}
29192108Sphk
29292108Sphk
29392108Sphk
29492108SphkSTATIC union node *
29596987Sphkcommand() {
29696987Sphk	union node *n1, *n2;
29796987Sphk	union node *ap, **app;
29896987Sphk	union node *cp, **cpp;
29996987Sphk	union node *redir, **rpp;
30096987Sphk	int t;
30196987Sphk
30296987Sphk	checkkwd = 2;
30396987Sphk	redir = NULL;
30496987Sphk	n1 = NULL;
30596987Sphk	rpp = &redir;
30696987Sphk
30796987Sphk	/* Check for redirection which may precede command */
30896987Sphk	while (readtoken() == TREDIR) {
30996987Sphk		*rpp = n2 = redirnode;
31096987Sphk		rpp = &n2->nfile.next;
31196987Sphk		parsefname();
31296987Sphk	}
313105061Sphk	tokpushback++;
314105124Sjake
315105061Sphk	switch (readtoken()) {
31693248Sphk	case TIF:
31792108Sphk		n1 = (union node *)stalloc(sizeof (struct nif));
31892108Sphk		n1->type = NIF;
31992108Sphk		n1->nif.test = list(0);
32092108Sphk		if (readtoken() != TTHEN)
32193248Sphk			synexpect(TTHEN);
32292108Sphk		n1->nif.ifpart = list(0);
32392108Sphk		n2 = n1;
324105061Sphk		while (readtoken() == TELIF) {
32592108Sphk			n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
32695310Sphk			n2 = n2->nif.elsepart;
32792108Sphk			n2->type = NIF;
328105068Sphk			n2->nif.test = list(0);
329105068Sphk			if (readtoken() != TTHEN)
330105068Sphk				synexpect(TTHEN);
331105092Sphk			n2->nif.ifpart = list(0);
332105068Sphk		}
333105068Sphk		if (lasttoken == TELSE)
334105068Sphk			n2->nif.elsepart = list(0);
335105068Sphk		else {
336105068Sphk			n2->nif.elsepart = NULL;
33795323Sphk			tokpushback++;
338105092Sphk		}
339105092Sphk		if (readtoken() != TFI)
340105092Sphk			synexpect(TFI);
341105092Sphk		checkkwd = 1;
342105092Sphk		break;
343105092Sphk	case TWHILE:
344105092Sphk	case TUNTIL: {
345105092Sphk		int got;
346105092Sphk		n1 = (union node *)stalloc(sizeof (struct nbinary));
347105092Sphk		n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
348105092Sphk		n1->nbinary.ch1 = list(0);
349105092Sphk		if ((got=readtoken()) != TDO) {
350105092SphkTRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
351105092Sphk			synexpect(TDO);
352105092Sphk		}
353105092Sphk		n1->nbinary.ch2 = list(0);
354105092Sphk		if (readtoken() != TDONE)
355105092Sphk			synexpect(TDONE);
356105092Sphk		checkkwd = 1;
357105092Sphk		break;
358105092Sphk	}
359105092Sphk	case TFOR:
360105092Sphk		if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
361105092Sphk			synerror("Bad for loop variable");
362103279Sphk		n1 = (union node *)stalloc(sizeof (struct nfor));
363104060Sphk		n1->type = NFOR;
364103279Sphk		n1->nfor.var = wordtext;
365104060Sphk		if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
366103279Sphk			app = &ap;
367103279Sphk			while (readtoken() == TWORD) {
368104193Sphk				n2 = (union node *)stalloc(sizeof (struct narg));
369103279Sphk				n2->type = NARG;
370103279Sphk				n2->narg.text = wordtext;
371103279Sphk				n2->narg.backquote = backquotelist;
37295276Sphk				*app = n2;
373				app = &n2->narg.next;
374			}
375			*app = NULL;
376			n1->nfor.args = ap;
377			if (lasttoken != TNL && lasttoken != TSEMI)
378				synexpect(-1);
379		} else {
380#ifndef GDB_HACK
381			static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
382								   '@', '=', '\0'};
383#endif
384			n2 = (union node *)stalloc(sizeof (struct narg));
385			n2->type = NARG;
386			n2->narg.text = (char *)argvars;
387			n2->narg.backquote = NULL;
388			n2->narg.next = NULL;
389			n1->nfor.args = n2;
390			/*
391			 * Newline or semicolon here is optional (but note
392			 * that the original Bourne shell only allowed NL).
393			 */
394			if (lasttoken != TNL && lasttoken != TSEMI)
395				tokpushback++;
396		}
397		checkkwd = 2;
398		if ((t = readtoken()) == TDO)
399			t = TDONE;
400		else if (t == TBEGIN)
401			t = TEND;
402		else
403			synexpect(-1);
404		n1->nfor.body = list(0);
405		if (readtoken() != t)
406			synexpect(t);
407		checkkwd = 1;
408		break;
409	case TCASE:
410		n1 = (union node *)stalloc(sizeof (struct ncase));
411		n1->type = NCASE;
412		if (readtoken() != TWORD)
413			synexpect(TWORD);
414		n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
415		n2->type = NARG;
416		n2->narg.text = wordtext;
417		n2->narg.backquote = backquotelist;
418		n2->narg.next = NULL;
419		while (readtoken() == TNL);
420		if (lasttoken != TWORD || ! equal(wordtext, "in"))
421			synerror("expecting \"in\"");
422		cpp = &n1->ncase.cases;
423		noaliases = 1;	/* turn off alias expansion */
424		checkkwd = 2, readtoken();
425		do {
426			*cpp = cp = (union node *)stalloc(sizeof (struct nclist));
427			cp->type = NCLIST;
428			app = &cp->nclist.pattern;
429			for (;;) {
430				*app = ap = (union node *)stalloc(sizeof (struct narg));
431				ap->type = NARG;
432				ap->narg.text = wordtext;
433				ap->narg.backquote = backquotelist;
434				if (checkkwd = 2, readtoken() != TPIPE)
435					break;
436				app = &ap->narg.next;
437				readtoken();
438			}
439			ap->narg.next = NULL;
440			if (lasttoken != TRP)
441				noaliases = 0, synexpect(TRP);
442			cp->nclist.body = list(0);
443
444			checkkwd = 2;
445			if ((t = readtoken()) != TESAC) {
446				if (t != TENDCASE)
447					noaliases = 0, synexpect(TENDCASE);
448				else
449					checkkwd = 2, readtoken();
450			}
451			cpp = &cp->nclist.next;
452		} while(lasttoken != TESAC);
453		noaliases = 0;	/* reset alias expansion */
454		*cpp = NULL;
455		checkkwd = 1;
456		break;
457	case TLP:
458		n1 = (union node *)stalloc(sizeof (struct nredir));
459		n1->type = NSUBSHELL;
460		n1->nredir.n = list(0);
461		n1->nredir.redirect = NULL;
462		if (readtoken() != TRP)
463			synexpect(TRP);
464		checkkwd = 1;
465		break;
466	case TBEGIN:
467		n1 = list(0);
468		if (readtoken() != TEND)
469			synexpect(TEND);
470		checkkwd = 1;
471		break;
472	/* Handle an empty command like other simple commands.  */
473	case TSEMI:
474		/*
475		 * An empty command before a ; doesn't make much sense, and
476		 * should certainly be disallowed in the case of `if ;'.
477		 */
478		if (!redir)
479			synexpect(-1);
480	case TAND:
481	case TOR:
482	case TNL:
483	case TEOF:
484	case TWORD:
485	case TRP:
486		tokpushback++;
487		return simplecmd(rpp, redir);
488	default:
489		synexpect(-1);
490	}
491
492	/* Now check for redirection which may follow command */
493	while (readtoken() == TREDIR) {
494		*rpp = n2 = redirnode;
495		rpp = &n2->nfile.next;
496		parsefname();
497	}
498	tokpushback++;
499	*rpp = NULL;
500	if (redir) {
501		if (n1->type != NSUBSHELL) {
502			n2 = (union node *)stalloc(sizeof (struct nredir));
503			n2->type = NREDIR;
504			n2->nredir.n = n1;
505			n1 = n2;
506		}
507		n1->nredir.redirect = redir;
508	}
509	return n1;
510}
511
512
513STATIC union node *
514simplecmd(rpp, redir)
515	union node **rpp, *redir;
516	{
517	union node *args, **app;
518	union node **orig_rpp = rpp;
519	union node *n = NULL;
520
521	/* If we don't have any redirections already, then we must reset */
522	/* rpp to be the address of the local redir variable.  */
523	if (redir == 0)
524		rpp = &redir;
525
526	args = NULL;
527	app = &args;
528	/*
529	 * We save the incoming value, because we need this for shell
530	 * functions.  There can not be a redirect or an argument between
531	 * the function name and the open parenthesis.
532	 */
533	orig_rpp = rpp;
534
535	for (;;) {
536		if (readtoken() == TWORD) {
537			n = (union node *)stalloc(sizeof (struct narg));
538			n->type = NARG;
539			n->narg.text = wordtext;
540			n->narg.backquote = backquotelist;
541			*app = n;
542			app = &n->narg.next;
543		} else if (lasttoken == TREDIR) {
544			*rpp = n = redirnode;
545			rpp = &n->nfile.next;
546			parsefname();	/* read name of redirection file */
547		} else if (lasttoken == TLP && app == &args->narg.next
548					    && rpp == orig_rpp) {
549			/* We have a function */
550			if (readtoken() != TRP)
551				synexpect(TRP);
552#ifdef notdef
553			if (! goodname(n->narg.text))
554				synerror("Bad function name");
555#endif
556			n->type = NDEFUN;
557			n->narg.next = command();
558			return n;
559		} else {
560			tokpushback++;
561			break;
562		}
563	}
564	*app = NULL;
565	*rpp = NULL;
566	n = (union node *)stalloc(sizeof (struct ncmd));
567	n->type = NCMD;
568	n->ncmd.backgnd = 0;
569	n->ncmd.args = args;
570	n->ncmd.redirect = redir;
571	return n;
572}
573
574STATIC union node *
575makename() {
576	union node *n;
577
578	n = (union node *)stalloc(sizeof (struct narg));
579	n->type = NARG;
580	n->narg.next = NULL;
581	n->narg.text = wordtext;
582	n->narg.backquote = backquotelist;
583	return n;
584}
585
586void fixredir(n, text, err)
587	union node *n;
588	const char *text;
589	int err;
590	{
591	TRACE(("Fix redir %s %d\n", text, err));
592	if (!err)
593		n->ndup.vname = NULL;
594
595	if (is_digit(text[0]) && text[1] == '\0')
596		n->ndup.dupfd = digit_val(text[0]);
597	else if (text[0] == '-' && text[1] == '\0')
598		n->ndup.dupfd = -1;
599	else {
600
601		if (err)
602			synerror("Bad fd number");
603		else
604			n->ndup.vname = makename();
605	}
606}
607
608
609STATIC void
610parsefname() {
611	union node *n = redirnode;
612
613	if (readtoken() != TWORD)
614		synexpect(-1);
615	if (n->type == NHERE) {
616		struct heredoc *here = heredoc;
617		struct heredoc *p;
618		int i;
619
620		if (quoteflag == 0)
621			n->type = NXHERE;
622		TRACE(("Here document %d\n", n->type));
623		if (here->striptabs) {
624			while (*wordtext == '\t')
625				wordtext++;
626		}
627		if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
628			synerror("Illegal eof marker for << redirection");
629		rmescapes(wordtext);
630		here->eofmark = wordtext;
631		here->next = NULL;
632		if (heredoclist == NULL)
633			heredoclist = here;
634		else {
635			for (p = heredoclist ; p->next ; p = p->next);
636			p->next = here;
637		}
638	} else if (n->type == NTOFD || n->type == NFROMFD) {
639		fixredir(n, wordtext, 0);
640	} else {
641		n->nfile.fname = makename();
642	}
643}
644
645
646/*
647 * Input any here documents.
648 */
649
650STATIC void
651parseheredoc() {
652	struct heredoc *here;
653	union node *n;
654
655	while (heredoclist) {
656		here = heredoclist;
657		heredoclist = here->next;
658		if (needprompt) {
659			setprompt(2);
660			needprompt = 0;
661		}
662		readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
663				here->eofmark, here->striptabs);
664		n = (union node *)stalloc(sizeof (struct narg));
665		n->narg.type = NARG;
666		n->narg.next = NULL;
667		n->narg.text = wordtext;
668		n->narg.backquote = backquotelist;
669		here->here->nhere.doc = n;
670	}
671}
672
673STATIC int
674peektoken() {
675	int t;
676
677	t = readtoken();
678	tokpushback++;
679	return (t);
680}
681
682STATIC int
683readtoken() {
684	int t;
685	int savecheckkwd = checkkwd;
686	struct alias *ap;
687#ifdef DEBUG
688	int alreadyseen = tokpushback;
689#endif
690
691	top:
692	t = xxreadtoken();
693
694	if (checkkwd) {
695		/*
696		 * eat newlines
697		 */
698		if (checkkwd == 2) {
699			checkkwd = 0;
700			while (t == TNL) {
701				parseheredoc();
702				t = xxreadtoken();
703			}
704		} else
705			checkkwd = 0;
706		/*
707		 * check for keywords and aliases
708		 */
709		if (t == TWORD && !quoteflag)
710		{
711			char * const *pp;
712
713			for (pp = (char **)parsekwd; *pp; pp++) {
714				if (**pp == *wordtext && equal(*pp, wordtext))
715				{
716					lasttoken = t = pp - parsekwd + KWDOFFSET;
717					TRACE(("keyword %s recognized\n", tokname[t]));
718					goto out;
719				}
720			}
721			if (noaliases == 0 &&
722			    (ap = lookupalias(wordtext, 1)) != NULL) {
723				pushstring(ap->val, strlen(ap->val), ap);
724				checkkwd = savecheckkwd;
725				goto top;
726			}
727		}
728out:
729		checkkwd = 0;
730	}
731#ifdef DEBUG
732	if (!alreadyseen)
733	    TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
734	else
735	    TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
736#endif
737	return (t);
738}
739
740
741/*
742 * Read the next input token.
743 * If the token is a word, we set backquotelist to the list of cmds in
744 *	backquotes.  We set quoteflag to true if any part of the word was
745 *	quoted.
746 * If the token is TREDIR, then we set redirnode to a structure containing
747 *	the redirection.
748 * In all cases, the variable startlinno is set to the number of the line
749 *	on which the token starts.
750 *
751 * [Change comment:  here documents and internal procedures]
752 * [Readtoken shouldn't have any arguments.  Perhaps we should make the
753 *  word parsing code into a separate routine.  In this case, readtoken
754 *  doesn't need to have any internal procedures, but parseword does.
755 *  We could also make parseoperator in essence the main routine, and
756 *  have parseword (readtoken1?) handle both words and redirection.]
757 */
758
759#define RETURN(token)	return lasttoken = token
760
761STATIC int
762xxreadtoken() {
763	int c;
764
765	if (tokpushback) {
766		tokpushback = 0;
767		return lasttoken;
768	}
769	if (needprompt) {
770		setprompt(2);
771		needprompt = 0;
772	}
773	startlinno = plinno;
774	for (;;) {	/* until token or start of word found */
775		c = pgetc_macro();
776		if (c == ' ' || c == '\t')
777			continue;		/* quick check for white space first */
778		switch (c) {
779		case ' ': case '\t':
780			continue;
781		case '#':
782			while ((c = pgetc()) != '\n' && c != PEOF);
783			pungetc();
784			continue;
785		case '\\':
786			if (pgetc() == '\n') {
787				startlinno = ++plinno;
788				if (doprompt)
789					setprompt(2);
790				else
791					setprompt(0);
792				continue;
793			}
794			pungetc();
795			goto breakloop;
796		case '\n':
797			plinno++;
798			needprompt = doprompt;
799			RETURN(TNL);
800		case PEOF:
801			RETURN(TEOF);
802		case '&':
803			if (pgetc() == '&')
804				RETURN(TAND);
805			pungetc();
806			RETURN(TBACKGND);
807		case '|':
808			if (pgetc() == '|')
809				RETURN(TOR);
810			pungetc();
811			RETURN(TPIPE);
812		case ';':
813			if (pgetc() == ';')
814				RETURN(TENDCASE);
815			pungetc();
816			RETURN(TSEMI);
817		case '(':
818			RETURN(TLP);
819		case ')':
820			RETURN(TRP);
821		default:
822			goto breakloop;
823		}
824	}
825breakloop:
826	return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
827#undef RETURN
828}
829
830
831
832/*
833 * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
834 * is not NULL, read a here document.  In the latter case, eofmark is the
835 * word which marks the end of the document and striptabs is true if
836 * leading tabs should be stripped from the document.  The argument firstc
837 * is the first character of the input token or document.
838 *
839 * Because C does not have internal subroutines, I have simulated them
840 * using goto's to implement the subroutine linkage.  The following macros
841 * will run code that appears at the end of readtoken1.
842 */
843
844#define CHECKEND()	{goto checkend; checkend_return:;}
845#define PARSEREDIR()	{goto parseredir; parseredir_return:;}
846#define PARSESUB()	{goto parsesub; parsesub_return:;}
847#define PARSEBACKQOLD()	{oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
848#define PARSEBACKQNEW()	{oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
849#define	PARSEARITH()	{goto parsearith; parsearith_return:;}
850
851STATIC int
852readtoken1(firstc, syntax, eofmark, striptabs)
853	int firstc;
854	char const *syntax;
855	char *eofmark;
856	int striptabs;
857	{
858	int c = firstc;
859	char *out;
860	int len;
861	char line[EOFMARKLEN + 1];
862	struct nodelist *bqlist;
863	int quotef;
864	int dblquote;
865	int varnest;	/* levels of variables expansion */
866	int arinest;	/* levels of arithmetic expansion */
867	int parenlevel;	/* levels of parens in arithmetic */
868	int oldstyle;
869	char const *prevsyntax;	/* syntax before arithmetic */
870	int synentry;
871#if __GNUC__
872	/* Avoid longjmp clobbering */
873	(void) &out;
874	(void) &quotef;
875	(void) &dblquote;
876	(void) &varnest;
877	(void) &arinest;
878	(void) &parenlevel;
879	(void) &oldstyle;
880	(void) &prevsyntax;
881	(void) &syntax;
882	(void) &synentry;
883#endif
884
885	startlinno = plinno;
886	dblquote = 0;
887	if (syntax == DQSYNTAX)
888		dblquote = 1;
889	quotef = 0;
890	bqlist = NULL;
891	varnest = 0;
892	arinest = 0;
893	parenlevel = 0;
894
895	STARTSTACKSTR(out);
896	loop: {	/* for each line, until end of word */
897#if ATTY
898		if (c == '\034' && doprompt
899		 && attyset() && ! equal(termval(), "emacs")) {
900			attyline();
901			if (syntax == BASESYNTAX)
902				return readtoken();
903			c = pgetc();
904			goto loop;
905		}
906#endif
907		CHECKEND();	/* set c to PEOF if at end of here document */
908		for (;;) {	/* until end of line or end of word */
909			CHECKSTRSPACE(3, out);	/* permit 3 calls to USTPUTC */
910
911			if (c < 0 && c != PEOF)
912				synentry = CWORD;
913			else
914				synentry = syntax[c];
915
916			switch(synentry) {
917			case CNL:	/* '\n' */
918				if (syntax == BASESYNTAX)
919					goto endword;	/* exit outer loop */
920				USTPUTC(c, out);
921				plinno++;
922				if (doprompt)
923					setprompt(2);
924				else
925					setprompt(0);
926				c = pgetc();
927				goto loop;		/* continue outer loop */
928			case CWORD:
929				USTPUTC(c, out);
930				break;
931			case CCTL:
932				if (eofmark == NULL || dblquote)
933					USTPUTC(CTLESC, out);
934				USTPUTC(c, out);
935				break;
936			case CBACK:	/* backslash */
937				c = pgetc();
938				if (c == PEOF) {
939					USTPUTC('\\', out);
940					pungetc();
941				} else if (c == '\n') {
942					if (doprompt)
943						setprompt(2);
944					else
945						setprompt(0);
946				} else {
947					if (dblquote && c != '\\' &&
948					    c != '`' && c != '$' &&
949					    (c != '"' || eofmark != NULL))
950						USTPUTC('\\', out);
951					if (c >= 0 && SQSYNTAX[c] == CCTL)
952						USTPUTC(CTLESC, out);
953					else if (eofmark == NULL)
954						USTPUTC(CTLQUOTEMARK, out);
955					USTPUTC(c, out);
956					quotef++;
957				}
958				break;
959			case CSQUOTE:
960				if (eofmark == NULL)
961					USTPUTC(CTLQUOTEMARK, out);
962				syntax = SQSYNTAX;
963				break;
964			case CDQUOTE:
965				if (eofmark == NULL)
966					USTPUTC(CTLQUOTEMARK, out);
967				syntax = DQSYNTAX;
968				dblquote = 1;
969				break;
970			case CENDQUOTE:
971				if (eofmark != NULL && arinest == 0 &&
972				    varnest == 0) {
973					USTPUTC(c, out);
974				} else {
975					if (arinest) {
976						syntax = ARISYNTAX;
977						dblquote = 0;
978					} else if (eofmark == NULL) {
979						syntax = BASESYNTAX;
980						dblquote = 0;
981					}
982					quotef++;
983				}
984				break;
985			case CVAR:	/* '$' */
986				PARSESUB();		/* parse substitution */
987				break;
988			case CENDVAR:	/* '}' */
989				if (varnest > 0) {
990					varnest--;
991					USTPUTC(CTLENDVAR, out);
992				} else {
993					USTPUTC(c, out);
994				}
995				break;
996			case CLP:	/* '(' in arithmetic */
997				parenlevel++;
998				USTPUTC(c, out);
999				break;
1000			case CRP:	/* ')' in arithmetic */
1001				if (parenlevel > 0) {
1002					USTPUTC(c, out);
1003					--parenlevel;
1004				} else {
1005					if (pgetc() == ')') {
1006						if (--arinest == 0) {
1007							USTPUTC(CTLENDARI, out);
1008							syntax = prevsyntax;
1009							if (syntax == DQSYNTAX)
1010								dblquote = 1;
1011							else
1012								dblquote = 0;
1013						} else
1014							USTPUTC(')', out);
1015					} else {
1016						/*
1017						 * unbalanced parens
1018						 *  (don't 2nd guess - no error)
1019						 */
1020						pungetc();
1021						USTPUTC(')', out);
1022					}
1023				}
1024				break;
1025			case CBQUOTE:	/* '`' */
1026				PARSEBACKQOLD();
1027				break;
1028			case CEOF:
1029				goto endword;		/* exit outer loop */
1030			default:
1031				if (varnest == 0)
1032					goto endword;	/* exit outer loop */
1033				USTPUTC(c, out);
1034			}
1035			c = pgetc_macro();
1036		}
1037	}
1038endword:
1039	if (syntax == ARISYNTAX)
1040		synerror("Missing '))'");
1041	if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
1042		synerror("Unterminated quoted string");
1043	if (varnest != 0) {
1044		startlinno = plinno;
1045		synerror("Missing '}'");
1046	}
1047	USTPUTC('\0', out);
1048	len = out - stackblock();
1049	out = stackblock();
1050	if (eofmark == NULL) {
1051		if ((c == '>' || c == '<')
1052		 && quotef == 0
1053		 && len <= 2
1054		 && (*out == '\0' || is_digit(*out))) {
1055			PARSEREDIR();
1056			return lasttoken = TREDIR;
1057		} else {
1058			pungetc();
1059		}
1060	}
1061	quoteflag = quotef;
1062	backquotelist = bqlist;
1063	grabstackblock(len);
1064	wordtext = out;
1065	return lasttoken = TWORD;
1066/* end of readtoken routine */
1067
1068
1069
1070/*
1071 * Check to see whether we are at the end of the here document.  When this
1072 * is called, c is set to the first character of the next input line.  If
1073 * we are at the end of the here document, this routine sets the c to PEOF.
1074 */
1075
1076checkend: {
1077	if (eofmark) {
1078		if (striptabs) {
1079			while (c == '\t')
1080				c = pgetc();
1081		}
1082		if (c == *eofmark) {
1083			if (pfgets(line, sizeof line) != NULL) {
1084				char *p, *q;
1085
1086				p = line;
1087				for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
1088				if (*p == '\n' && *q == '\0') {
1089					c = PEOF;
1090					plinno++;
1091					needprompt = doprompt;
1092				} else {
1093					pushstring(line, strlen(line), NULL);
1094				}
1095			}
1096		}
1097	}
1098	goto checkend_return;
1099}
1100
1101
1102/*
1103 * Parse a redirection operator.  The variable "out" points to a string
1104 * specifying the fd to be redirected.  The variable "c" contains the
1105 * first character of the redirection operator.
1106 */
1107
1108parseredir: {
1109	char fd = *out;
1110	union node *np;
1111
1112	np = (union node *)stalloc(sizeof (struct nfile));
1113	if (c == '>') {
1114		np->nfile.fd = 1;
1115		c = pgetc();
1116		if (c == '>')
1117			np->type = NAPPEND;
1118		else if (c == '&')
1119			np->type = NTOFD;
1120		else {
1121			np->type = NTO;
1122			pungetc();
1123		}
1124	} else {	/* c == '<' */
1125		np->nfile.fd = 0;
1126		c = pgetc();
1127		if (c == '<') {
1128			if (sizeof (struct nfile) != sizeof (struct nhere)) {
1129				np = (union node *)stalloc(sizeof (struct nhere));
1130				np->nfile.fd = 0;
1131			}
1132			np->type = NHERE;
1133			heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
1134			heredoc->here = np;
1135			if ((c = pgetc()) == '-') {
1136				heredoc->striptabs = 1;
1137			} else {
1138				heredoc->striptabs = 0;
1139				pungetc();
1140			}
1141		} else if (c == '&')
1142			np->type = NFROMFD;
1143		else {
1144			np->type = NFROM;
1145			pungetc();
1146		}
1147	}
1148	if (fd != '\0')
1149		np->nfile.fd = digit_val(fd);
1150	redirnode = np;
1151	goto parseredir_return;
1152}
1153
1154
1155/*
1156 * Parse a substitution.  At this point, we have read the dollar sign
1157 * and nothing else.
1158 */
1159
1160parsesub: {
1161	int subtype;
1162	int typeloc;
1163	int flags;
1164	char *p;
1165#ifndef GDB_HACK
1166	static const char types[] = "}-+?=";
1167#endif
1168       int bracketed_name = 0; /* used to handle ${[0-9]*} variables */
1169
1170	c = pgetc();
1171	if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) {
1172		USTPUTC('$', out);
1173		pungetc();
1174	} else if (c == '(') {	/* $(command) or $((arith)) */
1175		if (pgetc() == '(') {
1176			PARSEARITH();
1177		} else {
1178			pungetc();
1179			PARSEBACKQNEW();
1180		}
1181	} else {
1182		USTPUTC(CTLVAR, out);
1183		typeloc = out - stackblock();
1184		USTPUTC(VSNORMAL, out);
1185		subtype = VSNORMAL;
1186		if (c == '{') {
1187			bracketed_name = 1;
1188			c = pgetc();
1189			if (c == '#') {
1190				if ((c = pgetc()) == '}')
1191					c = '#';
1192				else
1193					subtype = VSLENGTH;
1194			}
1195			else
1196				subtype = 0;
1197		}
1198		if (is_name(c)) {
1199			do {
1200				STPUTC(c, out);
1201				c = pgetc();
1202			} while (is_in_name(c));
1203		} else if (is_digit(c)) {
1204			if (bracketed_name) {
1205				do {
1206					STPUTC(c, out);
1207					c = pgetc();
1208				} while (is_digit(c));
1209			} else {
1210				STPUTC(c, out);
1211				c = pgetc();
1212			}
1213		} else {
1214			if (! is_special(c))
1215badsub:				synerror("Bad substitution");
1216			USTPUTC(c, out);
1217			c = pgetc();
1218		}
1219		STPUTC('=', out);
1220		flags = 0;
1221		if (subtype == 0) {
1222			switch (c) {
1223			case ':':
1224				flags = VSNUL;
1225				c = pgetc();
1226				/*FALLTHROUGH*/
1227			default:
1228				p = strchr(types, c);
1229				if (p == NULL)
1230					goto badsub;
1231				subtype = p - types + VSNORMAL;
1232				break;
1233			case '%':
1234			case '#':
1235				{
1236					int cc = c;
1237					subtype = c == '#' ? VSTRIMLEFT :
1238							     VSTRIMRIGHT;
1239					c = pgetc();
1240					if (c == cc)
1241						subtype++;
1242					else
1243						pungetc();
1244					break;
1245				}
1246			}
1247		} else {
1248			pungetc();
1249		}
1250		if (subtype != VSLENGTH && (dblquote || arinest))
1251			flags |= VSQUOTE;
1252		*(stackblock() + typeloc) = subtype | flags;
1253		if (subtype != VSNORMAL)
1254			varnest++;
1255	}
1256	goto parsesub_return;
1257}
1258
1259
1260/*
1261 * Called to parse command substitutions.  Newstyle is set if the command
1262 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
1263 * list of commands (passed by reference), and savelen is the number of
1264 * characters on the top of the stack which must be preserved.
1265 */
1266
1267parsebackq: {
1268	struct nodelist **nlpp;
1269	int savepbq;
1270	union node *n;
1271	char *volatile str;
1272	struct jmploc jmploc;
1273	struct jmploc *volatile savehandler;
1274	int savelen;
1275	int saveprompt;
1276#if __GNUC__
1277	/* Avoid longjmp clobbering */
1278	(void) &saveprompt;
1279#endif
1280
1281	savepbq = parsebackquote;
1282	if (setjmp(jmploc.loc)) {
1283		if (str)
1284			ckfree(str);
1285		parsebackquote = 0;
1286		handler = savehandler;
1287		longjmp(handler->loc, 1);
1288	}
1289	INTOFF;
1290	str = NULL;
1291	savelen = out - stackblock();
1292	if (savelen > 0) {
1293		str = ckmalloc(savelen);
1294		memcpy(str, stackblock(), savelen);
1295	}
1296	savehandler = handler;
1297	handler = &jmploc;
1298	INTON;
1299        if (oldstyle) {
1300                /* We must read until the closing backquote, giving special
1301                   treatment to some slashes, and then push the string and
1302                   reread it as input, interpreting it normally.  */
1303                char *out;
1304                int c;
1305                int savelen;
1306                char *str;
1307
1308
1309                STARTSTACKSTR(out);
1310		for (;;) {
1311			if (needprompt) {
1312				setprompt(2);
1313				needprompt = 0;
1314			}
1315			switch (c = pgetc()) {
1316			case '`':
1317				goto done;
1318
1319			case '\\':
1320                                if ((c = pgetc()) == '\n') {
1321					plinno++;
1322					if (doprompt)
1323						setprompt(2);
1324					else
1325						setprompt(0);
1326					/*
1327					 * If eating a newline, avoid putting
1328					 * the newline into the new character
1329					 * stream (via the STPUTC after the
1330					 * switch).
1331					 */
1332					continue;
1333				}
1334                                if (c != '\\' && c != '`' && c != '$'
1335                                    && (!dblquote || c != '"'))
1336                                        STPUTC('\\', out);
1337				break;
1338
1339			case '\n':
1340				plinno++;
1341				needprompt = doprompt;
1342				break;
1343
1344			case PEOF:
1345			        startlinno = plinno;
1346				synerror("EOF in backquote substitution");
1347 				break;
1348
1349			default:
1350				break;
1351			}
1352			STPUTC(c, out);
1353                }
1354done:
1355                STPUTC('\0', out);
1356                savelen = out - stackblock();
1357                if (savelen > 0) {
1358                        str = ckmalloc(savelen);
1359                        memcpy(str, stackblock(), savelen);
1360			setinputstring(str, 1);
1361                }
1362        }
1363	nlpp = &bqlist;
1364	while (*nlpp)
1365		nlpp = &(*nlpp)->next;
1366	*nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
1367	(*nlpp)->next = NULL;
1368	parsebackquote = oldstyle;
1369
1370	if (oldstyle) {
1371		saveprompt = doprompt;
1372		doprompt = 0;
1373	}
1374
1375	n = list(0);
1376
1377	if (oldstyle)
1378		doprompt = saveprompt;
1379	else {
1380		if (readtoken() != TRP)
1381			synexpect(TRP);
1382	}
1383
1384	(*nlpp)->n = n;
1385        if (oldstyle) {
1386		/*
1387		 * Start reading from old file again, ignoring any pushed back
1388		 * tokens left from the backquote parsing
1389		 */
1390                popfile();
1391		tokpushback = 0;
1392	}
1393	while (stackblocksize() <= savelen)
1394		growstackblock();
1395	STARTSTACKSTR(out);
1396	if (str) {
1397		memcpy(out, str, savelen);
1398		STADJUST(savelen, out);
1399		INTOFF;
1400		ckfree(str);
1401		str = NULL;
1402		INTON;
1403	}
1404	parsebackquote = savepbq;
1405	handler = savehandler;
1406	if (arinest || dblquote)
1407		USTPUTC(CTLBACKQ | CTLQUOTE, out);
1408	else
1409		USTPUTC(CTLBACKQ, out);
1410	if (oldstyle)
1411		goto parsebackq_oldreturn;
1412	else
1413		goto parsebackq_newreturn;
1414}
1415
1416/*
1417 * Parse an arithmetic expansion (indicate start of one and set state)
1418 */
1419parsearith: {
1420
1421	if (++arinest == 1) {
1422		prevsyntax = syntax;
1423		syntax = ARISYNTAX;
1424		USTPUTC(CTLARI, out);
1425		if (dblquote)
1426			USTPUTC('"',out);
1427		else
1428			USTPUTC(' ',out);
1429	} else {
1430		/*
1431		 * we collapse embedded arithmetic expansion to
1432		 * parenthesis, which should be equivalent
1433		 */
1434		USTPUTC('(', out);
1435	}
1436	goto parsearith_return;
1437}
1438
1439} /* end of readtoken */
1440
1441
1442
1443#ifdef mkinit
1444RESET {
1445	tokpushback = 0;
1446	checkkwd = 0;
1447}
1448#endif
1449
1450/*
1451 * Returns true if the text contains nothing to expand (no dollar signs
1452 * or backquotes).
1453 */
1454
1455STATIC int
1456noexpand(text)
1457	char *text;
1458	{
1459	char *p;
1460	char c;
1461
1462	p = text;
1463	while ((c = *p++) != '\0') {
1464		if ( c == CTLQUOTEMARK)
1465			continue;
1466		if (c == CTLESC)
1467			p++;
1468		else if (c >= 0 && BASESYNTAX[(int)c] == CCTL)
1469			return 0;
1470	}
1471	return 1;
1472}
1473
1474
1475/*
1476 * Return true if the argument is a legal variable name (a letter or
1477 * underscore followed by zero or more letters, underscores, and digits).
1478 */
1479
1480int
1481goodname(name)
1482	char *name;
1483	{
1484	char *p;
1485
1486	p = name;
1487	if (! is_name(*p))
1488		return 0;
1489	while (*++p) {
1490		if (! is_in_name(*p))
1491			return 0;
1492	}
1493	return 1;
1494}
1495
1496
1497/*
1498 * Called when an unexpected token is read during the parse.  The argument
1499 * is the token that is expected, or -1 if more than one type of token can
1500 * occur at this point.
1501 */
1502
1503STATIC void
1504synexpect(token)
1505	int token;
1506{
1507	char msg[64];
1508
1509	if (token >= 0) {
1510		fmtstr(msg, 64, "%s unexpected (expecting %s)",
1511			tokname[lasttoken], tokname[token]);
1512	} else {
1513		fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
1514	}
1515	synerror(msg);
1516}
1517
1518
1519STATIC void
1520synerror(msg)
1521	char *msg;
1522	{
1523	if (commandname)
1524		outfmt(&errout, "%s: %d: ", commandname, startlinno);
1525	outfmt(&errout, "Syntax error: %s\n", msg);
1526	error((char *)NULL);
1527}
1528
1529STATIC void
1530setprompt(which)
1531	int which;
1532	{
1533	whichprompt = which;
1534
1535#ifndef NO_HISTORY
1536	if (!el)
1537#endif
1538		out2str(getprompt(NULL));
1539}
1540
1541/*
1542 * called by editline -- any expansions to the prompt
1543 *    should be added here.
1544 */
1545char *
1546getprompt(unused)
1547	void *unused __unused;
1548{
1549	switch (whichprompt) {
1550	case 0:
1551		return "";
1552	case 1:
1553		return ps1val();
1554	case 2:
1555		return ps2val();
1556	default:
1557		return "<internal prompt error>";
1558	}
1559}
1560