main.c revision 95887
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
4095060Sjmallett#include <sys/cdefs.h>
4195060Sjmallett__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
4295060Sjmallett	The Regents of the University of California.  All rights reserved.\n");
4395887Sjmallett__SCCSID("@(#)main.c	8.1 (Berkeley) 6/6/93");
4495887Sjmallett__RCSID_SOURCE("$OpenBSD: main.c,v 1.53 2002/04/26 16:15:16 espie Exp $");
4595060Sjmallett__FBSDID("$FreeBSD: head/usr.bin/m4/main.c 95887 2002-05-01 21:37:29Z jmallett $");
461590Srgrimes
471590Srgrimes/*
481590Srgrimes * main.c
491590Srgrimes * Facility: m4 macro processor
501590Srgrimes * by: oz
511590Srgrimes */
521590Srgrimes
531590Srgrimes#include <sys/types.h>
5495060Sjmallett#include <assert.h>
551590Srgrimes#include <signal.h>
5695060Sjmallett#include <errno.h>
5795060Sjmallett#include <unistd.h>
581590Srgrimes#include <stdio.h>
5995060Sjmallett#include <ctype.h>
6095060Sjmallett#include <string.h>
6195060Sjmallett#include <stddef.h>
6280289Sobrien#include <stdlib.h>
6395060Sjmallett#include <err.h>
641590Srgrimes#include "mdef.h"
651590Srgrimes#include "stdd.h"
661590Srgrimes#include "extern.h"
671590Srgrimes#include "pathnames.h"
681590Srgrimes
691590Srgrimesndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
7095060Sjmallettstae *mstack;		 	/* stack of m4 machine         */
7195060Sjmallettchar *sstack;		 	/* shadow stack, for string space extension */
7295060Sjmallettstatic size_t STACKMAX;		/* current maximum size of stack */
731590Srgrimesint sp; 			/* current m4  stack pointer   */
741590Srgrimesint fp; 			/* m4 call frame pointer       */
7595060Sjmallettstruct input_file infile[MAXINP];/* input file stack (0=stdin)  */
7695060Sjmallettchar *inname[MAXINP];		/* names of these input files */
7795060Sjmallettint inlineno[MAXINP];		/* current number in each input file */
7895060SjmallettFILE **outfile;			/* diversion array(0=bitbucket)*/
7995060Sjmallettint maxout;
801590SrgrimesFILE *active;			/* active output file pointer  */
811590Srgrimesint ilevel = 0; 		/* input file stack pointer    */
821590Srgrimesint oindex = 0; 		/* diversion index..	       */
8395095Sjmallettconst char *null = "";          /* as it says.. just a null..  */
8495095Sjmallettconst char *m4wraps = "";       /* m4wrap string default..     */
8595060Sjmallettchar lquote[MAXCCHARS+1] = {LQUOTE};	/* left quote character  (`)   */
8695060Sjmallettchar rquote[MAXCCHARS+1] = {RQUOTE};	/* right quote character (')   */
8795060Sjmallettchar scommt[MAXCCHARS+1] = {SCOMMT};	/* start character for comment */
8895060Sjmallettchar ecommt[MAXCCHARS+1] = {ECOMMT};	/* end character for comment   */
8994957Sjmallettint synccpp;			/* Line synchronisation for C preprocessor */
901590Srgrimes
911590Srgrimesstruct keyblk keywrds[] = {	/* m4 keywords to be installed */
9295060Sjmallett	{ "include",      INCLTYPE },
9395060Sjmallett	{ "sinclude",     SINCTYPE },
9495060Sjmallett	{ "define",       DEFITYPE },
9595060Sjmallett	{ "defn",         DEFNTYPE },
9695060Sjmallett	{ "divert",       DIVRTYPE | NOARGS },
9795060Sjmallett	{ "expr",         EXPRTYPE },
9895060Sjmallett	{ "eval",         EXPRTYPE },
9995060Sjmallett	{ "substr",       SUBSTYPE },
10095060Sjmallett	{ "ifelse",       IFELTYPE },
10195060Sjmallett	{ "ifdef",        IFDFTYPE },
10295060Sjmallett	{ "len",          LENGTYPE },
10395060Sjmallett	{ "incr",         INCRTYPE },
10495060Sjmallett	{ "decr",         DECRTYPE },
10595060Sjmallett	{ "dnl",          DNLNTYPE | NOARGS },
10695060Sjmallett	{ "changequote",  CHNQTYPE | NOARGS },
10795060Sjmallett	{ "changecom",    CHNCTYPE | NOARGS },
10895060Sjmallett	{ "index",        INDXTYPE },
1091590Srgrimes#ifdef EXTENDED
11095060Sjmallett	{ "paste",        PASTTYPE },
11195060Sjmallett	{ "spaste",       SPASTYPE },
11295060Sjmallett    	/* Newer extensions, needed to handle gnu-m4 scripts */
11395060Sjmallett	{ "indir",        INDIRTYPE},
11495060Sjmallett	{ "builtin",      BUILTINTYPE},
11595060Sjmallett	{ "patsubst",	  PATSTYPE},
11695060Sjmallett	{ "regexp",	  REGEXPTYPE},
11795060Sjmallett	{ "esyscmd",	  ESYSCMDTYPE},
11895060Sjmallett	{ "__file__",	  FILENAMETYPE | NOARGS},
11995060Sjmallett	{ "__line__",	  LINETYPE | NOARGS},
1201590Srgrimes#endif
12195060Sjmallett	{ "popdef",       POPDTYPE },
12295060Sjmallett	{ "pushdef",      PUSDTYPE },
12395060Sjmallett	{ "dumpdef",      DUMPTYPE | NOARGS },
12495060Sjmallett	{ "shift",        SHIFTYPE | NOARGS },
12595060Sjmallett	{ "translit",     TRNLTYPE },
12695060Sjmallett	{ "undefine",     UNDFTYPE },
12795060Sjmallett	{ "undivert",     UNDVTYPE | NOARGS },
12895060Sjmallett	{ "divnum",       DIVNTYPE | NOARGS },
12995060Sjmallett	{ "maketemp",     MKTMTYPE },
13095060Sjmallett	{ "errprint",     ERRPTYPE | NOARGS },
13195060Sjmallett	{ "m4wrap",       M4WRTYPE | NOARGS },
13295060Sjmallett	{ "m4exit",       EXITTYPE | NOARGS },
13395060Sjmallett	{ "syscmd",       SYSCTYPE },
13495060Sjmallett	{ "sysval",       SYSVTYPE | NOARGS },
13595060Sjmallett	{ "traceon",	  TRACEONTYPE | NOARGS },
13695060Sjmallett	{ "traceoff",	  TRACEOFFTYPE | NOARGS },
1371590Srgrimes
13895060Sjmallett#if defined(unix) || defined(__unix__)
13995060Sjmallett	{ "unix",         SELFTYPE | NOARGS },
1401590Srgrimes#else
1411590Srgrimes#ifdef vms
14295060Sjmallett	{ "vms",          SELFTYPE | NOARGS },
1431590Srgrimes#endif
1441590Srgrimes#endif
1451590Srgrimes};
1461590Srgrimes
1471590Srgrimes#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
1481590Srgrimes
14995060Sjmallettextern int optind;
15095060Sjmallettextern char *optarg;
1511590Srgrimes
15295060Sjmallett#define MAXRECORD 50
15395060Sjmallettstatic struct position {
15495060Sjmallett	char *name;
15595060Sjmallett	unsigned long line;
15695060Sjmallett} quotes[MAXRECORD], paren[MAXRECORD];
15795060Sjmallett
15895060Sjmallettstatic void record(struct position *, int);
15995060Sjmallettstatic void dump_stack(struct position *, int);
16095060Sjmallett
16195060Sjmallettstatic void macro(void);
16295060Sjmallettstatic void initkwds(void);
16395060Sjmallettstatic ndptr inspect(int, char *);
16495060Sjmallettstatic int do_look_ahead(int, const char *);
16595060Sjmallett
16695060Sjmallettstatic void enlarge_stack(void);
16795060Sjmallett
16895060Sjmallettint main(int, char *[]);
16995060Sjmallett
1701590Srgrimesint
17195887Sjmallettmain(int argc, char *argv[])
1721590Srgrimes{
17395060Sjmallett	int c;
17495060Sjmallett	int n;
1751590Srgrimes	char *p;
1761590Srgrimes
17795060Sjmallett	traceout = stderr;
17895060Sjmallett
1791590Srgrimes	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1801590Srgrimes		signal(SIGINT, onintr);
1811590Srgrimes
1821590Srgrimes	initkwds();
18395060Sjmallett	initspaces();
18495060Sjmallett	STACKMAX = INITSTACKMAX;
1851590Srgrimes
18695060Sjmallett	mstack = (stae *)xalloc(sizeof(stae) * STACKMAX);
18795060Sjmallett	sstack = (char *)xalloc(STACKMAX);
18895060Sjmallett
18995060Sjmallett	maxout = 0;
19095060Sjmallett	outfile = NULL;
19195060Sjmallett	resizedivs(MAXOUT);
19295060Sjmallett
19395060Sjmallett	while ((c = getopt(argc, argv, "gst:d:D:U:o:I:")) != -1)
1941590Srgrimes		switch(c) {
1951590Srgrimes
1961590Srgrimes		case 'D':               /* define something..*/
1971590Srgrimes			for (p = optarg; *p; p++)
1981590Srgrimes				if (*p == '=')
1991590Srgrimes					break;
2001590Srgrimes			if (*p)
2011590Srgrimes				*p++ = EOS;
2021590Srgrimes			dodefine(optarg, p);
2031590Srgrimes			break;
20495060Sjmallett		case 'I':
20595060Sjmallett			addtoincludepath(optarg);
20695060Sjmallett			break;
2071590Srgrimes		case 'U':               /* undefine...       */
2081590Srgrimes			remhash(optarg, TOP);
2091590Srgrimes			break;
21095060Sjmallett		case 'g':
21195060Sjmallett			mimic_gnu = 1;
21295060Sjmallett			break;
21395060Sjmallett		case 'd':
21495060Sjmallett			set_trace_flags(optarg);
21595060Sjmallett			break;
21694957Sjmallett		case 's':
21794957Sjmallett			synccpp = 1;
21894957Sjmallett			break;
21995060Sjmallett		case 't':
22095060Sjmallett			mark_traced(optarg, 1);
22195060Sjmallett			break;
22295060Sjmallett		case 'o':
22395060Sjmallett			trace_file(optarg);
22495060Sjmallett                        break;
2251590Srgrimes		case '?':
2261590Srgrimes			usage();
2271590Srgrimes		}
2281590Srgrimes
2291590Srgrimes        argc -= optind;
2301590Srgrimes        argv += optind;
2311590Srgrimes
2321590Srgrimes	active = stdout;		/* default active output     */
2331590Srgrimes	bbase[0] = bufbase;
2341590Srgrimes        if (!argc) {
2351590Srgrimes 		sp = -1;		/* stack pointer initialized */
2361590Srgrimes		fp = 0; 		/* frame pointer initialized */
23795060Sjmallett		set_input(infile+0, stdin, "stdin");
23895060Sjmallett					/* default input (naturally) */
23994957Sjmallett		if ((inname[0] = strdup("-")) == NULL)
24094957Sjmallett			err(1, NULL);
24194957Sjmallett		inlineno[0] = 1;
24294957Sjmallett		emitline();
2431590Srgrimes		macro();
2441590Srgrimes	} else
2451590Srgrimes		for (; argc--; ++argv) {
2461590Srgrimes			p = *argv;
24795060Sjmallett			if (p[0] == '-' && p[1] == EOS)
24895060Sjmallett				set_input(infile, stdin, "stdin");
24995060Sjmallett			else if (fopen_trypath(infile, p) == NULL)
25027625Scharnier				err(1, "%s", p);
2511590Srgrimes			sp = -1;
25295060Sjmallett			fp = 0;
25394957Sjmallett			if ((inname[0] = strdup(p)) == NULL)
25494957Sjmallett				err(1, NULL);
25594957Sjmallett			inlineno[0] = 1;
25694957Sjmallett			emitline();
2571590Srgrimes			macro();
25895060Sjmallett		    	release_input(infile);
2591590Srgrimes		}
2601590Srgrimes
2611590Srgrimes	if (*m4wraps) { 		/* anything for rundown ??   */
2621590Srgrimes		ilevel = 0;		/* in case m4wrap includes.. */
2631590Srgrimes		bufbase = bp = buf;	/* use the entire buffer   */
2641590Srgrimes		pbstr(m4wraps); 	/* user-defined wrapup act   */
2651590Srgrimes		macro();		/* last will and testament   */
2661590Srgrimes	}
2671590Srgrimes
2681590Srgrimes	if (active != stdout)
2691590Srgrimes		active = stdout;	/* reset output just in case */
27095060Sjmallett	for (n = 1; n < maxout; n++)	/* default wrap-up: undivert */
2711590Srgrimes		if (outfile[n] != NULL)
2721590Srgrimes			getdiv(n);
2731590Srgrimes					/* remove bitbucket if used  */
27495060Sjmallett	if (outfile[0] != NULL) {
27595060Sjmallett		(void) fclose(outfile[0]);
27695060Sjmallett	}
27795060Sjmallett
2781590Srgrimes	return 0;
2791590Srgrimes}
2801590Srgrimes
28195060Sjmallett/*
28295060Sjmallett * Look ahead for `token'.
28395060Sjmallett * (on input `t == token[0]')
28495060Sjmallett * Used for comment and quoting delimiters.
28595060Sjmallett * Returns 1 if `token' present; copied to output.
28695060Sjmallett *         0 if `token' not found; all characters pushed back
28795060Sjmallett */
28895060Sjmallettstatic int
28995887Sjmallettdo_look_ahead(int t, const char *token)
29095060Sjmallett{
29195060Sjmallett	int i;
2921590Srgrimes
29395060Sjmallett	assert((unsigned char)t == (unsigned char)token[0]);
29495060Sjmallett
29595060Sjmallett	for (i = 1; *++token; i++) {
29695060Sjmallett		t = gpbc();
29795060Sjmallett		if (t == EOF || (unsigned char)t != (unsigned char)*token) {
29895060Sjmallett			putback(t);
29995060Sjmallett			while (--i)
30095060Sjmallett				putback(*--token);
30195060Sjmallett			return 0;
30295060Sjmallett		}
30395060Sjmallett	}
30495060Sjmallett	return 1;
30595060Sjmallett}
30695060Sjmallett
30795060Sjmallett#define LOOK_AHEAD(t, token) (t != EOF && 		\
30895060Sjmallett    (unsigned char)(t)==(unsigned char)(token)[0] && 	\
30995060Sjmallett    do_look_ahead(t,token))
31095060Sjmallett
3111590Srgrimes/*
3121590Srgrimes * macro - the work horse..
3131590Srgrimes */
31495060Sjmallettstatic void
31595060Sjmallettmacro()
31695060Sjmallett{
31795060Sjmallett	char token[MAXTOK+1];
31895060Sjmallett	int t, l;
31995060Sjmallett	ndptr p;
32095060Sjmallett	int  nlpar;
3211590Srgrimes
3221590Srgrimes	cycle {
32395060Sjmallett		t = gpbc();
32495060Sjmallett		if (t == '_' || isalpha(t)) {
32595060Sjmallett			p = inspect(t, token);
32695060Sjmallett			if (p != nil)
32795060Sjmallett				putback(l = gpbc());
32895060Sjmallett			if (p == nil || (l != LPAREN &&
32995060Sjmallett			    (p->type & NEEDARGS) != 0))
33095060Sjmallett				outputstr(token);
3311590Srgrimes			else {
3321590Srgrimes		/*
3331590Srgrimes		 * real thing.. First build a call frame:
3341590Srgrimes		 */
3351590Srgrimes				pushf(fp);	/* previous call frm */
3361590Srgrimes				pushf(p->type); /* type of the call  */
3371590Srgrimes				pushf(0);	/* parenthesis level */
3381590Srgrimes				fp = sp;	/* new frame pointer */
3391590Srgrimes		/*
3401590Srgrimes		 * now push the string arguments:
3411590Srgrimes		 */
34295060Sjmallett				pushs1(p->defn);	/* defn string */
34395060Sjmallett				pushs1(p->name);	/* macro name  */
34495060Sjmallett				pushs(ep);	      	/* start next..*/
3451590Srgrimes
34695060Sjmallett				if (l != LPAREN && PARLEV == 0)  {
34795060Sjmallett				    /* no bracks  */
34895060Sjmallett					chrsave(EOS);
34995060Sjmallett
35095060Sjmallett					if (sp == STACKMAX)
35195060Sjmallett						errx(1, "internal stack overflow");
35295060Sjmallett					eval((const char **) mstack+fp+1, 2,
35395060Sjmallett					    CALTYP);
35495060Sjmallett
35595060Sjmallett					ep = PREVEP;	/* flush strspace */
35695060Sjmallett					sp = PREVSP;	/* previous sp..  */
35795060Sjmallett					fp = PREVFP;	/* rewind stack...*/
3581590Srgrimes				}
3591590Srgrimes			}
36095060Sjmallett		} else if (t == EOF) {
36195060Sjmallett			if (sp > -1) {
36295060Sjmallett				warnx( "unexpected end of input, unclosed parenthesis:");
36395060Sjmallett				dump_stack(paren, PARLEV);
36495060Sjmallett				exit(1);
36595060Sjmallett			}
3661590Srgrimes			if (ilevel <= 0)
3671590Srgrimes				break;			/* all done thanks.. */
36895060Sjmallett			release_input(infile+ilevel--);
36994957Sjmallett			free(inname[ilevel+1]);
3701590Srgrimes			bufbase = bbase[ilevel];
37194957Sjmallett			emitline();
3721590Srgrimes			continue;
3731590Srgrimes		}
3741590Srgrimes	/*
37595060Sjmallett	 * non-alpha token possibly seen..
3761590Srgrimes	 * [the order of else if .. stmts is important.]
3771590Srgrimes	 */
37895060Sjmallett		else if (LOOK_AHEAD(t,lquote)) {	/* strip quotes */
37995060Sjmallett			nlpar = 0;
38095060Sjmallett			record(quotes, nlpar++);
38195060Sjmallett			/*
38295060Sjmallett			 * Opening quote: scan forward until matching
38395060Sjmallett			 * closing quote has been found.
38495060Sjmallett			 */
3851590Srgrimes			do {
38695060Sjmallett
38795060Sjmallett				l = gpbc();
38895060Sjmallett				if (LOOK_AHEAD(l,rquote)) {
38995060Sjmallett					if (--nlpar > 0)
39095060Sjmallett						outputstr(rquote);
39195060Sjmallett				} else if (LOOK_AHEAD(l,lquote)) {
39295060Sjmallett					record(quotes, nlpar++);
39395060Sjmallett					outputstr(lquote);
39495060Sjmallett				} else if (l == EOF) {
39595060Sjmallett					if (nlpar == 1)
39695060Sjmallett						warnx("unclosed quote:");
3971590Srgrimes					else
39895060Sjmallett						warnx("%d unclosed quotes:", nlpar);
39995060Sjmallett					dump_stack(quotes, nlpar);
40095060Sjmallett					exit(1);
40195060Sjmallett				} else {
40295060Sjmallett					if (nlpar > 0) {
40395060Sjmallett						if (sp < 0)
40495060Sjmallett							putc(l, active);
40595060Sjmallett						else
40695060Sjmallett							CHRSAVE(l);
40795060Sjmallett					}
4081590Srgrimes				}
4091590Srgrimes			}
4101590Srgrimes			while (nlpar != 0);
4111590Srgrimes		}
4121590Srgrimes
41395060Sjmallett		else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
41495060Sjmallett			fputs(scommt, active);
41595060Sjmallett
41695060Sjmallett			for(;;) {
41795060Sjmallett				t = gpbc();
41895060Sjmallett				if (LOOK_AHEAD(t, ecommt)) {
41995060Sjmallett					fputs(ecommt, active);
42095060Sjmallett					break;
42195060Sjmallett				}
42295060Sjmallett				if (t == EOF)
42395060Sjmallett					break;
4241590Srgrimes				putc(t, active);
4251590Srgrimes			}
42695060Sjmallett		}
42795060Sjmallett
42895060Sjmallett		else if (sp < 0) {		/* not in a macro at all */
4291590Srgrimes			putc(t, active);	/* output directly..	 */
4301590Srgrimes		}
4311590Srgrimes
4321590Srgrimes		else switch(t) {
4331590Srgrimes
4341590Srgrimes		case LPAREN:
4351590Srgrimes			if (PARLEV > 0)
4361590Srgrimes				chrsave(t);
43795060Sjmallett			while (isspace(l = gpbc()))
4381590Srgrimes				;		/* skip blank, tab, nl.. */
4391590Srgrimes			putback(l);
44095060Sjmallett			record(paren, PARLEV++);
4411590Srgrimes			break;
4421590Srgrimes
4431590Srgrimes		case RPAREN:
4441590Srgrimes			if (--PARLEV > 0)
4451590Srgrimes				chrsave(t);
4461590Srgrimes			else {			/* end of argument list */
4471590Srgrimes				chrsave(EOS);
4481590Srgrimes
4491590Srgrimes				if (sp == STACKMAX)
45027625Scharnier					errx(1, "internal stack overflow");
4511590Srgrimes
45295060Sjmallett				eval((const char **) mstack+fp+1, sp-fp,
45395060Sjmallett				    CALTYP);
4541590Srgrimes
4551590Srgrimes				ep = PREVEP;	/* flush strspace */
4561590Srgrimes				sp = PREVSP;	/* previous sp..  */
4571590Srgrimes				fp = PREVFP;	/* rewind stack...*/
4581590Srgrimes			}
4591590Srgrimes			break;
4601590Srgrimes
4611590Srgrimes		case COMMA:
4621590Srgrimes			if (PARLEV == 1) {
4631590Srgrimes				chrsave(EOS);		/* new argument   */
46495060Sjmallett				while (isspace(l = gpbc()))
4651590Srgrimes					;
4661590Srgrimes				putback(l);
4671590Srgrimes				pushs(ep);
4681590Srgrimes			} else
4691590Srgrimes				chrsave(t);
4701590Srgrimes			break;
4711590Srgrimes
4721590Srgrimes		default:
47395060Sjmallett			if (LOOK_AHEAD(t, scommt)) {
47495095Sjmallett				char *pc;
47595095Sjmallett				for (pc = scommt; *pc; pc++)
47695095Sjmallett					chrsave(*pc);
47795060Sjmallett				for(;;) {
47895060Sjmallett					t = gpbc();
47995060Sjmallett					if (LOOK_AHEAD(t, ecommt)) {
48095095Sjmallett						for (pc = ecommt; *pc; pc++)
48195095Sjmallett							chrsave(*pc);
48295060Sjmallett						break;
48395060Sjmallett					}
48495060Sjmallett					if (t == EOF)
48595060Sjmallett					    break;
48695060Sjmallett					CHRSAVE(t);
48795060Sjmallett				}
48895060Sjmallett			} else
48995060Sjmallett				CHRSAVE(t);		/* stack the char */
4901590Srgrimes			break;
4911590Srgrimes		}
4921590Srgrimes	}
4931590Srgrimes}
4941590Srgrimes
49595060Sjmallett/*
49695060Sjmallett * output string directly, without pushing it for reparses.
49795060Sjmallett */
49895060Sjmallettvoid
49995887Sjmallettoutputstr(const char *s)
50095060Sjmallett{
50195060Sjmallett	if (sp < 0)
50295060Sjmallett		while (*s)
50395060Sjmallett			putc(*s++, active);
50495060Sjmallett	else
50595060Sjmallett		while (*s)
50695060Sjmallett			CHRSAVE(*s++);
50795060Sjmallett}
50895060Sjmallett
5091590Srgrimes/*
5101590Srgrimes * build an input token..
5111590Srgrimes * consider only those starting with _ or A-Za-z. This is a
5121590Srgrimes * combo with lookup to speed things up.
5131590Srgrimes */
51495060Sjmallettstatic ndptr
51595887Sjmallettinspect(int c, char *tp)
5161590Srgrimes{
51795060Sjmallett	char *name = tp;
51895060Sjmallett	char *etp = tp+MAXTOK;
51995060Sjmallett	ndptr p;
52095060Sjmallett	unsigned int h;
52195060Sjmallett
52295060Sjmallett	h = *tp++ = c;
5231590Srgrimes
52495060Sjmallett	while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
5251590Srgrimes		h = (h << 5) + h + (*tp++ = c);
52695060Sjmallett	if (c != EOF)
52795060Sjmallett		PUTBACK(c);
5281590Srgrimes	*tp = EOS;
52995060Sjmallett	/* token is too long, it won't match anything, but it can still
53095060Sjmallett	 * be output. */
53195060Sjmallett	if (tp == ep) {
53295060Sjmallett		outputstr(name);
53395060Sjmallett		while (isalnum(c = gpbc()) || c == '_') {
53495060Sjmallett			if (sp < 0)
53595060Sjmallett				putc(c, active);
53695060Sjmallett			else
53795060Sjmallett				CHRSAVE(c);
53895060Sjmallett		}
53995060Sjmallett		*name = EOS;
54095060Sjmallett		return nil;
54195060Sjmallett	}
5421590Srgrimes
54395060Sjmallett	for (p = hashtab[h % HASHSIZE]; p != nil; p = p->nxtptr)
54495060Sjmallett		if (h == p->hv && STREQ(name, p->name))
5451590Srgrimes			break;
5461590Srgrimes	return p;
5471590Srgrimes}
5481590Srgrimes
5491590Srgrimes/*
55095060Sjmallett * initkwds - initialise m4 keywords as fast as possible.
5511590Srgrimes * This very similar to install, but without certain overheads,
55295060Sjmallett * such as calling lookup. Malloc is not used for storing the
55395060Sjmallett * keyword strings, since we simply use the static pointers
5541590Srgrimes * within keywrds block.
5551590Srgrimes */
55695060Sjmallettstatic void
55795060Sjmallettinitkwds()
55895060Sjmallett{
55995060Sjmallett	size_t i;
56095060Sjmallett	unsigned int h;
56195060Sjmallett	ndptr p;
5621590Srgrimes
5631590Srgrimes	for (i = 0; i < MAXKEYS; i++) {
5641590Srgrimes		h = hash(keywrds[i].knam);
56595060Sjmallett		p = (ndptr) xalloc(sizeof(struct ndblock));
56695060Sjmallett		p->nxtptr = hashtab[h % HASHSIZE];
56795060Sjmallett		hashtab[h % HASHSIZE] = p;
56895060Sjmallett		p->name = xstrdup(keywrds[i].knam);
56995095Sjmallett		p->defn = xstrdup(null);
57095060Sjmallett		p->hv = h;
57195060Sjmallett		p->type = keywrds[i].ktyp & TYPEMASK;
57295060Sjmallett		if ((keywrds[i].ktyp & NOARGS) == 0)
57395060Sjmallett			p->type |= NEEDARGS;
5741590Srgrimes	}
5751590Srgrimes}
57694957Sjmallett
57795060Sjmallett/* Look up a builtin type, even if overridden by the user */
57895060Sjmallettint
57995887Sjmallettbuiltin_type(const char *key)
58095060Sjmallett{
58195060Sjmallett	int i;
58295060Sjmallett
58395060Sjmallett	for (i = 0; i != MAXKEYS; i++)
58495060Sjmallett		if (STREQ(keywrds[i].knam, key))
58595060Sjmallett			return keywrds[i].ktyp;
58695060Sjmallett	return -1;
58795060Sjmallett}
58895060Sjmallett
58995095Sjmallettconst char *
59095887Sjmallettbuiltin_realname(int n)
59195060Sjmallett{
59295060Sjmallett	int i;
59395060Sjmallett
59495060Sjmallett	for (i = 0; i != MAXKEYS; i++)
59595060Sjmallett		if (((keywrds[i].ktyp ^ n) & TYPEMASK) == 0)
59695060Sjmallett			return keywrds[i].knam;
59795060Sjmallett	return NULL;
59895060Sjmallett}
59995060Sjmallett
60095060Sjmallettstatic void
60195887Sjmallettrecord(struct position *t, int lev)
60295060Sjmallett{
60395060Sjmallett	if (lev < MAXRECORD) {
60495060Sjmallett		t[lev].name = CURRENT_NAME;
60595060Sjmallett		t[lev].line = CURRENT_LINE;
60695060Sjmallett	}
60795060Sjmallett}
60895060Sjmallett
60995060Sjmallettstatic void
61095887Sjmallettdump_stack(struct position *t, int lev)
61195060Sjmallett{
61295060Sjmallett	int i;
61395060Sjmallett
61495060Sjmallett	for (i = 0; i < lev; i++) {
61595060Sjmallett		if (i == MAXRECORD) {
61695060Sjmallett			fprintf(stderr, "   ...\n");
61795060Sjmallett			break;
61895060Sjmallett		}
61995060Sjmallett		fprintf(stderr, "   %s at line %lu\n",
62095060Sjmallett			t[i].name, t[i].line);
62195060Sjmallett	}
62295060Sjmallett}
62395060Sjmallett
62495060Sjmallett
62595060Sjmallettstatic void
62695060Sjmallettenlarge_stack()
62795060Sjmallett{
62895060Sjmallett	STACKMAX *= 2;
62995060Sjmallett	mstack = realloc(mstack, sizeof(stae) * STACKMAX);
63095060Sjmallett	sstack = realloc(sstack, STACKMAX);
63195060Sjmallett	if (mstack == NULL || sstack == NULL)
63295060Sjmallett		errx(1, "Evaluation stack overflow (%lu)",
63395060Sjmallett		    (unsigned long)STACKMAX);
63495060Sjmallett}
63595060Sjmallett
63694957Sjmallett/* Emit preprocessor #line directive if -s option used. */
63794957Sjmallettvoid
63894957Sjmallettemitline(void)
63994957Sjmallett{
64095060Sjmallett
64194957Sjmallett	if (synccpp)
64294957Sjmallett		fprintf(active, "#line %d \"%s\"\n", inlineno[ilevel],
64395060Sjmallett			inname[ilevel]);
64494957Sjmallett}
645