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