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