Home | History | Annotate | Line # | Download | only in m4
      1 /*	$OpenBSD: main.c,v 1.90 2026/02/25 05:37:25 op Exp $	*/
      2 /*	$NetBSD: main.c,v 1.52 2026/06/10 22:25:02 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.52 2026/06/10 22:25:02 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  error_warns = 0;           /* -E option to make warnings exit_code = 1 */
     87 int  fatal_warns = 0;		/* -E -E option to exit on warnings */
     88 int  quiet = 0;			/* -Q option to silence warnings */
     89 int  nesting_limit = -1;	/* -L for nesting limit */
     90 static const char *freeze = NULL;	/* -F to freeze state */
     91 static const char *reload = NULL;	/* -R to reload state */
     92 #ifndef REAL_FREEZE
     93 FILE *freezef = NULL;
     94 int thawing = 0;
     95 #endif
     96 
     97 struct keyblk {
     98 	const char *knam;	/* keyword name */
     99 	int	ktyp;		/* keyword type */
    100 };
    101 
    102 static struct keyblk keywrds[] = {	/* m4 keywords to be installed */
    103 	{ "include",      INCLUDETYPE },
    104 	{ "sinclude",     SINCLUDETYPE },
    105 	{ "define",       DEFINETYPE },
    106 	{ "defn",         DEFNTYPE },
    107 	{ "divert",       DIVERTTYPE | NOARGS },
    108 	{ "eval",         EVALTYPE },
    109 	{ "expr",         EVALTYPE },
    110 	{ "substr",       SUBSTRTYPE },
    111 	{ "ifelse",       IFELSETYPE },
    112 	{ "ifdef",        IFDEFTYPE },
    113 	{ "len",          LENTYPE },
    114 	{ "incr",         INCRTYPE },
    115 	{ "decr",         DECRTYPE },
    116 	{ "dnl",          DNLTYPE | NOARGS },
    117 	{ "changequote",  CHANGEQUOTETYPE | NOARGS },
    118 	{ "changecom",    CHANGECOMTYPE | NOARGS },
    119 	{ "index",        INDEXTYPE },
    120 #ifdef EXTENDED
    121 	{ "paste",        PASTETYPE },
    122 	{ "spaste",       SPASTETYPE },
    123 	/* Newer extensions, needed to handle gnu-m4 scripts */
    124 	{ "indir",        INDIRTYPE},
    125 	{ "builtin",      BUILTINTYPE},
    126 	{ "patsubst",	  PATSUBSTTYPE},
    127 	{ "regexp",	  REGEXPTYPE},
    128 	{ "esyscmd",	  ESYSCMDTYPE},
    129 	{ "__file__",	  FILENAMETYPE | NOARGS},
    130 	{ "__line__",	  LINETYPE | NOARGS},
    131 #endif
    132 	{ "popdef",       POPDEFTYPE },
    133 	{ "pushdef",      PUSHDEFTYPE },
    134 	{ "dumpdef",      DUMPDEFTYPE | NOARGS },
    135 	{ "shift",        SHIFTTYPE | NOARGS },
    136 	{ "translit",     TRANSLITTYPE },
    137 	{ "undefine",     UNDEFINETYPE },
    138 	{ "undivert",     UNDIVERTTYPE | NOARGS },
    139 	{ "divnum",       DIVNUMTYPE | NOARGS },
    140 	{ "maketemp",     MKSTEMPTYPE },
    141 	{ "mkstemp",      MKSTEMPTYPE },
    142 	{ "errprint",     ERRPRINTTYPE | NOARGS },
    143 	{ "m4wrap",       M4WRAPTYPE | NOARGS },
    144 	{ "m4exit",       M4EXITTYPE | NOARGS },
    145 	{ "syscmd",       SYSCMDTYPE },
    146 	{ "sysval",       SYSVALTYPE | NOARGS },
    147 	{ "traceon",	  TRACEONTYPE | NOARGS },
    148 	{ "traceoff",	  TRACEOFFTYPE | NOARGS },
    149 
    150 /* Macro that expands to itself, signature of the current OS */
    151 	{ "unix",         SELFTYPE | NOARGS },
    152 };
    153 
    154 #define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
    155 
    156 #define MAXRECORD 50
    157 static struct position {
    158 	char *name;
    159 	unsigned long line;
    160 } quotes[MAXRECORD], paren[MAXRECORD];
    161 
    162 static void record(struct position *, int);
    163 static void dump_stack(struct position *, int);
    164 
    165 static void macro(void);
    166 static void initkwds(void);
    167 static ndptr inspect(int, char *);
    168 static int do_look_ahead(int, const char *);
    169 static void reallyoutputstr(const char *);
    170 static void reallyputchar(int);
    171 
    172 static void enlarge_stack(void);
    173 static void help(void);
    174 
    175 int exit_code = 0;
    176 #define OPT_HELP 1
    177 
    178 static struct option longopts[] = {
    179 	{ "debug",		optional_argument,	0, 'd' },
    180 	{ "define",		required_argument,	0, 'D' },
    181 	{ "error-output",	required_argument,	0, 'o' }, /* sic */
    182 	{ "fatal-warnings",	no_argument,		0, 'E' },
    183 	{ "freeze-state",	required_argument,	0, 'F' },
    184 	{ "gnu",		no_argument,		0, 'g' },
    185 	{ "help",		no_argument,		0, OPT_HELP },
    186 	{ "include",		required_argument,	0, 'I' },
    187 	{ "interactive",	no_argument,		0, 'i' },
    188 	{ "nesting-limit",	required_argument,	0, 'L' },
    189 	{ "prefix-builtins",	no_argument,		0, 'P' },
    190 	{ "quiet",		no_argument,		0, 'Q' },
    191 	{ "reload-state",	required_argument,	0, 'R' },
    192 	{ "silent",		no_argument,		0, 'Q' },
    193 	{ "synclines",		no_argument,		0, 's' },
    194 	{ "trace",		required_argument,	0, 't' },
    195 	{ "traditional",	no_argument,		0, 'G' },
    196 	{ "undefine",		required_argument,	0, 'U' },
    197 	{ "version",		no_argument,		0, 'v' },
    198 #ifdef notyet
    199 	{ "arglength",		required_argument,	0, 'l' },
    200 	{ "debugfile",		optional_argument,	0, OPT_DEBUGFILE },
    201 	{ "hashsize",		required_argument,	0, 'H' },
    202 	{ "warn-macro-sequence",optional_argument,	0, OPT_WARN_SEQUENCE },
    203 #endif
    204 	{ 0,			0,			0, 0 },
    205 };
    206 
    207 int
    208 main(int argc, char *argv[])
    209 {
    210 	int c;
    211 	int n;
    212 	int error;
    213 	char *p;
    214 
    215 	setprogname(argv[0]);
    216 
    217 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
    218 		signal(SIGINT, onintr);
    219 
    220 	init_macros();
    221 	initspaces();
    222 	STACKMAX = INITSTACKMAX;
    223 
    224 	mstack = xreallocarray(NULL, STACKMAX, sizeof(stae), NULL);
    225 	sstack = xalloc(STACKMAX, NULL);
    226 
    227 	maxout = 0;
    228 	outfile = NULL;
    229 	resizedivs(MAXOUT);
    230 
    231 	while ((c = getopt_long(argc, argv, "D:d:EF:GgI:iL:o:PR:Qst:U:v",
    232 	    longopts, NULL)) != -1)
    233 		switch(c) {
    234 		case 'D':		/* define something..*/
    235 			for (p = optarg; *p; p++)
    236 				if (*p == '=')
    237 					break;
    238 			if (*p)
    239 				*p++ = EOS;
    240 			dodefine(optarg, p);
    241 			break;
    242 		case 'd':
    243 			set_trace_flags(optarg);
    244 			break;
    245 		case 'E':               /* like GNU m4 1.4.9+ */
    246 			if (error_warns == 0)
    247 				error_warns = 1;
    248 			else
    249 				fatal_warns = 1;
    250 			break;
    251 		case 'F':
    252 			freeze = optarg;
    253 #ifndef REAL_FREEZE
    254 			if ((freezef = fopen(freeze, "w")) == NULL)
    255 				err(EXIT_FAILURE, "Can't open `%s'", freeze);
    256 #endif
    257 			break;
    258 		case 'I':
    259 			addtoincludepath(optarg);
    260 			break;
    261 		case 'i':
    262 			setvbuf(stdout, NULL, _IONBF, 0);
    263 			signal(SIGINT, SIG_IGN);
    264 			break;
    265 		case 'G':
    266 			mimic_gnu = 0;
    267 			break;
    268 		case 'g':
    269 			mimic_gnu = 1;
    270 			break;
    271 		case 'L':
    272 			nesting_limit = atoi(optarg);
    273 			break;
    274 		case 'o':
    275 			error = trace_file(optarg);
    276 			if (error)
    277 				warn("%s", optarg);
    278 			break;
    279 		case 'P':
    280 			prefix_builtins = 1;
    281 			break;
    282 		case 'Q':
    283 			quiet++;
    284 			break;
    285 		case 'R':
    286 			reload = optarg;
    287 			break;
    288 		case 's':
    289 			synch_lines = 1;
    290 			break;
    291 		case 't':
    292 			mark_traced(optarg, 1);
    293 			break;
    294 		case 'U':		/* undefine...	     */
    295 			macro_popdef(optarg);
    296 			break;
    297 		case 'v':
    298 			fprintf(stderr, "%s version %d\n", getprogname(),
    299 			    VERSION);
    300 			return EXIT_SUCCESS;
    301 		case OPT_HELP:
    302 			help();
    303 			return EXIT_SUCCESS;
    304 		case '?':
    305 		default:
    306 			usage(stderr);
    307 			return EXIT_FAILURE;
    308 		}
    309 
    310 #ifdef REDIRECT
    311 	/*
    312 	 * This is meant only for debugging; it makes all output
    313 	 * go to a known file, even if the command line options
    314 	 * send it elsewhere. It should not be turned of in production code.
    315 	 */
    316 	if (freopen("/tmp/m4", "w+", stderr) == NULL)
    317 		err(EXIT_FAILURE, "Can't redirect errors to `%s'",
    318 		    "/tmp/m4");
    319 #endif
    320 	argc -= optind;
    321 	argv += optind;
    322 
    323 	initkwds();
    324 	if (mimic_gnu)
    325 		setup_builtin("format", FORMATTYPE);
    326 
    327 	active = stdout;		/* default active output     */
    328 	bbase[0] = bufbase;
    329 
    330 	if (reload) {
    331 #ifdef REAL_FREEZE
    332 		thaw_state(reload);
    333 #else
    334 		if (fopen_trypath(infile, reload) == NULL)
    335 			err(1, "Can't open `%s'", reload);
    336 		sp = -1;
    337 		fp = 0;
    338 		thawing = 1;
    339 		macro();
    340 		thawing = 0;
    341 		release_input(infile);
    342 #endif
    343 	}
    344 
    345 	if (!argc) {
    346 		sp = -1;		/* stack pointer initialized */
    347 		fp = 0;			/* frame pointer initialized */
    348 		set_input(infile+0, stdin, "stdin");
    349 					/* default input (naturally) */
    350 		macro();
    351 	} else
    352 		for (; argc--; ++argv) {
    353 			p = *argv;
    354 			if (p[0] == '-' && p[1] == EOS)
    355 				set_input(infile, stdin, "stdin");
    356 			else if (fopen_trypath(infile, p) == NULL)
    357 				err(1, "%s", p);
    358 			sp = -1;
    359 			fp = 0;
    360 			macro();
    361 			release_input(infile);
    362 		}
    363 
    364 	if (wrapindex) {
    365 		int i;
    366 
    367 		ilevel = 0;		/* in case m4wrap includes.. */
    368 		bufbase = bp = buf;	/* use the entire buffer   */
    369 		if (mimic_gnu) {
    370 			while (wrapindex != 0) {
    371 				for (i = 0; i < wrapindex; i++)
    372 					pbstr(m4wraps[i]);
    373 				wrapindex =0;
    374 				macro();
    375 			}
    376 		} else {
    377 			for (i = 0; i < wrapindex; i++) {
    378 				pbstr(m4wraps[i]);
    379 				macro();
    380 			}
    381 		}
    382 	}
    383 
    384 	if (active != stdout)
    385 		active = stdout;	/* reset output just in case */
    386 	for (n = 1; n < maxout; n++)	/* default wrap-up: undivert */
    387 		if (outfile[n] != NULL)
    388 			getdiv(n);
    389 					/* remove bitbucket if used  */
    390 	if (outfile[0] != NULL) {
    391 		(void) fclose(outfile[0]);
    392 	}
    393 
    394 #ifdef REAL_FREEZE
    395 	if (freeze)
    396 		freeze_state(freeze);
    397 #else
    398 	if (freezef)
    399 		fclose(freezef);
    400 #endif
    401 
    402 	return 0;
    403 }
    404 
    405 /*
    406  * Look ahead for `token'.
    407  * (on input `t == token[0]')
    408  * Used for comment and quoting delimiters.
    409  * Returns 1 if `token' present; copied to output.
    410  *	   0 if `token' not found; all characters pushed back
    411  */
    412 static int
    413 do_look_ahead(int t, const char *token)
    414 {
    415 	int i;
    416 
    417 	assert((unsigned char)t == (unsigned char)token[0]);
    418 
    419 	for (i = 1; *++token; i++) {
    420 		t = gpbc();
    421 		if (t == EOF || (unsigned char)t != (unsigned char)*token) {
    422 			pushback(t);
    423 			while (--i)
    424 				pushback(*--token);
    425 			return 0;
    426 		}
    427 	}
    428 	return 1;
    429 }
    430 
    431 #define LOOK_AHEAD(t, token) (t != EOF &&		\
    432     (unsigned char)(t)==(unsigned char)(token)[0] &&	\
    433     do_look_ahead(t,token))
    434 
    435 /*
    436  * macro - the work horse..
    437  */
    438 static void
    439 macro(void)
    440 {
    441 	char token[MAXTOK+1];
    442 	int t, l;
    443 	ndptr p;
    444 	int  nlpar;
    445 
    446 	cycle {
    447 		t = gpbc();
    448 
    449 		if (LOOK_AHEAD(t,lquote)) {	/* strip quotes */
    450 			nlpar = 0;
    451 			record(quotes, nlpar++);
    452 			/*
    453 			 * Opening quote: scan forward until matching
    454 			 * closing quote has been found.
    455 			 */
    456 			do {
    457 
    458 				l = gpbc();
    459 				if (LOOK_AHEAD(l,rquote)) {
    460 					if (--nlpar > 0)
    461 						outputstr(rquote);
    462 				} else if (LOOK_AHEAD(l,lquote)) {
    463 					record(quotes, nlpar++);
    464 					outputstr(lquote);
    465 				} else if (l == EOF) {
    466 					if (!quiet) {
    467 						if (nlpar == 1)
    468 							warnx("unclosed quote:");
    469 						else
    470 							warnx(
    471 							    "%d unclosed quotes:",
    472 							    nlpar);
    473 						dump_stack(quotes, nlpar);
    474 					}
    475 					exit(EXIT_FAILURE);
    476 				} else {
    477 					if (nlpar > 0) {
    478 						if (sp < 0)
    479 							reallyputchar(l);
    480 						else
    481 							CHRSAVE(l);
    482 					}
    483 				}
    484 			}
    485 			while (nlpar != 0);
    486 		} else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
    487 			reallyoutputstr(scommt);
    488 
    489 			for(;;) {
    490 				t = gpbc();
    491 				if (LOOK_AHEAD(t, ecommt)) {
    492 					reallyoutputstr(ecommt);
    493 					break;
    494 				}
    495 				if (t == EOF)
    496 					break;
    497 				reallyputchar(t);
    498 			}
    499 		} else if (t == '_' || isalpha(t)) {
    500 			p = inspect(t, token);
    501 			if (p != NULL)
    502 				pushback(l = gpbc());
    503 			if (p == NULL || (l != LPAREN &&
    504 			    (macro_getdef(p)->type & NEEDARGS) != 0))
    505 				outputstr(token);
    506 			else {
    507 		/*
    508 		 * real thing.. First build a call frame:
    509 		 */
    510 				pushf(fp);	/* previous call frm */
    511 				pushf(macro_getdef(p)->type); /* type of the call  */
    512 				pushf(is_traced(p));
    513 				pushf(0);	/* parenthesis level */
    514 				fp = sp;	/* new frame pointer */
    515 		/*
    516 		 * now push the string arguments:
    517 		 * XXX: Copy the macro definition. This leaks, but too
    518 		 * lazy to fix properly.
    519 		 * The problem is that if we evaluate a pushdef'ed
    520 		 * macro and then popdef it while it the definition
    521 		 * is still on the stack we are going to reference
    522 		 * free memory.
    523 		 */
    524 				pushs1(xstrdup(macro_getdef(p)->defn)); /* defn string */
    525 				pushs1((char *)macro_name(p));	/* macro name  */
    526 				pushs(ep);		/* start next..*/
    527 
    528 				if (l != LPAREN && PARLEV == 0)	{
    529 				    /* no bracks  */
    530 					chrsave(EOS);
    531 
    532 					if ((size_t)sp == STACKMAX)
    533 						errx(1, "internal stack overflow");
    534 					eval((const char **)(void *)mstack+fp+1, 2,
    535 					    CALTYP, TRACESTATUS);
    536 
    537 					ep = PREVEP;	/* flush strspace */
    538 					sp = PREVSP;	/* previous sp..  */
    539 					fp = PREVFP;	/* rewind stack...*/
    540 				}
    541 			}
    542 		} else if (t == EOF) {
    543 			if (sp > -1 && ilevel <= 0) {
    544 				if (!quiet) {
    545 					warnx("unexpected end of input, "
    546 					    "unclosed parenthesis:");
    547 					dump_stack(paren, PARLEV);
    548 				}
    549 				exit(EXIT_FAILURE);
    550 			}
    551 			if (ilevel <= 0)
    552 				break;			/* all done thanks.. */
    553 			release_input(infile+ilevel--);
    554 			emit_synchline();
    555 			bufbase = bbase[ilevel];
    556 			continue;
    557 		} else if (sp < 0) {		/* not in a macro at all */
    558 			reallyputchar(t);	/* output directly..	 */
    559 		}
    560 
    561 		else switch(t) {
    562 
    563 		case LPAREN:
    564 			if (PARLEV > 0)
    565 				chrsave(t);
    566 			while (isspace(l = gpbc())) /* skip blank, tab, nl.. */
    567 				if (PARLEV > 0)
    568 					chrsave(l);
    569 			pushback(l);
    570 			record(paren, PARLEV++);
    571 			break;
    572 
    573 		case RPAREN:
    574 			if (--PARLEV > 0)
    575 				chrsave(t);
    576 			else {			/* end of argument list */
    577 				chrsave(EOS);
    578 
    579 				if ((size_t)sp == STACKMAX)
    580 					errx(1, "internal stack overflow");
    581 
    582 				eval((const char **)(void *)mstack+fp+1, sp-fp,
    583 				    CALTYP, TRACESTATUS);
    584 
    585 				ep = PREVEP;	/* flush strspace */
    586 				sp = PREVSP;	/* previous sp..  */
    587 				fp = PREVFP;	/* rewind stack...*/
    588 			}
    589 			break;
    590 
    591 		case COMMA:
    592 			if (PARLEV == 1) {
    593 				chrsave(EOS);		/* new argument	  */
    594 				while (isspace(l = gpbc()))
    595 					;
    596 				pushback(l);
    597 				pushs(ep);
    598 			} else
    599 				chrsave(t);
    600 			break;
    601 
    602 		default:
    603 			if (LOOK_AHEAD(t, scommt)) {
    604 				char *q;
    605 				for (q = scommt; *q; q++)
    606 					chrsave(*q);
    607 				for(;;) {
    608 					t = gpbc();
    609 					if (LOOK_AHEAD(t, ecommt)) {
    610 						for (q = ecommt; *q; q++)
    611 							chrsave(*q);
    612 						break;
    613 					}
    614 					if (t == EOF)
    615 					    break;
    616 					CHRSAVE(t);
    617 				}
    618 			} else
    619 				CHRSAVE(t);		/* stack the char */
    620 			break;
    621 		}
    622 	}
    623 }
    624 
    625 /*
    626  * output string directly, without pushing it for reparses.
    627  */
    628 void
    629 outputstr(const char *s)
    630 {
    631 	if (sp < 0)
    632 		reallyoutputstr(s);
    633 	else
    634 		while (*s)
    635 			CHRSAVE(*s++);
    636 }
    637 
    638 void
    639 reallyoutputstr(const char *s)
    640 {
    641 	if (synch_lines) {
    642 		while (*s) {
    643 			fputc(*s, active);
    644 			if (*s++ == '\n') {
    645 				infile[ilevel].synch_lineno++;
    646 				if (infile[ilevel].synch_lineno !=
    647 				    infile[ilevel].lineno)
    648 					do_emit_synchline();
    649 			}
    650 		}
    651 	} else
    652 		fputs(s, active);
    653 }
    654 
    655 void
    656 reallyputchar(int c)
    657 {
    658 	putc(c, active);
    659 	if (synch_lines && c == '\n') {
    660 		infile[ilevel].synch_lineno++;
    661 		if (infile[ilevel].synch_lineno != infile[ilevel].lineno)
    662 			do_emit_synchline();
    663 	}
    664 }
    665 
    666 /*
    667  * build an input token..
    668  * consider only those starting with _ or A-Za-z.
    669  */
    670 static ndptr
    671 inspect(int c, char *tp)
    672 {
    673 	char *name = tp;
    674 	char *etp = tp+MAXTOK;
    675 	ndptr p;
    676 
    677 	*tp++ = c;
    678 
    679 	while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
    680 		*tp++ = c;
    681 	if (c != EOF)
    682 		PUSHBACK(c);
    683 	*tp = EOS;
    684 	/* token is too long, it won't match anything, but it can still
    685 	 * be output. */
    686 	if (tp == ep) {
    687 		outputstr(name);
    688 		while (isalnum(c = gpbc()) || c == '_') {
    689 			if (sp < 0)
    690 				reallyputchar(c);
    691 			else
    692 				CHRSAVE(c);
    693 		}
    694 		*name = EOS;
    695 		return NULL;
    696 	}
    697 
    698 	p = ohash_find(&macros, ohash_qlookupi(&macros, name, (void *)&tp));
    699 	if (p == NULL)
    700 		return NULL;
    701 	if (macro_getdef(p) == NULL)
    702 		return NULL;
    703 	return p;
    704 }
    705 
    706 /*
    707  * initkwds - initialise m4 keywords as fast as possible.
    708  * This very similar to install, but without certain overheads,
    709  * such as calling lookup. Malloc is not used for storing the
    710  * keyword strings, since we simply use the static pointers
    711  * within keywrds block.
    712  */
    713 static void
    714 initkwds(void)
    715 {
    716 	unsigned int type;
    717 	size_t i;
    718 
    719 	for (i = 0; i < MAXKEYS; i++) {
    720 		type = keywrds[i].ktyp & TYPEMASK;
    721 		if ((keywrds[i].ktyp & NOARGS) == 0)
    722 			type |= NEEDARGS;
    723 		setup_builtin(keywrds[i].knam, type);
    724 	}
    725 }
    726 
    727 static void
    728 record(struct position *t, int lev)
    729 {
    730 	if (lev < MAXRECORD) {
    731 		t[lev].name = CURRENT_NAME;
    732 		t[lev].line = CURRENT_LINE;
    733 	}
    734 }
    735 
    736 static void
    737 dump_stack(struct position *t, int lev)
    738 {
    739 	int i;
    740 
    741 	for (i = 0; i < lev; i++) {
    742 		if (i == MAXRECORD) {
    743 			fprintf(stderr, "   ...\n");
    744 			break;
    745 		}
    746 		fprintf(stderr, "   %s at line %lu\n",
    747 			t[i].name, t[i].line);
    748 	}
    749 }
    750 
    751 
    752 static void
    753 enlarge_stack(void)
    754 {
    755 	STACKMAX += STACKMAX/2;
    756 	mstack = xreallocarray(mstack, STACKMAX, sizeof(stae),
    757 	    "Evaluation stack overflow (%lu)",
    758 	    (unsigned long)STACKMAX);
    759 	sstack = xrealloc(sstack, STACKMAX,
    760 	    "Evaluation stack overflow (%lu)",
    761 	    (unsigned long)STACKMAX);
    762 }
    763 
    764 static const struct {
    765 	const char *n;
    766 	const char *d;
    767 } nd [] = {
    768 { "-d, --debug[=flags]",	"set debug flags" },
    769 { "-D, --define=name[=value]",	"define macro" },
    770 { "-e, --error-output=file",	"send error output to file" },
    771 { "-E, --fatal-warnings",	"exit on warnings" },
    772 { "-F, --freeze-state=file",	"save state to file" },
    773 { "-g, --gnu",			"enable gnu extensions" },
    774 { "    --help",			"print this message and exit" },
    775 { "-I, --include=file",		"include file" },
    776 { "-i, --interactive",		"unbuffer output, ignore tty signals" },
    777 { "-L, --nesting-limit=num",	"macro expansion nesting limit (unimpl)" },
    778 { "-P, --prefix-builtins",	"prefix builtins with m4_" },
    779 { "-Q, --quiet",		"don't print warnings" },
    780 { "-R, --reload-state=file",	"restore state from file" },
    781 { "-Q, --silent",		"don't print warnings" },
    782 { "-s, --synclines",		"output line directives for cpp(1)" },
    783 { "-t, --trace=macro",		"trace the named macro" },
    784 { "-G, --traditional",		"disable gnu extensions" },
    785 { "-U, --undefine=name",	"undefine the named macro" },
    786 { "-v, --version",		"print the version number and exit" },
    787 };
    788 
    789 static void
    790 help(void)
    791 {
    792 	size_t i;
    793 	fprintf(stdout, "%s version %d\n\n", getprogname(), VERSION);
    794 	usage(stdout);
    795 
    796 	fprintf(stdout, "\nThe long options are:\n");
    797 	for (i = 0; i < __arraycount(nd); i++)
    798 		fprintf(stdout, "\t%-25.25s\t%s\n", nd[i].n, nd[i].d);
    799 }
    800