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