main.c revision 99939
195887Sjmallett/*	$OpenBSD: main.c,v 1.53 2002/04/26 16:15:16 espie Exp $	*/
295060Sjmallett/*	$NetBSD: main.c,v 1.12 1997/02/08 23:54:49 cgd Exp $	*/
395060Sjmallett
41590Srgrimes/*-
51590Srgrimes * Copyright (c) 1989, 1993
61590Srgrimes *	The Regents of the University of California.  All rights reserved.
71590Srgrimes *
81590Srgrimes * This code is derived from software contributed to Berkeley by
91590Srgrimes * Ozan Yigit at York University.
101590Srgrimes *
111590Srgrimes * Redistribution and use in source and binary forms, with or without
121590Srgrimes * modification, are permitted provided that the following conditions
131590Srgrimes * are met:
141590Srgrimes * 1. Redistributions of source code must retain the above copyright
151590Srgrimes *    notice, this list of conditions and the following disclaimer.
161590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
171590Srgrimes *    notice, this list of conditions and the following disclaimer in the
181590Srgrimes *    documentation and/or other materials provided with the distribution.
191590Srgrimes * 3. All advertising materials mentioning features or use of this software
201590Srgrimes *    must display the following acknowledgement:
211590Srgrimes *	This product includes software developed by the University of
221590Srgrimes *	California, Berkeley and its contributors.
231590Srgrimes * 4. Neither the name of the University nor the names of its contributors
241590Srgrimes *    may be used to endorse or promote products derived from this software
251590Srgrimes *    without specific prior written permission.
261590Srgrimes *
271590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
281590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
291590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
301590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
311590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
321590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
331590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
341590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
351590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
361590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
371590Srgrimes * SUCH DAMAGE.
381590Srgrimes */
391590Srgrimes
4095982Sjmallett#ifndef lint
4195982Sjmallettstatic char copyright[] =
4295982Sjmallett"@(#) Copyright (c) 1989, 1993\n\
4395982Sjmallett	The Regents of the University of California.  All rights reserved.\n";
4495982Sjmallett#endif /* not lint */
4595982Sjmallett
4695982Sjmallett#ifndef lint
4795982Sjmallett#if 0
4895982Sjmallettstatic char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
4995982Sjmallett#else
5095982Sjmallett#if 0
5195982Sjmallettstatic char rcsid[] = "$OpenBSD: main.c,v 1.53 2002/04/26 16:15:16 espie Exp $";
5295982Sjmallett#endif
5395982Sjmallett#endif
5495982Sjmallett#endif /* not lint */
5595982Sjmallett
5695060Sjmallett#include <sys/cdefs.h>
5795060Sjmallett__FBSDID("$FreeBSD: head/usr.bin/m4/main.c 99939 2002-07-14 02:03:23Z jmallett $");
581590Srgrimes
591590Srgrimes/*
601590Srgrimes * main.c
611590Srgrimes * Facility: m4 macro processor
621590Srgrimes * by: oz
631590Srgrimes */
641590Srgrimes
651590Srgrimes#include <sys/types.h>
6695060Sjmallett#include <assert.h>
671590Srgrimes#include <signal.h>
6895060Sjmallett#include <errno.h>
6995060Sjmallett#include <unistd.h>
701590Srgrimes#include <stdio.h>
7195060Sjmallett#include <ctype.h>
7295060Sjmallett#include <string.h>
7395060Sjmallett#include <stddef.h>
7480289Sobrien#include <stdlib.h>
7595060Sjmallett#include <err.h>
761590Srgrimes#include "mdef.h"
771590Srgrimes#include "stdd.h"
781590Srgrimes#include "extern.h"
791590Srgrimes#include "pathnames.h"
801590Srgrimes
811590Srgrimesndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
8295060Sjmallettstae *mstack;		 	/* stack of m4 machine         */
8395060Sjmallettchar *sstack;		 	/* shadow stack, for string space extension */
8495060Sjmallettstatic size_t STACKMAX;		/* current maximum size of stack */
851590Srgrimesint sp; 			/* current m4  stack pointer   */
861590Srgrimesint fp; 			/* m4 call frame pointer       */
8795060Sjmallettstruct input_file infile[MAXINP];/* input file stack (0=stdin)  */
8895060Sjmallettchar *inname[MAXINP];		/* names of these input files */
8995060Sjmallettint inlineno[MAXINP];		/* current number in each input file */
9095060SjmallettFILE **outfile;			/* diversion array(0=bitbucket)*/
9195060Sjmallettint maxout;
921590SrgrimesFILE *active;			/* active output file pointer  */
931590Srgrimesint ilevel = 0; 		/* input file stack pointer    */
941590Srgrimesint oindex = 0; 		/* diversion index..	       */
9595095Sjmallettconst char *null = "";          /* as it says.. just a null..  */
9695095Sjmallettconst char *m4wraps = "";       /* m4wrap string default..     */
9795060Sjmallettchar lquote[MAXCCHARS+1] = {LQUOTE};	/* left quote character  (`)   */
9895060Sjmallettchar rquote[MAXCCHARS+1] = {RQUOTE};	/* right quote character (')   */
9995060Sjmallettchar scommt[MAXCCHARS+1] = {SCOMMT};	/* start character for comment */
10095060Sjmallettchar ecommt[MAXCCHARS+1] = {ECOMMT};	/* end character for comment   */
10194957Sjmallettint synccpp;			/* Line synchronisation for C preprocessor */
1021590Srgrimes
1031590Srgrimesstruct keyblk keywrds[] = {	/* m4 keywords to be installed */
10495060Sjmallett	{ "include",      INCLTYPE },
10595060Sjmallett	{ "sinclude",     SINCTYPE },
10695060Sjmallett	{ "define",       DEFITYPE },
10795060Sjmallett	{ "defn",         DEFNTYPE },
10895060Sjmallett	{ "divert",       DIVRTYPE | NOARGS },
10995060Sjmallett	{ "expr",         EXPRTYPE },
11095060Sjmallett	{ "eval",         EXPRTYPE },
11195060Sjmallett	{ "substr",       SUBSTYPE },
11295060Sjmallett	{ "ifelse",       IFELTYPE },
11395060Sjmallett	{ "ifdef",        IFDFTYPE },
11495060Sjmallett	{ "len",          LENGTYPE },
11595060Sjmallett	{ "incr",         INCRTYPE },
11695060Sjmallett	{ "decr",         DECRTYPE },
11795060Sjmallett	{ "dnl",          DNLNTYPE | NOARGS },
11895060Sjmallett	{ "changequote",  CHNQTYPE | NOARGS },
11995060Sjmallett	{ "changecom",    CHNCTYPE | NOARGS },
12095060Sjmallett	{ "index",        INDXTYPE },
1211590Srgrimes#ifdef EXTENDED
12295060Sjmallett	{ "paste",        PASTTYPE },
12395060Sjmallett	{ "spaste",       SPASTYPE },
12495060Sjmallett    	/* Newer extensions, needed to handle gnu-m4 scripts */
12595060Sjmallett	{ "indir",        INDIRTYPE},
12695060Sjmallett	{ "builtin",      BUILTINTYPE},
12795060Sjmallett	{ "patsubst",	  PATSTYPE},
12895060Sjmallett	{ "regexp",	  REGEXPTYPE},
12995060Sjmallett	{ "esyscmd",	  ESYSCMDTYPE},
13095060Sjmallett	{ "__file__",	  FILENAMETYPE | NOARGS},
13195060Sjmallett	{ "__line__",	  LINETYPE | NOARGS},
1321590Srgrimes#endif
13395060Sjmallett	{ "popdef",       POPDTYPE },
13495060Sjmallett	{ "pushdef",      PUSDTYPE },
13595060Sjmallett	{ "dumpdef",      DUMPTYPE | NOARGS },
13695060Sjmallett	{ "shift",        SHIFTYPE | NOARGS },
13795060Sjmallett	{ "translit",     TRNLTYPE },
13895060Sjmallett	{ "undefine",     UNDFTYPE },
13995060Sjmallett	{ "undivert",     UNDVTYPE | NOARGS },
14095060Sjmallett	{ "divnum",       DIVNTYPE | NOARGS },
14195060Sjmallett	{ "maketemp",     MKTMTYPE },
14295060Sjmallett	{ "errprint",     ERRPTYPE | NOARGS },
14395060Sjmallett	{ "m4wrap",       M4WRTYPE | NOARGS },
14495060Sjmallett	{ "m4exit",       EXITTYPE | NOARGS },
14595060Sjmallett	{ "syscmd",       SYSCTYPE },
14695060Sjmallett	{ "sysval",       SYSVTYPE | NOARGS },
14795060Sjmallett	{ "traceon",	  TRACEONTYPE | NOARGS },
14895060Sjmallett	{ "traceoff",	  TRACEOFFTYPE | NOARGS },
1491590Srgrimes
15095060Sjmallett#if defined(unix) || defined(__unix__)
15195060Sjmallett	{ "unix",         SELFTYPE | NOARGS },
1521590Srgrimes#else
1531590Srgrimes#ifdef vms
15495060Sjmallett	{ "vms",          SELFTYPE | NOARGS },
1551590Srgrimes#endif
1561590Srgrimes#endif
1571590Srgrimes};
1581590Srgrimes
1591590Srgrimes#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
1601590Srgrimes
16195060Sjmallett#define MAXRECORD 50
16295060Sjmallettstatic struct position {
16395060Sjmallett	char *name;
16495060Sjmallett	unsigned long line;
16595060Sjmallett} quotes[MAXRECORD], paren[MAXRECORD];
16695060Sjmallett
16795060Sjmallettstatic void record(struct position *, int);
16895060Sjmallettstatic void dump_stack(struct position *, int);
16995060Sjmallett
17095060Sjmallettstatic void macro(void);
17195060Sjmallettstatic void initkwds(void);
17295060Sjmallettstatic ndptr inspect(int, char *);
17395060Sjmallettstatic int do_look_ahead(int, const char *);
17495060Sjmallett
17595060Sjmallettstatic void enlarge_stack(void);
17695060Sjmallett
1771590Srgrimesint
17895887Sjmallettmain(int argc, char *argv[])
1791590Srgrimes{
18095060Sjmallett	int c;
18195060Sjmallett	int n;
18297296Stjr	int rval;
1831590Srgrimes	char *p;
1841590Srgrimes
18595060Sjmallett	traceout = stderr;
18695060Sjmallett
1871590Srgrimes	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1881590Srgrimes		signal(SIGINT, onintr);
1891590Srgrimes
1901590Srgrimes	initkwds();
19195060Sjmallett	initspaces();
19295060Sjmallett	STACKMAX = INITSTACKMAX;
1931590Srgrimes
19495060Sjmallett	mstack = (stae *)xalloc(sizeof(stae) * STACKMAX);
19595060Sjmallett	sstack = (char *)xalloc(STACKMAX);
19695060Sjmallett
19795060Sjmallett	maxout = 0;
19895060Sjmallett	outfile = NULL;
19995060Sjmallett	resizedivs(MAXOUT);
20095060Sjmallett
20195060Sjmallett	while ((c = getopt(argc, argv, "gst:d:D:U:o:I:")) != -1)
2021590Srgrimes		switch(c) {
2031590Srgrimes
2041590Srgrimes		case 'D':               /* define something..*/
2051590Srgrimes			for (p = optarg; *p; p++)
2061590Srgrimes				if (*p == '=')
2071590Srgrimes					break;
2081590Srgrimes			if (*p)
2091590Srgrimes				*p++ = EOS;
2101590Srgrimes			dodefine(optarg, p);
2111590Srgrimes			break;
21295060Sjmallett		case 'I':
21395060Sjmallett			addtoincludepath(optarg);
21495060Sjmallett			break;
2151590Srgrimes		case 'U':               /* undefine...       */
2161590Srgrimes			remhash(optarg, TOP);
2171590Srgrimes			break;
21895060Sjmallett		case 'g':
21995060Sjmallett			mimic_gnu = 1;
22095060Sjmallett			break;
22195060Sjmallett		case 'd':
22295060Sjmallett			set_trace_flags(optarg);
22395060Sjmallett			break;
22494957Sjmallett		case 's':
22594957Sjmallett			synccpp = 1;
22694957Sjmallett			break;
22795060Sjmallett		case 't':
22895060Sjmallett			mark_traced(optarg, 1);
22995060Sjmallett			break;
23095060Sjmallett		case 'o':
23195060Sjmallett			trace_file(optarg);
23295060Sjmallett                        break;
2331590Srgrimes		case '?':
2341590Srgrimes			usage();
2351590Srgrimes		}
2361590Srgrimes
2371590Srgrimes        argc -= optind;
2381590Srgrimes        argv += optind;
2391590Srgrimes
24097296Stjr	rval = 0;
2411590Srgrimes	active = stdout;		/* default active output     */
2421590Srgrimes	bbase[0] = bufbase;
2431590Srgrimes        if (!argc) {
2441590Srgrimes 		sp = -1;		/* stack pointer initialized */
2451590Srgrimes		fp = 0; 		/* frame pointer initialized */
24695060Sjmallett		set_input(infile+0, stdin, "stdin");
24795060Sjmallett					/* default input (naturally) */
24894957Sjmallett		if ((inname[0] = strdup("-")) == NULL)
24994957Sjmallett			err(1, NULL);
25094957Sjmallett		inlineno[0] = 1;
25194957Sjmallett		emitline();
2521590Srgrimes		macro();
2531590Srgrimes	} else
2541590Srgrimes		for (; argc--; ++argv) {
2551590Srgrimes			p = *argv;
25695060Sjmallett			if (p[0] == '-' && p[1] == EOS)
25795060Sjmallett				set_input(infile, stdin, "stdin");
25897296Stjr			else if (fopen_trypath(infile, p) == NULL) {
25997296Stjr				warn("%s", p);
26097296Stjr				rval = 1;
26197296Stjr				continue;
26297296Stjr			}
2631590Srgrimes			sp = -1;
26495060Sjmallett			fp = 0;
26594957Sjmallett			if ((inname[0] = strdup(p)) == NULL)
26694957Sjmallett				err(1, NULL);
26794957Sjmallett			inlineno[0] = 1;
26894957Sjmallett			emitline();
2691590Srgrimes			macro();
27095060Sjmallett		    	release_input(infile);
2711590Srgrimes		}
2721590Srgrimes
2731590Srgrimes	if (*m4wraps) { 		/* anything for rundown ??   */
2741590Srgrimes		ilevel = 0;		/* in case m4wrap includes.. */
2751590Srgrimes		bufbase = bp = buf;	/* use the entire buffer   */
2761590Srgrimes		pbstr(m4wraps); 	/* user-defined wrapup act   */
2771590Srgrimes		macro();		/* last will and testament   */
2781590Srgrimes	}
2791590Srgrimes
2801590Srgrimes	if (active != stdout)
2811590Srgrimes		active = stdout;	/* reset output just in case */
28295060Sjmallett	for (n = 1; n < maxout; n++)	/* default wrap-up: undivert */
2831590Srgrimes		if (outfile[n] != NULL)
2841590Srgrimes			getdiv(n);
2851590Srgrimes					/* remove bitbucket if used  */
28695060Sjmallett	if (outfile[0] != NULL) {
28795060Sjmallett		(void) fclose(outfile[0]);
28895060Sjmallett	}
28995060Sjmallett
29097296Stjr	exit(rval);
2911590Srgrimes}
2921590Srgrimes
29395060Sjmallett/*
29495060Sjmallett * Look ahead for `token'.
29595060Sjmallett * (on input `t == token[0]')
29695060Sjmallett * Used for comment and quoting delimiters.
29795060Sjmallett * Returns 1 if `token' present; copied to output.
29895060Sjmallett *         0 if `token' not found; all characters pushed back
29995060Sjmallett */
30095060Sjmallettstatic int
30195887Sjmallettdo_look_ahead(int t, const char *token)
30295060Sjmallett{
30395060Sjmallett	int i;
3041590Srgrimes
30595060Sjmallett	assert((unsigned char)t == (unsigned char)token[0]);
30695060Sjmallett
30795060Sjmallett	for (i = 1; *++token; i++) {
30895060Sjmallett		t = gpbc();
30995060Sjmallett		if (t == EOF || (unsigned char)t != (unsigned char)*token) {
31095060Sjmallett			putback(t);
31195060Sjmallett			while (--i)
31295060Sjmallett				putback(*--token);
31395060Sjmallett			return 0;
31495060Sjmallett		}
31595060Sjmallett	}
31695060Sjmallett	return 1;
31795060Sjmallett}
31895060Sjmallett
31995060Sjmallett#define LOOK_AHEAD(t, token) (t != EOF && 		\
32095060Sjmallett    (unsigned char)(t)==(unsigned char)(token)[0] && 	\
32195060Sjmallett    do_look_ahead(t,token))
32295060Sjmallett
3231590Srgrimes/*
3241590Srgrimes * macro - the work horse..
3251590Srgrimes */
32695060Sjmallettstatic void
32799939Sjmallettmacro(void)
32895060Sjmallett{
32995060Sjmallett	char token[MAXTOK+1];
33095060Sjmallett	int t, l;
33195060Sjmallett	ndptr p;
33295060Sjmallett	int  nlpar;
3331590Srgrimes
3341590Srgrimes	cycle {
33595060Sjmallett		t = gpbc();
33695060Sjmallett		if (t == '_' || isalpha(t)) {
33795060Sjmallett			p = inspect(t, token);
33895060Sjmallett			if (p != nil)
33995060Sjmallett				putback(l = gpbc());
34095060Sjmallett			if (p == nil || (l != LPAREN &&
34195060Sjmallett			    (p->type & NEEDARGS) != 0))
34295060Sjmallett				outputstr(token);
3431590Srgrimes			else {
3441590Srgrimes		/*
3451590Srgrimes		 * real thing.. First build a call frame:
3461590Srgrimes		 */
3471590Srgrimes				pushf(fp);	/* previous call frm */
3481590Srgrimes				pushf(p->type); /* type of the call  */
3491590Srgrimes				pushf(0);	/* parenthesis level */
3501590Srgrimes				fp = sp;	/* new frame pointer */
3511590Srgrimes		/*
3521590Srgrimes		 * now push the string arguments:
3531590Srgrimes		 */
35495060Sjmallett				pushs1(p->defn);	/* defn string */
35595060Sjmallett				pushs1(p->name);	/* macro name  */
35695060Sjmallett				pushs(ep);	      	/* start next..*/
3571590Srgrimes
35895060Sjmallett				if (l != LPAREN && PARLEV == 0)  {
35995060Sjmallett				    /* no bracks  */
36095060Sjmallett					chrsave(EOS);
36195060Sjmallett
36298490Sjmallett					if ((uintptr_t)sp == STACKMAX)
36395060Sjmallett						errx(1, "internal stack overflow");
36495060Sjmallett					eval((const char **) mstack+fp+1, 2,
36595060Sjmallett					    CALTYP);
36695060Sjmallett
36795060Sjmallett					ep = PREVEP;	/* flush strspace */
36895060Sjmallett					sp = PREVSP;	/* previous sp..  */
36995060Sjmallett					fp = PREVFP;	/* rewind stack...*/
3701590Srgrimes				}
3711590Srgrimes			}
37295060Sjmallett		} else if (t == EOF) {
37395060Sjmallett			if (sp > -1) {
37495060Sjmallett				warnx( "unexpected end of input, unclosed parenthesis:");
37595060Sjmallett				dump_stack(paren, PARLEV);
37695060Sjmallett				exit(1);
37795060Sjmallett			}
3781590Srgrimes			if (ilevel <= 0)
3791590Srgrimes				break;			/* all done thanks.. */
38095060Sjmallett			release_input(infile+ilevel--);
38194957Sjmallett			free(inname[ilevel+1]);
3821590Srgrimes			bufbase = bbase[ilevel];
38394957Sjmallett			emitline();
3841590Srgrimes			continue;
3851590Srgrimes		}
3861590Srgrimes	/*
38795060Sjmallett	 * non-alpha token possibly seen..
3881590Srgrimes	 * [the order of else if .. stmts is important.]
3891590Srgrimes	 */
39095060Sjmallett		else if (LOOK_AHEAD(t,lquote)) {	/* strip quotes */
39195060Sjmallett			nlpar = 0;
39295060Sjmallett			record(quotes, nlpar++);
39395060Sjmallett			/*
39495060Sjmallett			 * Opening quote: scan forward until matching
39595060Sjmallett			 * closing quote has been found.
39695060Sjmallett			 */
3971590Srgrimes			do {
39895060Sjmallett
39995060Sjmallett				l = gpbc();
40095060Sjmallett				if (LOOK_AHEAD(l,rquote)) {
40195060Sjmallett					if (--nlpar > 0)
40295060Sjmallett						outputstr(rquote);
40395060Sjmallett				} else if (LOOK_AHEAD(l,lquote)) {
40495060Sjmallett					record(quotes, nlpar++);
40595060Sjmallett					outputstr(lquote);
40695060Sjmallett				} else if (l == EOF) {
40795060Sjmallett					if (nlpar == 1)
40895060Sjmallett						warnx("unclosed quote:");
4091590Srgrimes					else
41095060Sjmallett						warnx("%d unclosed quotes:", nlpar);
41195060Sjmallett					dump_stack(quotes, nlpar);
41295060Sjmallett					exit(1);
41395060Sjmallett				} else {
41495060Sjmallett					if (nlpar > 0) {
41595060Sjmallett						if (sp < 0)
41695060Sjmallett							putc(l, active);
41795060Sjmallett						else
41895060Sjmallett							CHRSAVE(l);
41995060Sjmallett					}
4201590Srgrimes				}
4211590Srgrimes			}
4221590Srgrimes			while (nlpar != 0);
4231590Srgrimes		}
4241590Srgrimes
42595060Sjmallett		else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
42695060Sjmallett			fputs(scommt, active);
42795060Sjmallett
42895060Sjmallett			for(;;) {
42995060Sjmallett				t = gpbc();
43095060Sjmallett				if (LOOK_AHEAD(t, ecommt)) {
43195060Sjmallett					fputs(ecommt, active);
43295060Sjmallett					break;
43395060Sjmallett				}
43495060Sjmallett				if (t == EOF)
43595060Sjmallett					break;
4361590Srgrimes				putc(t, active);
4371590Srgrimes			}
43895060Sjmallett		}
43995060Sjmallett
44095060Sjmallett		else if (sp < 0) {		/* not in a macro at all */
4411590Srgrimes			putc(t, active);	/* output directly..	 */
4421590Srgrimes		}
4431590Srgrimes
4441590Srgrimes		else switch(t) {
4451590Srgrimes
4461590Srgrimes		case LPAREN:
4471590Srgrimes			if (PARLEV > 0)
4481590Srgrimes				chrsave(t);
44995060Sjmallett			while (isspace(l = gpbc()))
4501590Srgrimes				;		/* skip blank, tab, nl.. */
4511590Srgrimes			putback(l);
45295060Sjmallett			record(paren, PARLEV++);
4531590Srgrimes			break;
4541590Srgrimes
4551590Srgrimes		case RPAREN:
4561590Srgrimes			if (--PARLEV > 0)
4571590Srgrimes				chrsave(t);
4581590Srgrimes			else {			/* end of argument list */
4591590Srgrimes				chrsave(EOS);
4601590Srgrimes
46198490Sjmallett				if ((uintptr_t)sp == STACKMAX)
46227625Scharnier					errx(1, "internal stack overflow");
4631590Srgrimes
46495060Sjmallett				eval((const char **) mstack+fp+1, sp-fp,
46595060Sjmallett				    CALTYP);
4661590Srgrimes
4671590Srgrimes				ep = PREVEP;	/* flush strspace */
4681590Srgrimes				sp = PREVSP;	/* previous sp..  */
4691590Srgrimes				fp = PREVFP;	/* rewind stack...*/
4701590Srgrimes			}
4711590Srgrimes			break;
4721590Srgrimes
4731590Srgrimes		case COMMA:
4741590Srgrimes			if (PARLEV == 1) {
4751590Srgrimes				chrsave(EOS);		/* new argument   */
47695060Sjmallett				while (isspace(l = gpbc()))
4771590Srgrimes					;
4781590Srgrimes				putback(l);
4791590Srgrimes				pushs(ep);
4801590Srgrimes			} else
4811590Srgrimes				chrsave(t);
4821590Srgrimes			break;
4831590Srgrimes
4841590Srgrimes		default:
48595060Sjmallett			if (LOOK_AHEAD(t, scommt)) {
48695095Sjmallett				char *pc;
48795095Sjmallett				for (pc = scommt; *pc; pc++)
48895095Sjmallett					chrsave(*pc);
48995060Sjmallett				for(;;) {
49095060Sjmallett					t = gpbc();
49195060Sjmallett					if (LOOK_AHEAD(t, ecommt)) {
49295095Sjmallett						for (pc = ecommt; *pc; pc++)
49395095Sjmallett							chrsave(*pc);
49495060Sjmallett						break;
49595060Sjmallett					}
49695060Sjmallett					if (t == EOF)
49795060Sjmallett					    break;
49895060Sjmallett					CHRSAVE(t);
49995060Sjmallett				}
50095060Sjmallett			} else
50195060Sjmallett				CHRSAVE(t);		/* stack the char */
5021590Srgrimes			break;
5031590Srgrimes		}
5041590Srgrimes	}
5051590Srgrimes}
5061590Srgrimes
50795060Sjmallett/*
50895060Sjmallett * output string directly, without pushing it for reparses.
50995060Sjmallett */
51095060Sjmallettvoid
51195887Sjmallettoutputstr(const char *s)
51295060Sjmallett{
51395060Sjmallett	if (sp < 0)
51495060Sjmallett		while (*s)
51595060Sjmallett			putc(*s++, active);
51695060Sjmallett	else
51795060Sjmallett		while (*s)
51895060Sjmallett			CHRSAVE(*s++);
51995060Sjmallett}
52095060Sjmallett
5211590Srgrimes/*
5221590Srgrimes * build an input token..
5231590Srgrimes * consider only those starting with _ or A-Za-z. This is a
5241590Srgrimes * combo with lookup to speed things up.
5251590Srgrimes */
52695060Sjmallettstatic ndptr
52795887Sjmallettinspect(int c, char *tp)
5281590Srgrimes{
52995060Sjmallett	char *name = tp;
53095060Sjmallett	char *etp = tp+MAXTOK;
53195060Sjmallett	ndptr p;
53295060Sjmallett	unsigned int h;
53395060Sjmallett
53495060Sjmallett	h = *tp++ = c;
5351590Srgrimes
53695060Sjmallett	while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
5371590Srgrimes		h = (h << 5) + h + (*tp++ = c);
53895060Sjmallett	if (c != EOF)
53995060Sjmallett		PUTBACK(c);
5401590Srgrimes	*tp = EOS;
54195060Sjmallett	/* token is too long, it won't match anything, but it can still
54295060Sjmallett	 * be output. */
54395060Sjmallett	if (tp == ep) {
54495060Sjmallett		outputstr(name);
54595060Sjmallett		while (isalnum(c = gpbc()) || c == '_') {
54695060Sjmallett			if (sp < 0)
54795060Sjmallett				putc(c, active);
54895060Sjmallett			else
54995060Sjmallett				CHRSAVE(c);
55095060Sjmallett		}
55195060Sjmallett		*name = EOS;
55295060Sjmallett		return nil;
55395060Sjmallett	}
5541590Srgrimes
55595060Sjmallett	for (p = hashtab[h % HASHSIZE]; p != nil; p = p->nxtptr)
55695060Sjmallett		if (h == p->hv && STREQ(name, p->name))
5571590Srgrimes			break;
5581590Srgrimes	return p;
5591590Srgrimes}
5601590Srgrimes
5611590Srgrimes/*
56295060Sjmallett * initkwds - initialise m4 keywords as fast as possible.
5631590Srgrimes * This very similar to install, but without certain overheads,
56495060Sjmallett * such as calling lookup. Malloc is not used for storing the
56595060Sjmallett * keyword strings, since we simply use the static pointers
5661590Srgrimes * within keywrds block.
5671590Srgrimes */
56895060Sjmallettstatic void
56999939Sjmallettinitkwds(void)
57095060Sjmallett{
57195060Sjmallett	size_t i;
57295060Sjmallett	unsigned int h;
57395060Sjmallett	ndptr p;
5741590Srgrimes
5751590Srgrimes	for (i = 0; i < MAXKEYS; i++) {
5761590Srgrimes		h = hash(keywrds[i].knam);
57795060Sjmallett		p = (ndptr) xalloc(sizeof(struct ndblock));
57895060Sjmallett		p->nxtptr = hashtab[h % HASHSIZE];
57995060Sjmallett		hashtab[h % HASHSIZE] = p;
58095060Sjmallett		p->name = xstrdup(keywrds[i].knam);
58195095Sjmallett		p->defn = xstrdup(null);
58295060Sjmallett		p->hv = h;
58395060Sjmallett		p->type = keywrds[i].ktyp & TYPEMASK;
58495060Sjmallett		if ((keywrds[i].ktyp & NOARGS) == 0)
58595060Sjmallett			p->type |= NEEDARGS;
5861590Srgrimes	}
5871590Srgrimes}
58894957Sjmallett
58995060Sjmallett/* Look up a builtin type, even if overridden by the user */
59095060Sjmallettint
59195887Sjmallettbuiltin_type(const char *key)
59295060Sjmallett{
59395060Sjmallett	int i;
59495060Sjmallett
59595060Sjmallett	for (i = 0; i != MAXKEYS; i++)
59695060Sjmallett		if (STREQ(keywrds[i].knam, key))
59795060Sjmallett			return keywrds[i].ktyp;
59895060Sjmallett	return -1;
59995060Sjmallett}
60095060Sjmallett
60195095Sjmallettconst char *
60295887Sjmallettbuiltin_realname(int n)
60395060Sjmallett{
60495060Sjmallett	int i;
60595060Sjmallett
60695060Sjmallett	for (i = 0; i != MAXKEYS; i++)
60795060Sjmallett		if (((keywrds[i].ktyp ^ n) & TYPEMASK) == 0)
60895060Sjmallett			return keywrds[i].knam;
60995060Sjmallett	return NULL;
61095060Sjmallett}
61195060Sjmallett
61295060Sjmallettstatic void
61395887Sjmallettrecord(struct position *t, int lev)
61495060Sjmallett{
61595060Sjmallett	if (lev < MAXRECORD) {
61695060Sjmallett		t[lev].name = CURRENT_NAME;
61795060Sjmallett		t[lev].line = CURRENT_LINE;
61895060Sjmallett	}
61995060Sjmallett}
62095060Sjmallett
62195060Sjmallettstatic void
62295887Sjmallettdump_stack(struct position *t, int lev)
62395060Sjmallett{
62495060Sjmallett	int i;
62595060Sjmallett
62695060Sjmallett	for (i = 0; i < lev; i++) {
62795060Sjmallett		if (i == MAXRECORD) {
62895060Sjmallett			fprintf(stderr, "   ...\n");
62995060Sjmallett			break;
63095060Sjmallett		}
63195060Sjmallett		fprintf(stderr, "   %s at line %lu\n",
63295060Sjmallett			t[i].name, t[i].line);
63395060Sjmallett	}
63495060Sjmallett}
63595060Sjmallett
63695060Sjmallett
63795060Sjmallettstatic void
63899939Sjmallettenlarge_stack(void)
63995060Sjmallett{
64095060Sjmallett	STACKMAX *= 2;
64195060Sjmallett	mstack = realloc(mstack, sizeof(stae) * STACKMAX);
64295060Sjmallett	sstack = realloc(sstack, STACKMAX);
64395060Sjmallett	if (mstack == NULL || sstack == NULL)
64495060Sjmallett		errx(1, "Evaluation stack overflow (%lu)",
64595060Sjmallett		    (unsigned long)STACKMAX);
64695060Sjmallett}
64795060Sjmallett
64894957Sjmallett/* Emit preprocessor #line directive if -s option used. */
64994957Sjmallettvoid
65094957Sjmallettemitline(void)
65194957Sjmallett{
65295060Sjmallett
65394957Sjmallett	if (synccpp)
65494957Sjmallett		fprintf(active, "#line %d \"%s\"\n", inlineno[ilevel],
65595060Sjmallett			inname[ilevel]);
65694957Sjmallett}
657