main.c revision 95060
195060Sjmallett/*	$OpenBSD: main.c,v 1.52 2002/02/16 21:27:48 millert 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");
4395060Sjmallett__SCCSID("@(#)main.c      8.1 (Berkeley) 6/6/93");
4495060Sjmallett__RCSID_SOURCE("$OpenBSD: main.c,v 1.52 2002/02/16 21:27:48 millert Exp $");
4595060Sjmallett__FBSDID("$FreeBSD: head/usr.bin/m4/main.c 95060 2002-04-19 17:26:21Z 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..	       */
831590Srgrimeschar *null = "";                /* as it says.. just a null..  */
841590Srgrimeschar *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 */
9094957Sjmallettint chscratch;			/* Scratch space for gpbc() macro */
911590Srgrimes
921590Srgrimesstruct keyblk keywrds[] = {	/* m4 keywords to be installed */
9395060Sjmallett	{ "include",      INCLTYPE },
9495060Sjmallett	{ "sinclude",     SINCTYPE },
9595060Sjmallett	{ "define",       DEFITYPE },
9695060Sjmallett	{ "defn",         DEFNTYPE },
9795060Sjmallett	{ "divert",       DIVRTYPE | NOARGS },
9895060Sjmallett	{ "expr",         EXPRTYPE },
9995060Sjmallett	{ "eval",         EXPRTYPE },
10095060Sjmallett	{ "substr",       SUBSTYPE },
10195060Sjmallett	{ "ifelse",       IFELTYPE },
10295060Sjmallett	{ "ifdef",        IFDFTYPE },
10395060Sjmallett	{ "len",          LENGTYPE },
10495060Sjmallett	{ "incr",         INCRTYPE },
10595060Sjmallett	{ "decr",         DECRTYPE },
10695060Sjmallett	{ "dnl",          DNLNTYPE | NOARGS },
10795060Sjmallett	{ "changequote",  CHNQTYPE | NOARGS },
10895060Sjmallett	{ "changecom",    CHNCTYPE | NOARGS },
10995060Sjmallett	{ "index",        INDXTYPE },
1101590Srgrimes#ifdef EXTENDED
11195060Sjmallett	{ "paste",        PASTTYPE },
11295060Sjmallett	{ "spaste",       SPASTYPE },
11395060Sjmallett    	/* Newer extensions, needed to handle gnu-m4 scripts */
11495060Sjmallett	{ "indir",        INDIRTYPE},
11595060Sjmallett	{ "builtin",      BUILTINTYPE},
11695060Sjmallett	{ "patsubst",	  PATSTYPE},
11795060Sjmallett	{ "regexp",	  REGEXPTYPE},
11895060Sjmallett	{ "esyscmd",	  ESYSCMDTYPE},
11995060Sjmallett	{ "__file__",	  FILENAMETYPE | NOARGS},
12095060Sjmallett	{ "__line__",	  LINETYPE | NOARGS},
1211590Srgrimes#endif
12295060Sjmallett	{ "popdef",       POPDTYPE },
12395060Sjmallett	{ "pushdef",      PUSDTYPE },
12495060Sjmallett	{ "dumpdef",      DUMPTYPE | NOARGS },
12595060Sjmallett	{ "shift",        SHIFTYPE | NOARGS },
12695060Sjmallett	{ "translit",     TRNLTYPE },
12795060Sjmallett	{ "undefine",     UNDFTYPE },
12895060Sjmallett	{ "undivert",     UNDVTYPE | NOARGS },
12995060Sjmallett	{ "divnum",       DIVNTYPE | NOARGS },
13095060Sjmallett	{ "maketemp",     MKTMTYPE },
13195060Sjmallett	{ "errprint",     ERRPTYPE | NOARGS },
13295060Sjmallett	{ "m4wrap",       M4WRTYPE | NOARGS },
13395060Sjmallett	{ "m4exit",       EXITTYPE | NOARGS },
13495060Sjmallett	{ "syscmd",       SYSCTYPE },
13595060Sjmallett	{ "sysval",       SYSVTYPE | NOARGS },
13695060Sjmallett	{ "traceon",	  TRACEONTYPE | NOARGS },
13795060Sjmallett	{ "traceoff",	  TRACEOFFTYPE | NOARGS },
1381590Srgrimes
13995060Sjmallett#if defined(unix) || defined(__unix__)
14095060Sjmallett	{ "unix",         SELFTYPE | NOARGS },
1411590Srgrimes#else
1421590Srgrimes#ifdef vms
14395060Sjmallett	{ "vms",          SELFTYPE | NOARGS },
1441590Srgrimes#endif
1451590Srgrimes#endif
1461590Srgrimes};
1471590Srgrimes
1481590Srgrimes#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
1491590Srgrimes
15095060Sjmallettextern int optind;
15195060Sjmallettextern char *optarg;
1521590Srgrimes
15395060Sjmallett#define MAXRECORD 50
15495060Sjmallettstatic struct position {
15595060Sjmallett	char *name;
15695060Sjmallett	unsigned long line;
15795060Sjmallett} quotes[MAXRECORD], paren[MAXRECORD];
15895060Sjmallett
15995060Sjmallettstatic void record(struct position *, int);
16095060Sjmallettstatic void dump_stack(struct position *, int);
16195060Sjmallett
16295060Sjmallettstatic void macro(void);
16395060Sjmallettstatic void initkwds(void);
16495060Sjmallettstatic ndptr inspect(int, char *);
16595060Sjmallettstatic int do_look_ahead(int, const char *);
16695060Sjmallett
16795060Sjmallettstatic void enlarge_stack(void);
16895060Sjmallett
16995060Sjmallettint main(int, char *[]);
17095060Sjmallett
1711590Srgrimesint
1721590Srgrimesmain(argc,argv)
1731590Srgrimes	int argc;
1741590Srgrimes	char *argv[];
1751590Srgrimes{
17695060Sjmallett	int c;
17795060Sjmallett	int n;
1781590Srgrimes	char *p;
1791590Srgrimes
18095060Sjmallett	traceout = stderr;
18195060Sjmallett
1821590Srgrimes	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1831590Srgrimes		signal(SIGINT, onintr);
1841590Srgrimes
1851590Srgrimes	initkwds();
18695060Sjmallett	initspaces();
18795060Sjmallett	STACKMAX = INITSTACKMAX;
1881590Srgrimes
18995060Sjmallett	mstack = (stae *)xalloc(sizeof(stae) * STACKMAX);
19095060Sjmallett	sstack = (char *)xalloc(STACKMAX);
19195060Sjmallett
19295060Sjmallett	maxout = 0;
19395060Sjmallett	outfile = NULL;
19495060Sjmallett	resizedivs(MAXOUT);
19595060Sjmallett
19695060Sjmallett	while ((c = getopt(argc, argv, "gst:d:D:U:o:I:")) != -1)
1971590Srgrimes		switch(c) {
1981590Srgrimes
1991590Srgrimes		case 'D':               /* define something..*/
2001590Srgrimes			for (p = optarg; *p; p++)
2011590Srgrimes				if (*p == '=')
2021590Srgrimes					break;
2031590Srgrimes			if (*p)
2041590Srgrimes				*p++ = EOS;
2051590Srgrimes			dodefine(optarg, p);
2061590Srgrimes			break;
20795060Sjmallett		case 'I':
20895060Sjmallett			addtoincludepath(optarg);
20995060Sjmallett			break;
2101590Srgrimes		case 'U':               /* undefine...       */
2111590Srgrimes			remhash(optarg, TOP);
2121590Srgrimes			break;
21395060Sjmallett		case 'g':
21495060Sjmallett			mimic_gnu = 1;
21595060Sjmallett			break;
21695060Sjmallett		case 'd':
21795060Sjmallett			set_trace_flags(optarg);
21895060Sjmallett			break;
21994957Sjmallett		case 's':
22094957Sjmallett			synccpp = 1;
22194957Sjmallett			break;
22295060Sjmallett		case 't':
22395060Sjmallett			mark_traced(optarg, 1);
22495060Sjmallett			break;
22595060Sjmallett		case 'o':
22695060Sjmallett			trace_file(optarg);
22795060Sjmallett                        break;
2281590Srgrimes		case '?':
2291590Srgrimes			usage();
2301590Srgrimes		}
2311590Srgrimes
2321590Srgrimes        argc -= optind;
2331590Srgrimes        argv += optind;
2341590Srgrimes
2351590Srgrimes	active = stdout;		/* default active output     */
2361590Srgrimes	bbase[0] = bufbase;
2371590Srgrimes        if (!argc) {
2381590Srgrimes 		sp = -1;		/* stack pointer initialized */
2391590Srgrimes		fp = 0; 		/* frame pointer initialized */
24095060Sjmallett		set_input(infile+0, stdin, "stdin");
24195060Sjmallett					/* default input (naturally) */
24294957Sjmallett		if ((inname[0] = strdup("-")) == NULL)
24394957Sjmallett			err(1, NULL);
24494957Sjmallett		inlineno[0] = 1;
24594957Sjmallett		emitline();
2461590Srgrimes		macro();
2471590Srgrimes	} else
2481590Srgrimes		for (; argc--; ++argv) {
2491590Srgrimes			p = *argv;
25095060Sjmallett			if (p[0] == '-' && p[1] == EOS)
25195060Sjmallett				set_input(infile, stdin, "stdin");
25295060Sjmallett			else if (fopen_trypath(infile, p) == NULL)
25327625Scharnier				err(1, "%s", p);
2541590Srgrimes			sp = -1;
25595060Sjmallett			fp = 0;
25694957Sjmallett			if ((inname[0] = strdup(p)) == NULL)
25794957Sjmallett				err(1, NULL);
25894957Sjmallett			inlineno[0] = 1;
25994957Sjmallett			emitline();
2601590Srgrimes			macro();
26195060Sjmallett		    	release_input(infile);
2621590Srgrimes		}
2631590Srgrimes
2641590Srgrimes	if (*m4wraps) { 		/* anything for rundown ??   */
2651590Srgrimes		ilevel = 0;		/* in case m4wrap includes.. */
2661590Srgrimes		bufbase = bp = buf;	/* use the entire buffer   */
2671590Srgrimes		pbstr(m4wraps); 	/* user-defined wrapup act   */
2681590Srgrimes		macro();		/* last will and testament   */
2691590Srgrimes	}
2701590Srgrimes
2711590Srgrimes	if (active != stdout)
2721590Srgrimes		active = stdout;	/* reset output just in case */
27395060Sjmallett	for (n = 1; n < maxout; n++)	/* default wrap-up: undivert */
2741590Srgrimes		if (outfile[n] != NULL)
2751590Srgrimes			getdiv(n);
2761590Srgrimes					/* remove bitbucket if used  */
27795060Sjmallett	if (outfile[0] != NULL) {
27895060Sjmallett		(void) fclose(outfile[0]);
27995060Sjmallett	}
28095060Sjmallett
2811590Srgrimes	return 0;
2821590Srgrimes}
2831590Srgrimes
28495060Sjmallett/*
28595060Sjmallett * Look ahead for `token'.
28695060Sjmallett * (on input `t == token[0]')
28795060Sjmallett * Used for comment and quoting delimiters.
28895060Sjmallett * Returns 1 if `token' present; copied to output.
28995060Sjmallett *         0 if `token' not found; all characters pushed back
29095060Sjmallett */
29195060Sjmallettstatic int
29295060Sjmallettdo_look_ahead(t, token)
29395060Sjmallett	int	t;
29495060Sjmallett	const char	*token;
29595060Sjmallett{
29695060Sjmallett	int i;
2971590Srgrimes
29895060Sjmallett	assert((unsigned char)t == (unsigned char)token[0]);
29995060Sjmallett
30095060Sjmallett	for (i = 1; *++token; i++) {
30195060Sjmallett		t = gpbc();
30295060Sjmallett		if (t == EOF || (unsigned char)t != (unsigned char)*token) {
30395060Sjmallett			putback(t);
30495060Sjmallett			while (--i)
30595060Sjmallett				putback(*--token);
30695060Sjmallett			return 0;
30795060Sjmallett		}
30895060Sjmallett	}
30995060Sjmallett	return 1;
31095060Sjmallett}
31195060Sjmallett
31295060Sjmallett#define LOOK_AHEAD(t, token) (t != EOF && 		\
31395060Sjmallett    (unsigned char)(t)==(unsigned char)(token)[0] && 	\
31495060Sjmallett    do_look_ahead(t,token))
31595060Sjmallett
3161590Srgrimes/*
3171590Srgrimes * macro - the work horse..
3181590Srgrimes */
31995060Sjmallettstatic void
32095060Sjmallettmacro()
32195060Sjmallett{
32295060Sjmallett	char token[MAXTOK+1];
32395060Sjmallett	int t, l;
32495060Sjmallett	ndptr p;
32595060Sjmallett	int  nlpar;
3261590Srgrimes
3271590Srgrimes	cycle {
32895060Sjmallett		t = gpbc();
32995060Sjmallett		if (t == '_' || isalpha(t)) {
33095060Sjmallett			p = inspect(t, token);
33195060Sjmallett			if (p != nil)
33295060Sjmallett				putback(l = gpbc());
33395060Sjmallett			if (p == nil || (l != LPAREN &&
33495060Sjmallett			    (p->type & NEEDARGS) != 0))
33595060Sjmallett				outputstr(token);
3361590Srgrimes			else {
3371590Srgrimes		/*
3381590Srgrimes		 * real thing.. First build a call frame:
3391590Srgrimes		 */
3401590Srgrimes				pushf(fp);	/* previous call frm */
3411590Srgrimes				pushf(p->type); /* type of the call  */
3421590Srgrimes				pushf(0);	/* parenthesis level */
3431590Srgrimes				fp = sp;	/* new frame pointer */
3441590Srgrimes		/*
3451590Srgrimes		 * now push the string arguments:
3461590Srgrimes		 */
34795060Sjmallett				pushs1(p->defn);	/* defn string */
34895060Sjmallett				pushs1(p->name);	/* macro name  */
34995060Sjmallett				pushs(ep);	      	/* start next..*/
3501590Srgrimes
35195060Sjmallett				if (l != LPAREN && PARLEV == 0)  {
35295060Sjmallett				    /* no bracks  */
35395060Sjmallett					chrsave(EOS);
35495060Sjmallett
35595060Sjmallett					if (sp == STACKMAX)
35695060Sjmallett						errx(1, "internal stack overflow");
35795060Sjmallett					eval((const char **) mstack+fp+1, 2,
35895060Sjmallett					    CALTYP);
35995060Sjmallett
36095060Sjmallett					ep = PREVEP;	/* flush strspace */
36195060Sjmallett					sp = PREVSP;	/* previous sp..  */
36295060Sjmallett					fp = PREVFP;	/* rewind stack...*/
3631590Srgrimes				}
3641590Srgrimes			}
36595060Sjmallett		} else if (t == EOF) {
36695060Sjmallett			if (sp > -1) {
36795060Sjmallett				warnx( "unexpected end of input, unclosed parenthesis:");
36895060Sjmallett				dump_stack(paren, PARLEV);
36995060Sjmallett				exit(1);
37095060Sjmallett			}
3711590Srgrimes			if (ilevel <= 0)
3721590Srgrimes				break;			/* all done thanks.. */
37395060Sjmallett			release_input(infile+ilevel--);
37494957Sjmallett			free(inname[ilevel+1]);
3751590Srgrimes			bufbase = bbase[ilevel];
37694957Sjmallett			emitline();
3771590Srgrimes			continue;
3781590Srgrimes		}
3791590Srgrimes	/*
38095060Sjmallett	 * non-alpha token possibly seen..
3811590Srgrimes	 * [the order of else if .. stmts is important.]
3821590Srgrimes	 */
38395060Sjmallett		else if (LOOK_AHEAD(t,lquote)) {	/* strip quotes */
38495060Sjmallett			nlpar = 0;
38595060Sjmallett			record(quotes, nlpar++);
38695060Sjmallett			/*
38795060Sjmallett			 * Opening quote: scan forward until matching
38895060Sjmallett			 * closing quote has been found.
38995060Sjmallett			 */
3901590Srgrimes			do {
39195060Sjmallett
39295060Sjmallett				l = gpbc();
39395060Sjmallett				if (LOOK_AHEAD(l,rquote)) {
39495060Sjmallett					if (--nlpar > 0)
39595060Sjmallett						outputstr(rquote);
39695060Sjmallett				} else if (LOOK_AHEAD(l,lquote)) {
39795060Sjmallett					record(quotes, nlpar++);
39895060Sjmallett					outputstr(lquote);
39995060Sjmallett				} else if (l == EOF) {
40095060Sjmallett					if (nlpar == 1)
40195060Sjmallett						warnx("unclosed quote:");
4021590Srgrimes					else
40395060Sjmallett						warnx("%d unclosed quotes:", nlpar);
40495060Sjmallett					dump_stack(quotes, nlpar);
40595060Sjmallett					exit(1);
40695060Sjmallett				} else {
40795060Sjmallett					if (nlpar > 0) {
40895060Sjmallett						if (sp < 0)
40995060Sjmallett							putc(l, active);
41095060Sjmallett						else
41195060Sjmallett							CHRSAVE(l);
41295060Sjmallett					}
4131590Srgrimes				}
4141590Srgrimes			}
4151590Srgrimes			while (nlpar != 0);
4161590Srgrimes		}
4171590Srgrimes
41895060Sjmallett		else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
41995060Sjmallett			fputs(scommt, active);
42095060Sjmallett
42195060Sjmallett			for(;;) {
42295060Sjmallett				t = gpbc();
42395060Sjmallett				if (LOOK_AHEAD(t, ecommt)) {
42495060Sjmallett					fputs(ecommt, active);
42595060Sjmallett					break;
42695060Sjmallett				}
42795060Sjmallett				if (t == EOF)
42895060Sjmallett					break;
4291590Srgrimes				putc(t, active);
4301590Srgrimes			}
43195060Sjmallett		}
43295060Sjmallett
43395060Sjmallett		else if (sp < 0) {		/* not in a macro at all */
4341590Srgrimes			putc(t, active);	/* output directly..	 */
4351590Srgrimes		}
4361590Srgrimes
4371590Srgrimes		else switch(t) {
4381590Srgrimes
4391590Srgrimes		case LPAREN:
4401590Srgrimes			if (PARLEV > 0)
4411590Srgrimes				chrsave(t);
44295060Sjmallett			while (isspace(l = gpbc()))
4431590Srgrimes				;		/* skip blank, tab, nl.. */
4441590Srgrimes			putback(l);
44595060Sjmallett			record(paren, PARLEV++);
4461590Srgrimes			break;
4471590Srgrimes
4481590Srgrimes		case RPAREN:
4491590Srgrimes			if (--PARLEV > 0)
4501590Srgrimes				chrsave(t);
4511590Srgrimes			else {			/* end of argument list */
4521590Srgrimes				chrsave(EOS);
4531590Srgrimes
4541590Srgrimes				if (sp == STACKMAX)
45527625Scharnier					errx(1, "internal stack overflow");
4561590Srgrimes
45795060Sjmallett				eval((const char **) mstack+fp+1, sp-fp,
45895060Sjmallett				    CALTYP);
4591590Srgrimes
4601590Srgrimes				ep = PREVEP;	/* flush strspace */
4611590Srgrimes				sp = PREVSP;	/* previous sp..  */
4621590Srgrimes				fp = PREVFP;	/* rewind stack...*/
4631590Srgrimes			}
4641590Srgrimes			break;
4651590Srgrimes
4661590Srgrimes		case COMMA:
4671590Srgrimes			if (PARLEV == 1) {
4681590Srgrimes				chrsave(EOS);		/* new argument   */
46995060Sjmallett				while (isspace(l = gpbc()))
4701590Srgrimes					;
4711590Srgrimes				putback(l);
4721590Srgrimes				pushs(ep);
4731590Srgrimes			} else
4741590Srgrimes				chrsave(t);
4751590Srgrimes			break;
4761590Srgrimes
4771590Srgrimes		default:
47895060Sjmallett			if (LOOK_AHEAD(t, scommt)) {
47995060Sjmallett				char *p;
48095060Sjmallett				for (p = scommt; *p; p++)
48195060Sjmallett					chrsave(*p);
48295060Sjmallett				for(;;) {
48395060Sjmallett					t = gpbc();
48495060Sjmallett					if (LOOK_AHEAD(t, ecommt)) {
48595060Sjmallett						for (p = ecommt; *p; p++)
48695060Sjmallett							chrsave(*p);
48795060Sjmallett						break;
48895060Sjmallett					}
48995060Sjmallett					if (t == EOF)
49095060Sjmallett					    break;
49195060Sjmallett					CHRSAVE(t);
49295060Sjmallett				}
49395060Sjmallett			} else
49495060Sjmallett				CHRSAVE(t);		/* stack the char */
4951590Srgrimes			break;
4961590Srgrimes		}
4971590Srgrimes	}
4981590Srgrimes}
4991590Srgrimes
50095060Sjmallett/*
50195060Sjmallett * output string directly, without pushing it for reparses.
50295060Sjmallett */
50395060Sjmallettvoid
50495060Sjmallettoutputstr(s)
50595060Sjmallett	const char *s;
50695060Sjmallett{
50795060Sjmallett	if (sp < 0)
50895060Sjmallett		while (*s)
50995060Sjmallett			putc(*s++, active);
51095060Sjmallett	else
51195060Sjmallett		while (*s)
51295060Sjmallett			CHRSAVE(*s++);
51395060Sjmallett}
51495060Sjmallett
5151590Srgrimes/*
5161590Srgrimes * build an input token..
5171590Srgrimes * consider only those starting with _ or A-Za-z. This is a
5181590Srgrimes * combo with lookup to speed things up.
5191590Srgrimes */
52095060Sjmallettstatic ndptr
52195060Sjmallettinspect(c, tp)
52295060Sjmallett	int c;
52395060Sjmallett	char *tp;
5241590Srgrimes{
52595060Sjmallett	char *name = tp;
52695060Sjmallett	char *etp = tp+MAXTOK;
52795060Sjmallett	ndptr p;
52895060Sjmallett	unsigned int h;
52995060Sjmallett
53095060Sjmallett	h = *tp++ = c;
5311590Srgrimes
53295060Sjmallett	while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
5331590Srgrimes		h = (h << 5) + h + (*tp++ = c);
53495060Sjmallett	if (c != EOF)
53595060Sjmallett		PUTBACK(c);
5361590Srgrimes	*tp = EOS;
53795060Sjmallett	/* token is too long, it won't match anything, but it can still
53895060Sjmallett	 * be output. */
53995060Sjmallett	if (tp == ep) {
54095060Sjmallett		outputstr(name);
54195060Sjmallett		while (isalnum(c = gpbc()) || c == '_') {
54295060Sjmallett			if (sp < 0)
54395060Sjmallett				putc(c, active);
54495060Sjmallett			else
54595060Sjmallett				CHRSAVE(c);
54695060Sjmallett		}
54795060Sjmallett		*name = EOS;
54895060Sjmallett		return nil;
54995060Sjmallett	}
5501590Srgrimes
55195060Sjmallett	for (p = hashtab[h % HASHSIZE]; p != nil; p = p->nxtptr)
55295060Sjmallett		if (h == p->hv && STREQ(name, p->name))
5531590Srgrimes			break;
5541590Srgrimes	return p;
5551590Srgrimes}
5561590Srgrimes
5571590Srgrimes/*
55895060Sjmallett * initkwds - initialise m4 keywords as fast as possible.
5591590Srgrimes * This very similar to install, but without certain overheads,
56095060Sjmallett * such as calling lookup. Malloc is not used for storing the
56195060Sjmallett * keyword strings, since we simply use the static pointers
5621590Srgrimes * within keywrds block.
5631590Srgrimes */
56495060Sjmallettstatic void
56595060Sjmallettinitkwds()
56695060Sjmallett{
56795060Sjmallett	size_t i;
56895060Sjmallett	unsigned int h;
56995060Sjmallett	ndptr p;
5701590Srgrimes
5711590Srgrimes	for (i = 0; i < MAXKEYS; i++) {
5721590Srgrimes		h = hash(keywrds[i].knam);
57395060Sjmallett		p = (ndptr) xalloc(sizeof(struct ndblock));
57495060Sjmallett		p->nxtptr = hashtab[h % HASHSIZE];
57595060Sjmallett		hashtab[h % HASHSIZE] = p;
57695060Sjmallett		p->name = xstrdup(keywrds[i].knam);
5771590Srgrimes		p->defn = null;
57895060Sjmallett		p->hv = h;
57995060Sjmallett		p->type = keywrds[i].ktyp & TYPEMASK;
58095060Sjmallett		if ((keywrds[i].ktyp & NOARGS) == 0)
58195060Sjmallett			p->type |= NEEDARGS;
5821590Srgrimes	}
5831590Srgrimes}
58494957Sjmallett
58595060Sjmallett/* Look up a builtin type, even if overridden by the user */
58695060Sjmallettint
58795060Sjmallettbuiltin_type(key)
58895060Sjmallett	const char *key;
58995060Sjmallett{
59095060Sjmallett	int i;
59195060Sjmallett
59295060Sjmallett	for (i = 0; i != MAXKEYS; i++)
59395060Sjmallett		if (STREQ(keywrds[i].knam, key))
59495060Sjmallett			return keywrds[i].ktyp;
59595060Sjmallett	return -1;
59695060Sjmallett}
59795060Sjmallett
59895060Sjmallettchar *
59995060Sjmallettbuiltin_realname(n)
60095060Sjmallett	int n;
60195060Sjmallett{
60295060Sjmallett	int i;
60395060Sjmallett
60495060Sjmallett	for (i = 0; i != MAXKEYS; i++)
60595060Sjmallett		if (((keywrds[i].ktyp ^ n) & TYPEMASK) == 0)
60695060Sjmallett			return keywrds[i].knam;
60795060Sjmallett	return NULL;
60895060Sjmallett}
60995060Sjmallett
61095060Sjmallettstatic void
61195060Sjmallettrecord(t, lev)
61295060Sjmallett	struct position *t;
61395060Sjmallett	int lev;
61495060Sjmallett{
61595060Sjmallett	if (lev < MAXRECORD) {
61695060Sjmallett		t[lev].name = CURRENT_NAME;
61795060Sjmallett		t[lev].line = CURRENT_LINE;
61895060Sjmallett	}
61995060Sjmallett}
62095060Sjmallett
62195060Sjmallettstatic void
62295060Sjmallettdump_stack(t, lev)
62395060Sjmallett	struct position *t;
62495060Sjmallett	int lev;
62595060Sjmallett{
62695060Sjmallett	int i;
62795060Sjmallett
62895060Sjmallett	for (i = 0; i < lev; i++) {
62995060Sjmallett		if (i == MAXRECORD) {
63095060Sjmallett			fprintf(stderr, "   ...\n");
63195060Sjmallett			break;
63295060Sjmallett		}
63395060Sjmallett		fprintf(stderr, "   %s at line %lu\n",
63495060Sjmallett			t[i].name, t[i].line);
63595060Sjmallett	}
63695060Sjmallett}
63795060Sjmallett
63895060Sjmallett
63995060Sjmallettstatic void
64095060Sjmallettenlarge_stack()
64195060Sjmallett{
64295060Sjmallett	STACKMAX *= 2;
64395060Sjmallett	mstack = realloc(mstack, sizeof(stae) * STACKMAX);
64495060Sjmallett	sstack = realloc(sstack, STACKMAX);
64595060Sjmallett	if (mstack == NULL || sstack == NULL)
64695060Sjmallett		errx(1, "Evaluation stack overflow (%lu)",
64795060Sjmallett		    (unsigned long)STACKMAX);
64895060Sjmallett}
64995060Sjmallett
65094957Sjmallett/* Emit preprocessor #line directive if -s option used. */
65194957Sjmallettvoid
65294957Sjmallettemitline(void)
65394957Sjmallett{
65495060Sjmallett
65594957Sjmallett	if (synccpp)
65694957Sjmallett		fprintf(active, "#line %d \"%s\"\n", inlineno[ilevel],
65795060Sjmallett			inname[ilevel]);
65894957Sjmallett}
659