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