Home | History | Annotate | Line # | Download | only in m4
      1 /*	$OpenBSD: main.c,v 1.77 2009/10/14 17:19:47 sthen Exp $	*/
      2 /*	$NetBSD: main.c,v 1.50 2020/06/25 02:25:53 uwe 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.50 2020/06/25 02:25:53 uwe 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, 'o' }, /* sic */
    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 	int error;
    231 	char *p;
    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: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 'F':
    267 			freeze = optarg;
    268 #ifndef REAL_FREEZE
    269 			if ((freezef = fopen(freeze, "w")) == NULL)
    270 				err(EXIT_FAILURE, "Can't open `%s'", freeze);
    271 #endif
    272 			break;
    273 		case 'I':
    274 			addtoincludepath(optarg);
    275 			break;
    276 		case 'i':
    277 			setvbuf(stdout, NULL, _IONBF, 0);
    278 			signal(SIGINT, SIG_IGN);
    279 			break;
    280 		case 'G':
    281 			mimic_gnu = 0;
    282 			break;
    283 		case 'g':
    284 			mimic_gnu = 1;
    285 			break;
    286 		case 'L':
    287 			nesting_limit = atoi(optarg);
    288 			break;
    289 		case 'o':
    290 			error = trace_file(optarg);
    291 			if (error)
    292 				warn("%s", optarg);
    293                         break;
    294 		case 'P':
    295 			prefix_builtins = 1;
    296 			break;
    297 		case 'Q':
    298 			quiet++;
    299 			break;
    300 		case 'R':
    301 			reload = optarg;
    302 			break;
    303 		case 's':
    304 			synch_lines = 1;
    305 			break;
    306 		case 't':
    307 			mark_traced(optarg, 1);
    308 			break;
    309 		case 'U':               /* undefine...       */
    310 			macro_popdef(optarg);
    311 			break;
    312 		case 'v':
    313 			fprintf(stderr, "%s version %d\n", getprogname(),
    314 			    VERSION);
    315 			return EXIT_SUCCESS;
    316 		case OPT_HELP:
    317 			help();
    318 			return EXIT_SUCCESS;
    319 		case '?':
    320 		default:
    321 			usage(stderr);
    322 			return EXIT_FAILURE;
    323 		}
    324 
    325 #ifdef REDIRECT
    326 	/*
    327 	 * This is meant only for debugging; it makes all output
    328 	 * go to a known file, even if the command line options
    329 	 * send it elsewhere. It should not be turned of in production code.
    330 	 */
    331 	if (freopen("/tmp/m4", "w+", stderr) == NULL)
    332 		err(EXIT_FAILURE, "Can't redirect errors to `%s'",
    333 		    "/tmp/m4");
    334 #endif
    335         argc -= optind;
    336         argv += optind;
    337 
    338 
    339 	initkwds();
    340 	if (mimic_gnu)
    341 		setup_builtin("format", FORMATTYPE);
    342 
    343 	active = stdout;		/* default active output     */
    344 	bbase[0] = bufbase;
    345 
    346 	if (reload) {
    347 #ifdef REAL_FREEZE
    348 		thaw_state(reload);
    349 #else
    350 		if (fopen_trypath(infile, reload) == NULL)
    351 			err(1, "Can't open `%s'", reload);
    352 		sp = -1;
    353 		fp = 0;
    354 		thawing = 1;
    355 		macro();
    356 		thawing = 0;
    357 		release_input(infile);
    358 #endif
    359 	}
    360 
    361         if (!argc) {
    362  		sp = -1;		/* stack pointer initialized */
    363 		fp = 0; 		/* frame pointer initialized */
    364 		set_input(infile+0, stdin, "stdin");
    365 					/* default input (naturally) */
    366 		macro();
    367 	} else
    368 		for (; argc--; ++argv) {
    369 			p = *argv;
    370 			if (p[0] == '-' && p[1] == EOS)
    371 				set_input(infile, stdin, "stdin");
    372 			else if (fopen_trypath(infile, p) == NULL)
    373 				err(1, "%s", p);
    374 			sp = -1;
    375 			fp = 0;
    376 			macro();
    377 		    	release_input(infile);
    378 		}
    379 
    380 	if (wrapindex) {
    381 		int i;
    382 
    383 		ilevel = 0;		/* in case m4wrap includes.. */
    384 		bufbase = bp = buf;	/* use the entire buffer   */
    385 		if (mimic_gnu) {
    386 			while (wrapindex != 0) {
    387 				for (i = 0; i < wrapindex; i++)
    388 					pbstr(m4wraps[i]);
    389 				wrapindex =0;
    390 				macro();
    391 			}
    392 		} else {
    393 			for (i = 0; i < wrapindex; i++) {
    394 				pbstr(m4wraps[i]);
    395 				macro();
    396 		    	}
    397 		}
    398 	}
    399 
    400 	if (active != stdout)
    401 		active = stdout;	/* reset output just in case */
    402 	for (n = 1; n < maxout; n++)	/* default wrap-up: undivert */
    403 		if (outfile[n] != NULL)
    404 			getdiv(n);
    405 					/* remove bitbucket if used  */
    406 	if (outfile[0] != NULL) {
    407 		(void) fclose(outfile[0]);
    408 	}
    409 
    410 #ifdef REAL_FREEZE
    411 	if (freeze)
    412 		freeze_state(freeze);
    413 #else
    414 	if (freezef)
    415 		fclose(freezef);
    416 #endif
    417 
    418 	return 0;
    419 }
    420 
    421 /*
    422  * Look ahead for `token'.
    423  * (on input `t == token[0]')
    424  * Used for comment and quoting delimiters.
    425  * Returns 1 if `token' present; copied to output.
    426  *         0 if `token' not found; all characters pushed back
    427  */
    428 static int
    429 do_look_ahead(int t, const char *token)
    430 {
    431 	int i;
    432 
    433 	assert((unsigned char)t == (unsigned char)token[0]);
    434 
    435 	for (i = 1; *++token; i++) {
    436 		t = gpbc();
    437 		if (t == EOF || (unsigned char)t != (unsigned char)*token) {
    438 			pushback(t);
    439 			while (--i)
    440 				pushback(*--token);
    441 			return 0;
    442 		}
    443 	}
    444 	return 1;
    445 }
    446 
    447 #define LOOK_AHEAD(t, token) (t != EOF && 		\
    448     (unsigned char)(t)==(unsigned char)(token)[0] && 	\
    449     do_look_ahead(t,token))
    450 
    451 /*
    452  * macro - the work horse..
    453  */
    454 static void
    455 macro(void)
    456 {
    457 	char token[MAXTOK+1];
    458 	int t, l;
    459 	ndptr p;
    460 	int  nlpar;
    461 
    462 	cycle {
    463 		t = gpbc();
    464 
    465 		if (LOOK_AHEAD(t,lquote)) {	/* strip quotes */
    466 			nlpar = 0;
    467 			record(quotes, nlpar++);
    468 			/*
    469 			 * Opening quote: scan forward until matching
    470 			 * closing quote has been found.
    471 			 */
    472 			do {
    473 
    474 				l = gpbc();
    475 				if (LOOK_AHEAD(l,rquote)) {
    476 					if (--nlpar > 0)
    477 						outputstr(rquote);
    478 				} else if (LOOK_AHEAD(l,lquote)) {
    479 					record(quotes, nlpar++);
    480 					outputstr(lquote);
    481 				} else if (l == EOF) {
    482 					if (!quiet) {
    483 						if (nlpar == 1)
    484 							warnx("unclosed quote:");
    485 						else
    486 							warnx(
    487 							    "%d unclosed quotes:",
    488 							    nlpar);
    489 						dump_stack(quotes, nlpar);
    490 					}
    491 					exit(EXIT_FAILURE);
    492 				} else {
    493 					if (nlpar > 0) {
    494 						if (sp < 0)
    495 							reallyputchar(l);
    496 						else
    497 							CHRSAVE(l);
    498 					}
    499 				}
    500 			}
    501 			while (nlpar != 0);
    502 		} else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
    503 			reallyoutputstr(scommt);
    504 
    505 			for(;;) {
    506 				t = gpbc();
    507 				if (LOOK_AHEAD(t, ecommt)) {
    508 					reallyoutputstr(ecommt);
    509 					break;
    510 				}
    511 				if (t == EOF)
    512 					break;
    513 				reallyputchar(t);
    514 			}
    515 		} else if (t == '_' || isalpha(t)) {
    516 			p = inspect(t, token);
    517 			if (p != NULL)
    518 				pushback(l = gpbc());
    519 			if (p == NULL || (l != LPAREN &&
    520 			    (macro_getdef(p)->type & NEEDARGS) != 0))
    521 				outputstr(token);
    522 			else {
    523 		/*
    524 		 * real thing.. First build a call frame:
    525 		 */
    526 				pushf(fp);	/* previous call frm */
    527 				pushf(macro_getdef(p)->type); /* type of the call  */
    528 				pushf(is_traced(p));
    529 				pushf(0);	/* parenthesis level */
    530 				fp = sp;	/* new frame pointer */
    531 		/*
    532 		 * now push the string arguments:
    533 		 * XXX: Copy the macro definition. This leaks, but too
    534 		 * lazy to fix properly.
    535 		 * The problem is that if we evaluate a pushdef'ed
    536 		 * macro and then popdef it while it the definition
    537 		 * is still on the stack we are going to reference
    538 		 * free memory.
    539 		 */
    540 				pushs1(xstrdup(macro_getdef(p)->defn));	/* defn string */
    541 				pushs1((char *)macro_name(p));	/* macro name  */
    542 				pushs(ep);	      	/* start next..*/
    543 
    544 				if (l != LPAREN && PARLEV == 0)  {
    545 				    /* no bracks  */
    546 					chrsave(EOS);
    547 
    548 					if ((size_t)sp == STACKMAX)
    549 						errx(1, "internal stack overflow");
    550 					eval((const char **) mstack+fp+1, 2,
    551 					    CALTYP, TRACESTATUS);
    552 
    553 					ep = PREVEP;	/* flush strspace */
    554 					sp = PREVSP;	/* previous sp..  */
    555 					fp = PREVFP;	/* rewind stack...*/
    556 				}
    557 			}
    558 		} else if (t == EOF) {
    559 			if (sp > -1 && ilevel <= 0) {
    560 				if (!quiet) {
    561 					warnx("unexpected end of input, "
    562 					    "unclosed parenthesis:");
    563 					dump_stack(paren, PARLEV);
    564 				}
    565 				exit(EXIT_FAILURE);
    566 			}
    567 			if (ilevel <= 0)
    568 				break;			/* all done thanks.. */
    569 			release_input(infile+ilevel--);
    570 			emit_synchline();
    571 			bufbase = bbase[ilevel];
    572 			continue;
    573 		} else if (sp < 0) {		/* not in a macro at all */
    574 			reallyputchar(t);	/* output directly..	 */
    575 		}
    576 
    577 		else switch(t) {
    578 
    579 		case LPAREN:
    580 			if (PARLEV > 0)
    581 				chrsave(t);
    582 			while (isspace(l = gpbc())) /* skip blank, tab, nl.. */
    583 				if (PARLEV > 0)
    584 					chrsave(l);
    585 			pushback(l);
    586 			record(paren, PARLEV++);
    587 			break;
    588 
    589 		case RPAREN:
    590 			if (--PARLEV > 0)
    591 				chrsave(t);
    592 			else {			/* end of argument list */
    593 				chrsave(EOS);
    594 
    595 				if ((size_t)sp == STACKMAX)
    596 					errx(1, "internal stack overflow");
    597 
    598 				eval((const char **) mstack+fp+1, sp-fp,
    599 				    CALTYP, TRACESTATUS);
    600 
    601 				ep = PREVEP;	/* flush strspace */
    602 				sp = PREVSP;	/* previous sp..  */
    603 				fp = PREVFP;	/* rewind stack...*/
    604 			}
    605 			break;
    606 
    607 		case COMMA:
    608 			if (PARLEV == 1) {
    609 				chrsave(EOS);		/* new argument   */
    610 				while (isspace(l = gpbc()))
    611 					;
    612 				pushback(l);
    613 				pushs(ep);
    614 			} else
    615 				chrsave(t);
    616 			break;
    617 
    618 		default:
    619 			if (LOOK_AHEAD(t, scommt)) {
    620 				char *q;
    621 				for (q = scommt; *q; q++)
    622 					chrsave(*q);
    623 				for(;;) {
    624 					t = gpbc();
    625 					if (LOOK_AHEAD(t, ecommt)) {
    626 						for (q = ecommt; *q; q++)
    627 							chrsave(*q);
    628 						break;
    629 					}
    630 					if (t == EOF)
    631 					    break;
    632 					CHRSAVE(t);
    633 				}
    634 			} else
    635 				CHRSAVE(t);		/* stack the char */
    636 			break;
    637 		}
    638 	}
    639 }
    640 
    641 /*
    642  * output string directly, without pushing it for reparses.
    643  */
    644 void
    645 outputstr(const char *s)
    646 {
    647 	if (sp < 0)
    648 		reallyoutputstr(s);
    649 	else
    650 		while (*s)
    651 			CHRSAVE(*s++);
    652 }
    653 
    654 void
    655 reallyoutputstr(const char *s)
    656 {
    657 	if (synch_lines) {
    658 		while (*s) {
    659 			fputc(*s, active);
    660 			if (*s++ == '\n') {
    661 				infile[ilevel].synch_lineno++;
    662 				if (infile[ilevel].synch_lineno !=
    663 				    infile[ilevel].lineno)
    664 					do_emit_synchline();
    665 			}
    666 		}
    667 	} else
    668 		fputs(s, active);
    669 }
    670 
    671 void
    672 reallyputchar(int c)
    673 {
    674 	putc(c, active);
    675 	if (synch_lines && c == '\n') {
    676 		infile[ilevel].synch_lineno++;
    677 		if (infile[ilevel].synch_lineno != infile[ilevel].lineno)
    678 			do_emit_synchline();
    679 	}
    680 }
    681 
    682 /*
    683  * build an input token..
    684  * consider only those starting with _ or A-Za-z.
    685  */
    686 static ndptr
    687 inspect(int c, char *tp)
    688 {
    689 	char *name = tp;
    690 	char *etp = tp+MAXTOK;
    691 	ndptr p;
    692 
    693 	*tp++ = c;
    694 
    695 	while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
    696 		*tp++ = c;
    697 	if (c != EOF)
    698 		PUSHBACK(c);
    699 	*tp = EOS;
    700 	/* token is too long, it won't match anything, but it can still
    701 	 * be output. */
    702 	if (tp == ep) {
    703 		outputstr(name);
    704 		while (isalnum(c = gpbc()) || c == '_') {
    705 			if (sp < 0)
    706 				reallyputchar(c);
    707 			else
    708 				CHRSAVE(c);
    709 		}
    710 		*name = EOS;
    711 		return NULL;
    712 	}
    713 
    714 	p = ohash_find(&macros, ohash_qlookupi(&macros, name, (void *)&tp));
    715 	if (p == NULL)
    716 		return NULL;
    717 	if (macro_getdef(p) == NULL)
    718 		return NULL;
    719 	return p;
    720 }
    721 
    722 /*
    723  * initkwds - initialise m4 keywords as fast as possible.
    724  * This very similar to install, but without certain overheads,
    725  * such as calling lookup. Malloc is not used for storing the
    726  * keyword strings, since we simply use the static pointers
    727  * within keywrds block.
    728  */
    729 static void
    730 initkwds(void)
    731 {
    732 	unsigned int type;
    733 	size_t i;
    734 
    735 	for (i = 0; i < MAXKEYS; i++) {
    736 		type = keywrds[i].ktyp;
    737 		if ((keywrds[i].ktyp & NOARGS) == 0)
    738 			type |= NEEDARGS;
    739 		setup_builtin(keywrds[i].knam, type);
    740 	}
    741 }
    742 
    743 static void
    744 record(struct position *t, int lev)
    745 {
    746 	if (lev < MAXRECORD) {
    747 		t[lev].name = CURRENT_NAME;
    748 		t[lev].line = CURRENT_LINE;
    749 	}
    750 }
    751 
    752 static void
    753 dump_stack(struct position *t, int lev)
    754 {
    755 	int i;
    756 
    757 	for (i = 0; i < lev; i++) {
    758 		if (i == MAXRECORD) {
    759 			fprintf(stderr, "   ...\n");
    760 			break;
    761 		}
    762 		fprintf(stderr, "   %s at line %lu\n",
    763 			t[i].name, t[i].line);
    764 	}
    765 }
    766 
    767 
    768 static void
    769 enlarge_stack(void)
    770 {
    771 	STACKMAX += STACKMAX/2;
    772 	mstack = xrealloc(mstack, sizeof(stae) * STACKMAX,
    773 	    "Evaluation stack overflow (%lu)",
    774 	    (unsigned long)STACKMAX);
    775 	sstack = xrealloc(sstack, STACKMAX,
    776 	    "Evaluation stack overflow (%lu)",
    777 	    (unsigned long)STACKMAX);
    778 }
    779 
    780 static const struct {
    781 	const char *n;
    782 	const char *d;
    783 } nd [] = {
    784 { "-d, --debug[=flags]",	"set debug flags" },
    785 { "-D, --define=name[=value]",	"define macro" },
    786 { "-e, --error-output=file",	"send error output to file" },
    787 { "-E, --fatal-warnings",	"exit on warnings" },
    788 { "-F, --freeze-state=file",	"save state to file" },
    789 { "-g, --gnu",			"enable gnu extensions" },
    790 { "    --help",			"print this message and exit" },
    791 { "-I, --include=file",		"include file" },
    792 { "-i, --interactive",		"unbuffer output, ignore tty signals" },
    793 { "-L, --nesting-limit=num",	"macro expansion nesting limit (unimpl)" },
    794 { "-P, --prefix-builtins",	"prefix builtins with m4_" },
    795 { "-Q, --quiet",		"don't print warnings" },
    796 { "-R, --reload-state=file",	"restore state from file" },
    797 { "-Q, --silent",		"don't print warnings" },
    798 { "-s, --synclines",		"output line directives for cpp(1)" },
    799 { "-t, --trace=macro",		"trace the named macro" },
    800 { "-G, --traditional",		"disable gnu extensions" },
    801 { "-U, --undefine=name",	"undefine the named macro" },
    802 { "-v, --version",		"print the version number and exit" },
    803 };
    804 
    805 static void
    806 help(void)
    807 {
    808 	size_t i;
    809 	fprintf(stdout, "%s version %d\n\n", getprogname(), VERSION);
    810 	usage(stdout);
    811 
    812 	fprintf(stdout, "\nThe long options are:\n");
    813 	for (i = 0; i < __arraycount(nd); i++)
    814 		fprintf(stdout, "\t%-25.25s\t%s\n", nd[i].n, nd[i].d);
    815 }
    816