Home | History | Annotate | Line # | Download | only in m4
main.c revision 1.47
      1 /*	$OpenBSD: main.c,v 1.77 2009/10/14 17:19:47 sthen Exp $	*/
      2 /*	$NetBSD: main.c,v 1.47 2019/03/26 15:00:34 christos Exp $	*/
      3 
      4 /*-
      5  * Copyright (c) 1989, 1993
      6  *	The Regents of the University of California.  All rights reserved.
      7  *
      8  * This code is derived from software contributed to Berkeley by
      9  * Ozan Yigit at York University.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  * 3. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 /*
     37  * main.c
     38  * Facility: m4 macro processor
     39  * by: oz
     40  */
     41 #if HAVE_NBTOOL_CONFIG_H
     42 #include "nbtool_config.h"
     43 #endif
     44 #include <sys/cdefs.h>
     45 __RCSID("$NetBSD: main.c,v 1.47 2019/03/26 15:00:34 christos Exp $");
     46 #include <assert.h>
     47 #include <signal.h>
     48 #include <getopt.h>
     49 #include <err.h>
     50 #include <errno.h>
     51 #include <unistd.h>
     52 #include <stdio.h>
     53 #include <ctype.h>
     54 #include <string.h>
     55 #include <stddef.h>
     56 #include <stdint.h>
     57 #include <stdlib.h>
     58 #include <ohash.h>
     59 #include "mdef.h"
     60 #include "stdd.h"
     61 #include "extern.h"
     62 #include "pathnames.h"
     63 
     64 ndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
     65 stae *mstack;		 	/* stack of m4 machine         */
     66 char *sstack;		 	/* shadow stack, for string space extension */
     67 static size_t STACKMAX;		/* current maximum size of stack */
     68 int sp; 			/* current m4  stack pointer   */
     69 int fp; 			/* m4 call frame pointer       */
     70 struct input_file infile[MAXINP];/* input file stack (0=stdin)  */
     71 FILE **outfile;			/* diversion array(0=bitbucket)*/
     72 int maxout;
     73 FILE *active;			/* active output file pointer  */
     74 int ilevel = 0; 		/* input file stack pointer    */
     75 int oindex = 0; 		/* diversion index..	       */
     76 const char *null = "";          /* as it says.. just a null..  */
     77 char **m4wraps = NULL;		/* m4wraps array.     	       */
     78 int maxwraps = 0;		/* size of m4wraps array       */
     79 int wrapindex = 0;		/* current offset in m4wraps   */
     80 char lquote[MAXCCHARS+1] = {LQUOTE};	/* left quote character  (`)   */
     81 char rquote[MAXCCHARS+1] = {RQUOTE};	/* right quote character (')   */
     82 char scommt[MAXCCHARS+1] = {SCOMMT};	/* start character for comment */
     83 char ecommt[MAXCCHARS+1] = {ECOMMT};	/* end character for comment   */
     84 int  synch_lines = 0;		/* line synchronisation for C preprocessor */
     85 int  prefix_builtins = 0;	/* -P option to prefix builtin keywords */
     86 int  fatal_warnings = 0;	/* -E option to exit on warnings */
     87 int  quiet = 0;			/* -Q option to silence warnings */
     88 int  nesting_limit = -1;	/* -L for nesting limit */
     89 const char *freeze = NULL;	/* -F to freeze state */
     90 const char *reload = NULL;	/* -R to reload state */
     91 #ifndef REAL_FREEZE
     92 FILE *freezef = NULL;
     93 int thawing = 0;
     94 #endif
     95 
     96 struct keyblk {
     97         const char *knam;	/* keyword name */
     98         int	ktyp;           /* keyword type */
     99 };
    100 
    101 struct keyblk keywrds[] = {	/* m4 keywords to be installed */
    102 	{ "include",      INCLTYPE },
    103 	{ "sinclude",     SINCTYPE },
    104 	{ "define",       DEFITYPE },
    105 	{ "defn",         DEFNTYPE },
    106 	{ "divert",       DIVRTYPE | NOARGS },
    107 	{ "expr",         EXPRTYPE },
    108 	{ "eval",         EXPRTYPE },
    109 	{ "substr",       SUBSTYPE },
    110 	{ "ifelse",       IFELTYPE },
    111 	{ "ifdef",        IFDFTYPE },
    112 	{ "len",          LENGTYPE },
    113 	{ "incr",         INCRTYPE },
    114 	{ "decr",         DECRTYPE },
    115 	{ "dnl",          DNLNTYPE | NOARGS },
    116 	{ "changequote",  CHNQTYPE | NOARGS },
    117 	{ "changecom",    CHNCTYPE | NOARGS },
    118 	{ "index",        INDXTYPE },
    119 #ifdef EXTENDED
    120 	{ "paste",        PASTTYPE },
    121 	{ "spaste",       SPASTYPE },
    122     	/* Newer extensions, needed to handle gnu-m4 scripts */
    123 	{ "indir",        INDIRTYPE},
    124 	{ "builtin",      BUILTINTYPE},
    125 	{ "patsubst",	  PATSTYPE},
    126 	{ "regexp",	  REGEXPTYPE},
    127 	{ "esyscmd",	  ESYSCMDTYPE},
    128 	{ "__file__",	  FILENAMETYPE | NOARGS},
    129 	{ "__line__",	  LINETYPE | NOARGS},
    130 #endif
    131 	{ "popdef",       POPDTYPE },
    132 	{ "pushdef",      PUSDTYPE },
    133 	{ "dumpdef",      DUMPTYPE | NOARGS },
    134 	{ "shift",        SHIFTYPE | NOARGS },
    135 	{ "translit",     TRNLTYPE },
    136 	{ "undefine",     UNDFTYPE },
    137 	{ "undivert",     UNDVTYPE | NOARGS },
    138 	{ "divnum",       DIVNTYPE | NOARGS },
    139 	{ "maketemp",     MKTMTYPE },
    140 	{ "errprint",     ERRPTYPE | NOARGS },
    141 	{ "m4wrap",       M4WRTYPE | NOARGS },
    142 	{ "m4exit",       EXITTYPE | NOARGS },
    143 	{ "syscmd",       SYSCTYPE },
    144 	{ "sysval",       SYSVTYPE | NOARGS },
    145 	{ "traceon",	  TRACEONTYPE | NOARGS },
    146 	{ "traceoff",	  TRACEOFFTYPE | NOARGS },
    147 
    148 #if defined(unix) || defined(__unix__)
    149 	{ "unix",         SELFTYPE | NOARGS },
    150 #else
    151 #ifdef vms
    152 	{ "vms",          SELFTYPE | NOARGS },
    153 #endif
    154 #endif
    155 };
    156 
    157 #define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
    158 
    159 #define MAXRECORD 50
    160 static struct position {
    161 	char *name;
    162 	unsigned long line;
    163 } quotes[MAXRECORD], paren[MAXRECORD];
    164 
    165 static void record(struct position *, int);
    166 static void dump_stack(struct position *, int);
    167 
    168 static void macro(void);
    169 static void initkwds(void);
    170 static ndptr inspect(int, char *);
    171 static int do_look_ahead(int, const char *);
    172 static void reallyoutputstr(const char *);
    173 static void reallyputchar(int);
    174 
    175 static void enlarge_stack(void);
    176 static void help(void);
    177 
    178 static void
    179 usage(FILE *f)
    180 {
    181 	fprintf(f, "Usage: %s [-EGgiPQsv] [-Dname[=value]] [-d flags] "
    182 	    "[-I dirname] [-o filename] [-L limit]\n"
    183 	    "\t[-t macro] [-Uname] [file ...]\n", getprogname());
    184 }
    185 
    186 __dead static void
    187 onintr(int signo)
    188 {
    189 	char intrmessage[] = "m4: interrupted.\n";
    190 	write(STDERR_FILENO, intrmessage, sizeof(intrmessage)-1);
    191 	_exit(1);
    192 }
    193 
    194 #define OPT_HELP 1
    195 
    196 struct option longopts[] = {
    197 	{ "debug",		optional_argument,	0, 'd' },
    198 	{ "define",		required_argument,	0, 'D' },
    199 	{ "error-output",	required_argument,	0, 'e' },
    200 	{ "fatal-warnings",	no_argument,		0, 'E' },
    201 	{ "freeze-state",	required_argument,	0, 'F' },
    202 	{ "gnu",		no_argument,		0, 'g' },
    203 	{ "help",		no_argument,		0, OPT_HELP },
    204 	{ "include",		required_argument,	0, 'I' },
    205 	{ "interactive",	no_argument,		0, 'i' },
    206 	{ "nesting-limit",	required_argument,	0, 'L' },
    207 	{ "prefix-builtins",	no_argument,		0, 'P' },
    208 	{ "quiet",		no_argument,		0, 'Q' },
    209 	{ "reload-state",	required_argument,	0, 'R' },
    210 	{ "silent",		no_argument,		0, 'Q' },
    211 	{ "synclines",		no_argument,		0, 's' },
    212 	{ "trace",		required_argument,	0, 't' },
    213 	{ "traditional",	no_argument,		0, 'G' },
    214 	{ "undefine",		required_argument,	0, 'U' },
    215 	{ "version",		no_argument,		0, 'v' },
    216 #ifdef notyet
    217 	{ "arglength",		required_argument,	0, 'l' },
    218 	{ "debugfile",		optional_argument, 	0, OPT_DEBUGFILE },
    219 	{ "hashsize",		required_argument,	0, 'H' },
    220 	{ "warn-macro-sequence",optional_argument,	0, OPT_WARN_SEQUENCE },
    221 #endif
    222 	{ 0,			0,			0, 0 },
    223 };
    224 
    225 int
    226 main(int argc, char *argv[])
    227 {
    228 	int c;
    229 	int n;
    230 	char *p;
    231 	FILE *sfp;
    232 
    233 	setprogname(argv[0]);
    234 
    235 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
    236 		signal(SIGINT, onintr);
    237 
    238 	init_macros();
    239 	initspaces();
    240 	STACKMAX = INITSTACKMAX;
    241 
    242 	mstack = (stae *)xalloc(sizeof(stae) * STACKMAX, NULL);
    243 	sstack = (char *)xalloc(STACKMAX, NULL);
    244 
    245 	maxout = 0;
    246 	outfile = NULL;
    247 	resizedivs(MAXOUT);
    248 
    249 	while ((c = getopt_long(argc, argv, "D:d:e:EF:GgI:iL:o:PR:Qst:U:v",
    250 	    longopts, NULL)) != -1)
    251 		switch(c) {
    252 		case 'D':               /* define something..*/
    253 			for (p = optarg; *p; p++)
    254 				if (*p == '=')
    255 					break;
    256 			if (*p)
    257 				*p++ = EOS;
    258 			dodefine(optarg, p);
    259 			break;
    260 		case 'd':
    261 			set_trace_flags(optarg);
    262 			break;
    263 		case 'E':
    264 			fatal_warnings++;
    265 			break;
    266 		case 'e':
    267 			/*
    268 			 * Don't use freopen here because if it fails
    269 			 * we lose stderr, instead trash it.
    270 			 */
    271 			if ((sfp = fopen(optarg, "w+")) == NULL) {
    272 				warn("Can't redirect errors to `%s'", optarg);
    273 				break;
    274 			}
    275 			fclose(stderr);
    276 			memcpy(stderr, sfp, sizeof(*sfp));
    277 			break;
    278 		case 'F':
    279 			freeze = optarg;
    280 #ifndef REAL_FREEZE
    281 			if ((freezef = fopen(freeze, "w")) == NULL)
    282 				err(EXIT_FAILURE, "Can't open `%s'", freeze);
    283 #endif
    284 			break;
    285 		case 'I':
    286 			addtoincludepath(optarg);
    287 			break;
    288 		case 'i':
    289 			setvbuf(stdout, NULL, _IONBF, 0);
    290 			signal(SIGINT, SIG_IGN);
    291 			break;
    292 		case 'G':
    293 			mimic_gnu = 0;
    294 			break;
    295 		case 'g':
    296 			mimic_gnu = 1;
    297 			break;
    298 		case 'L':
    299 			nesting_limit = atoi(optarg);
    300 			break;
    301 		case 'o':
    302 			trace_file(optarg);
    303                         break;
    304 		case 'P':
    305 			prefix_builtins = 1;
    306 			break;
    307 		case 'Q':
    308 			quiet++;
    309 			break;
    310 		case 'R':
    311 			reload = optarg;
    312 			break;
    313 		case 's':
    314 			synch_lines = 1;
    315 			break;
    316 		case 't':
    317 			mark_traced(optarg, 1);
    318 			break;
    319 		case 'U':               /* undefine...       */
    320 			macro_popdef(optarg);
    321 			break;
    322 		case 'v':
    323 			fprintf(stderr, "%s version %d\n", getprogname(),
    324 			    VERSION);
    325 			return EXIT_SUCCESS;
    326 		case OPT_HELP:
    327 			help();
    328 			return EXIT_SUCCESS;
    329 		case '?':
    330 		default:
    331 			usage(stderr);
    332 			return EXIT_FAILURE;
    333 		}
    334 
    335 #ifdef REDIRECT
    336 	/*
    337 	 * This is meant only for debugging; it makes all output
    338 	 * go to a known file, even if the command line options
    339 	 * send it elsewhere. It should not be turned of in production code.
    340 	 */
    341 	if (freopen("/tmp/m4", "w+", stderr) == NULL)
    342 		err(EXIT_FAILURE, "Can't redirect errors to `%s'",
    343 		    "/tmp/m4");
    344 #endif
    345         argc -= optind;
    346         argv += optind;
    347 
    348 
    349 	initkwds();
    350 	if (mimic_gnu)
    351 		setup_builtin("format", FORMATTYPE);
    352 
    353 	active = stdout;		/* default active output     */
    354 	bbase[0] = bufbase;
    355 
    356 	if (reload) {
    357 #ifdef REAL_FREEZE
    358 		thaw_state(reload);
    359 #else
    360 		if (fopen_trypath(infile, reload) == NULL)
    361 			err(1, "Can't open `%s'", reload);
    362 		sp = -1;
    363 		fp = 0;
    364 		thawing = 1;
    365 		macro();
    366 		thawing = 0;
    367 		release_input(infile);
    368 #endif
    369 	}
    370 
    371         if (!argc) {
    372  		sp = -1;		/* stack pointer initialized */
    373 		fp = 0; 		/* frame pointer initialized */
    374 		set_input(infile+0, stdin, "stdin");
    375 					/* default input (naturally) */
    376 		macro();
    377 	} else
    378 		for (; argc--; ++argv) {
    379 			p = *argv;
    380 			if (p[0] == '-' && p[1] == EOS)
    381 				set_input(infile, stdin, "stdin");
    382 			else if (fopen_trypath(infile, p) == NULL)
    383 				err(1, "%s", p);
    384 			sp = -1;
    385 			fp = 0;
    386 			macro();
    387 		    	release_input(infile);
    388 		}
    389 
    390 	if (wrapindex) {
    391 		int i;
    392 
    393 		ilevel = 0;		/* in case m4wrap includes.. */
    394 		bufbase = bp = buf;	/* use the entire buffer   */
    395 		if (mimic_gnu) {
    396 			while (wrapindex != 0) {
    397 				for (i = 0; i < wrapindex; i++)
    398 					pbstr(m4wraps[i]);
    399 				wrapindex =0;
    400 				macro();
    401 			}
    402 		} else {
    403 			for (i = 0; i < wrapindex; i++) {
    404 				pbstr(m4wraps[i]);
    405 				macro();
    406 		    	}
    407 		}
    408 	}
    409 
    410 	if (active != stdout)
    411 		active = stdout;	/* reset output just in case */
    412 	for (n = 1; n < maxout; n++)	/* default wrap-up: undivert */
    413 		if (outfile[n] != NULL)
    414 			getdiv(n);
    415 					/* remove bitbucket if used  */
    416 	if (outfile[0] != NULL) {
    417 		(void) fclose(outfile[0]);
    418 	}
    419 
    420 #ifdef REAL_FREEZE
    421 	if (freeze)
    422 		freeze_state(freeze);
    423 #else
    424 	if (freezef)
    425 		fclose(freezef);
    426 #endif
    427 
    428 	return 0;
    429 }
    430 
    431 /*
    432  * Look ahead for `token'.
    433  * (on input `t == token[0]')
    434  * Used for comment and quoting delimiters.
    435  * Returns 1 if `token' present; copied to output.
    436  *         0 if `token' not found; all characters pushed back
    437  */
    438 static int
    439 do_look_ahead(int t, const char *token)
    440 {
    441 	int i;
    442 
    443 	assert((unsigned char)t == (unsigned char)token[0]);
    444 
    445 	for (i = 1; *++token; i++) {
    446 		t = gpbc();
    447 		if (t == EOF || (unsigned char)t != (unsigned char)*token) {
    448 			pushback(t);
    449 			while (--i)
    450 				pushback(*--token);
    451 			return 0;
    452 		}
    453 	}
    454 	return 1;
    455 }
    456 
    457 #define LOOK_AHEAD(t, token) (t != EOF && 		\
    458     (unsigned char)(t)==(unsigned char)(token)[0] && 	\
    459     do_look_ahead(t,token))
    460 
    461 /*
    462  * macro - the work horse..
    463  */
    464 static void
    465 macro(void)
    466 {
    467 	char token[MAXTOK+1];
    468 	int t, l;
    469 	ndptr p;
    470 	int  nlpar;
    471 
    472 	cycle {
    473 		t = gpbc();
    474 
    475 		if (LOOK_AHEAD(t,lquote)) {	/* strip quotes */
    476 			nlpar = 0;
    477 			record(quotes, nlpar++);
    478 			/*
    479 			 * Opening quote: scan forward until matching
    480 			 * closing quote has been found.
    481 			 */
    482 			do {
    483 
    484 				l = gpbc();
    485 				if (LOOK_AHEAD(l,rquote)) {
    486 					if (--nlpar > 0)
    487 						outputstr(rquote);
    488 				} else if (LOOK_AHEAD(l,lquote)) {
    489 					record(quotes, nlpar++);
    490 					outputstr(lquote);
    491 				} else if (l == EOF) {
    492 					if (!quiet) {
    493 						if (nlpar == 1)
    494 							warnx("unclosed quote:");
    495 						else
    496 							warnx(
    497 							    "%d unclosed quotes:",
    498 							    nlpar);
    499 						dump_stack(quotes, nlpar);
    500 					}
    501 					exit(EXIT_FAILURE);
    502 				} else {
    503 					if (nlpar > 0) {
    504 						if (sp < 0)
    505 							reallyputchar(l);
    506 						else
    507 							CHRSAVE(l);
    508 					}
    509 				}
    510 			}
    511 			while (nlpar != 0);
    512 		} else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
    513 			reallyoutputstr(scommt);
    514 
    515 			for(;;) {
    516 				t = gpbc();
    517 				if (LOOK_AHEAD(t, ecommt)) {
    518 					reallyoutputstr(ecommt);
    519 					break;
    520 				}
    521 				if (t == EOF)
    522 					break;
    523 				reallyputchar(t);
    524 			}
    525 		} else if (t == '_' || isalpha(t)) {
    526 			p = inspect(t, token);
    527 			if (p != NULL)
    528 				pushback(l = gpbc());
    529 			if (p == NULL || (l != LPAREN &&
    530 			    (macro_getdef(p)->type & NEEDARGS) != 0))
    531 				outputstr(token);
    532 			else {
    533 		/*
    534 		 * real thing.. First build a call frame:
    535 		 */
    536 				pushf(fp);	/* previous call frm */
    537 				pushf(macro_getdef(p)->type); /* type of the call  */
    538 				pushf(is_traced(p));
    539 				pushf(0);	/* parenthesis level */
    540 				fp = sp;	/* new frame pointer */
    541 		/*
    542 		 * now push the string arguments:
    543 		 */
    544 				pushs1(macro_getdef(p)->defn);	/* defn string */
    545 				pushs1((char *)macro_name(p));	/* macro name  */
    546 				pushs(ep);	      	/* start next..*/
    547 
    548 				if (l != LPAREN && PARLEV == 0)  {
    549 				    /* no bracks  */
    550 					chrsave(EOS);
    551 
    552 					if ((size_t)sp == STACKMAX)
    553 						errx(1, "internal stack overflow");
    554 					eval((const char **) mstack+fp+1, 2,
    555 					    CALTYP, TRACESTATUS);
    556 
    557 					ep = PREVEP;	/* flush strspace */
    558 					sp = PREVSP;	/* previous sp..  */
    559 					fp = PREVFP;	/* rewind stack...*/
    560 				}
    561 			}
    562 		} else if (t == EOF) {
    563 			if (sp > -1 && ilevel <= 0) {
    564 				if (!quiet) {
    565 					warnx("unexpected end of input, "
    566 					    "unclosed parenthesis:");
    567 					dump_stack(paren, PARLEV);
    568 				}
    569 				exit(EXIT_FAILURE);
    570 			}
    571 			if (ilevel <= 0)
    572 				break;			/* all done thanks.. */
    573 			release_input(infile+ilevel--);
    574 			emit_synchline();
    575 			bufbase = bbase[ilevel];
    576 			continue;
    577 		} else if (sp < 0) {		/* not in a macro at all */
    578 			reallyputchar(t);	/* output directly..	 */
    579 		}
    580 
    581 		else switch(t) {
    582 
    583 		case LPAREN:
    584 			if (PARLEV > 0)
    585 				chrsave(t);
    586 			while (isspace(l = gpbc())) /* skip blank, tab, nl.. */
    587 				if (PARLEV > 0)
    588 					chrsave(l);
    589 			pushback(l);
    590 			record(paren, PARLEV++);
    591 			break;
    592 
    593 		case RPAREN:
    594 			if (--PARLEV > 0)
    595 				chrsave(t);
    596 			else {			/* end of argument list */
    597 				chrsave(EOS);
    598 
    599 				if ((size_t)sp == STACKMAX)
    600 					errx(1, "internal stack overflow");
    601 
    602 				eval((const char **) mstack+fp+1, sp-fp,
    603 				    CALTYP, TRACESTATUS);
    604 
    605 				ep = PREVEP;	/* flush strspace */
    606 				sp = PREVSP;	/* previous sp..  */
    607 				fp = PREVFP;	/* rewind stack...*/
    608 			}
    609 			break;
    610 
    611 		case COMMA:
    612 			if (PARLEV == 1) {
    613 				chrsave(EOS);		/* new argument   */
    614 				while (isspace(l = gpbc()))
    615 					;
    616 				pushback(l);
    617 				pushs(ep);
    618 			} else
    619 				chrsave(t);
    620 			break;
    621 
    622 		default:
    623 			if (LOOK_AHEAD(t, scommt)) {
    624 				char *q;
    625 				for (q = scommt; *q; q++)
    626 					chrsave(*q);
    627 				for(;;) {
    628 					t = gpbc();
    629 					if (LOOK_AHEAD(t, ecommt)) {
    630 						for (q = ecommt; *q; q++)
    631 							chrsave(*q);
    632 						break;
    633 					}
    634 					if (t == EOF)
    635 					    break;
    636 					CHRSAVE(t);
    637 				}
    638 			} else
    639 				CHRSAVE(t);		/* stack the char */
    640 			break;
    641 		}
    642 	}
    643 }
    644 
    645 /*
    646  * output string directly, without pushing it for reparses.
    647  */
    648 void
    649 outputstr(const char *s)
    650 {
    651 	if (sp < 0)
    652 		reallyoutputstr(s);
    653 	else
    654 		while (*s)
    655 			CHRSAVE(*s++);
    656 }
    657 
    658 void
    659 reallyoutputstr(const char *s)
    660 {
    661 	if (synch_lines) {
    662 		while (*s) {
    663 			fputc(*s, active);
    664 			if (*s++ == '\n') {
    665 				infile[ilevel].synch_lineno++;
    666 				if (infile[ilevel].synch_lineno !=
    667 				    infile[ilevel].lineno)
    668 					do_emit_synchline();
    669 			}
    670 		}
    671 	} else
    672 		fputs(s, active);
    673 }
    674 
    675 void
    676 reallyputchar(int c)
    677 {
    678 	putc(c, active);
    679 	if (synch_lines && c == '\n') {
    680 		infile[ilevel].synch_lineno++;
    681 		if (infile[ilevel].synch_lineno != infile[ilevel].lineno)
    682 			do_emit_synchline();
    683 	}
    684 }
    685 
    686 /*
    687  * build an input token..
    688  * consider only those starting with _ or A-Za-z.
    689  */
    690 static ndptr
    691 inspect(int c, char *tp)
    692 {
    693 	char *name = tp;
    694 	char *etp = tp+MAXTOK;
    695 	ndptr p;
    696 
    697 	*tp++ = c;
    698 
    699 	while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
    700 		*tp++ = c;
    701 	if (c != EOF)
    702 		PUSHBACK(c);
    703 	*tp = EOS;
    704 	/* token is too long, it won't match anything, but it can still
    705 	 * be output. */
    706 	if (tp == ep) {
    707 		outputstr(name);
    708 		while (isalnum(c = gpbc()) || c == '_') {
    709 			if (sp < 0)
    710 				reallyputchar(c);
    711 			else
    712 				CHRSAVE(c);
    713 		}
    714 		*name = EOS;
    715 		return NULL;
    716 	}
    717 
    718 	p = ohash_find(&macros, ohash_qlookupi(&macros, name, (void *)&tp));
    719 	if (p == NULL)
    720 		return NULL;
    721 	if (macro_getdef(p) == NULL)
    722 		return NULL;
    723 	return p;
    724 }
    725 
    726 /*
    727  * initkwds - initialise m4 keywords as fast as possible.
    728  * This very similar to install, but without certain overheads,
    729  * such as calling lookup. Malloc is not used for storing the
    730  * keyword strings, since we simply use the static pointers
    731  * within keywrds block.
    732  */
    733 static void
    734 initkwds(void)
    735 {
    736 	unsigned int type;
    737 	size_t i;
    738 
    739 	for (i = 0; i < MAXKEYS; i++) {
    740 		type = keywrds[i].ktyp;
    741 		if ((keywrds[i].ktyp & NOARGS) == 0)
    742 			type |= NEEDARGS;
    743 		setup_builtin(keywrds[i].knam, type);
    744 	}
    745 }
    746 
    747 static void
    748 record(struct position *t, int lev)
    749 {
    750 	if (lev < MAXRECORD) {
    751 		t[lev].name = CURRENT_NAME;
    752 		t[lev].line = CURRENT_LINE;
    753 	}
    754 }
    755 
    756 static void
    757 dump_stack(struct position *t, int lev)
    758 {
    759 	int i;
    760 
    761 	for (i = 0; i < lev; i++) {
    762 		if (i == MAXRECORD) {
    763 			fprintf(stderr, "   ...\n");
    764 			break;
    765 		}
    766 		fprintf(stderr, "   %s at line %lu\n",
    767 			t[i].name, t[i].line);
    768 	}
    769 }
    770 
    771 
    772 static void
    773 enlarge_stack(void)
    774 {
    775 	STACKMAX += STACKMAX/2;
    776 	mstack = xrealloc(mstack, sizeof(stae) * STACKMAX,
    777 	    "Evaluation stack overflow (%lu)",
    778 	    (unsigned long)STACKMAX);
    779 	sstack = xrealloc(sstack, STACKMAX,
    780 	    "Evaluation stack overflow (%lu)",
    781 	    (unsigned long)STACKMAX);
    782 }
    783 
    784 static const struct {
    785 	const char *n;
    786 	const char *d;
    787 } nd [] = {
    788 { "-d, --debug[=flags]",	"set debug flags" },
    789 { "-D, --define=name[=value]",	"define macro" },
    790 { "-e, --error-output=file",	"send error output to file" },
    791 { "-E, --fatal-warnings",	"exit on warnings" },
    792 { "-F, --freeze-state=file",	"save state to file" },
    793 { "-g, --gnu",			"enable gnu extensions" },
    794 { "    --help",			"print this message and exit" },
    795 { "-I, --include=file",		"include file" },
    796 { "-i, --interactive",		"unbuffer output, ignore tty signals" },
    797 { "-L, --nesting-limit=num",	"macro expansion nesting limit (unimpl)" },
    798 { "-P, --prefix-builtins",	"prefix builtins with m4_" },
    799 { "-Q, --quiet",		"don't print warnings" },
    800 { "-R, --reload-state=file",	"restore state from file" },
    801 { "-Q, --silent",		"don't print warnings" },
    802 { "-s, --synclines",		"output line directives for cpp(1)" },
    803 { "-t, --trace=macro",		"trace the named macro" },
    804 { "-G, --traditional",		"disable gnu extensions" },
    805 { "-U, --undefine=name",	"undefine the named macro" },
    806 { "-v, --version",		"print the version number and exit" },
    807 };
    808 
    809 static void
    810 help(void)
    811 {
    812 	size_t i;
    813 	fprintf(stdout, "%s version %d\n\n", getprogname(), VERSION);
    814 	usage(stdout);
    815 
    816 	fprintf(stdout, "\nThe long options are:\n");
    817 	for (i = 0; i < __arraycount(nd); i++)
    818 		fprintf(stdout, "\t%-25.25s\t%s\n", nd[i].n, nd[i].d);
    819 }
    820