main.c revision 1.46
11.38Schristos/*	$OpenBSD: main.c,v 1.77 2009/10/14 17:19:47 sthen Exp $	*/
21.46Schristos/*	$NetBSD: main.c,v 1.46 2016/01/23 14:24:43 christos Exp $	*/
31.9Stls
41.8Sglass/*-
51.8Sglass * Copyright (c) 1989, 1993
61.8Sglass *	The Regents of the University of California.  All rights reserved.
71.8Sglass *
81.8Sglass * This code is derived from software contributed to Berkeley by
91.8Sglass * Ozan Yigit at York University.
101.8Sglass *
111.8Sglass * Redistribution and use in source and binary forms, with or without
121.8Sglass * modification, are permitted provided that the following conditions
131.8Sglass * are met:
141.8Sglass * 1. Redistributions of source code must retain the above copyright
151.8Sglass *    notice, this list of conditions and the following disclaimer.
161.8Sglass * 2. Redistributions in binary form must reproduce the above copyright
171.8Sglass *    notice, this list of conditions and the following disclaimer in the
181.8Sglass *    documentation and/or other materials provided with the distribution.
191.34Sagc * 3. Neither the name of the University nor the names of its contributors
201.8Sglass *    may be used to endorse or promote products derived from this software
211.8Sglass *    without specific prior written permission.
221.8Sglass *
231.8Sglass * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
241.8Sglass * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
251.8Sglass * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
261.8Sglass * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
271.8Sglass * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
281.8Sglass * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
291.8Sglass * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
301.8Sglass * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
311.8Sglass * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
321.8Sglass * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
331.8Sglass * SUCH DAMAGE.
341.8Sglass */
351.1Scgd
361.1Scgd/*
371.8Sglass * main.c
381.8Sglass * Facility: m4 macro processor
391.8Sglass * by: oz
401.1Scgd */
411.38Schristos#if HAVE_NBTOOL_CONFIG_H
421.38Schristos#include "nbtool_config.h"
431.38Schristos#endif
441.38Schristos#include <sys/cdefs.h>
451.46Schristos__RCSID("$NetBSD: main.c,v 1.46 2016/01/23 14:24:43 christos Exp $");
461.27Stv#include <assert.h>
471.38Schristos#include <signal.h>
481.43Schristos#include <getopt.h>
491.38Schristos#include <err.h>
501.38Schristos#include <errno.h>
511.38Schristos#include <unistd.h>
521.38Schristos#include <stdio.h>
531.13Slukem#include <ctype.h>
541.38Schristos#include <string.h>
551.27Stv#include <stddef.h>
561.38Schristos#include <stdint.h>
571.16Skleink#include <stdlib.h>
581.38Schristos#include <ohash.h>
591.8Sglass#include "mdef.h"
601.8Sglass#include "stdd.h"
611.8Sglass#include "extern.h"
621.8Sglass#include "pathnames.h"
631.8Sglass
641.8Sglassndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
651.27Stvstae *mstack;		 	/* stack of m4 machine         */
661.27Stvchar *sstack;		 	/* shadow stack, for string space extension */
671.27Stvstatic size_t STACKMAX;		/* current maximum size of stack */
681.1Scgdint sp; 			/* current m4  stack pointer   */
691.1Scgdint fp; 			/* m4 call frame pointer       */
701.27Stvstruct input_file infile[MAXINP];/* input file stack (0=stdin)  */
711.27StvFILE **outfile;			/* diversion array(0=bitbucket)*/
721.27Stvint maxout;
731.1ScgdFILE *active;			/* active output file pointer  */
741.1Scgdint ilevel = 0; 		/* input file stack pointer    */
751.1Scgdint oindex = 0; 		/* diversion index..	       */
761.38Schristosconst char *null = "";          /* as it says.. just a null..  */
771.38Schristoschar **m4wraps = NULL;		/* m4wraps array.     	       */
781.38Schristosint maxwraps = 0;		/* size of m4wraps array       */
791.38Schristosint wrapindex = 0;		/* current offset in m4wraps   */
801.11Spkchar lquote[MAXCCHARS+1] = {LQUOTE};	/* left quote character  (`)   */
811.11Spkchar rquote[MAXCCHARS+1] = {RQUOTE};	/* right quote character (')   */
821.11Spkchar scommt[MAXCCHARS+1] = {SCOMMT};	/* start character for comment */
831.11Spkchar ecommt[MAXCCHARS+1] = {ECOMMT};	/* end character for comment   */
841.38Schristosint  synch_lines = 0;		/* line synchronisation for C preprocessor */
851.38Schristosint  prefix_builtins = 0;	/* -P option to prefix builtin keywords */
861.43Schristosint  fatal_warnings = 0;	/* -E option to exit on warnings */
871.43Schristosint  quiet = 0;			/* -Q option to silence warnings */
881.43Schristosint  nesting_limit = -1;	/* -L for nesting limit */
891.43Schristosconst char *freeze = NULL;	/* -F to freeze state */
901.43Schristosconst char *reload = NULL;	/* -R to reload state */
911.43Schristos#ifndef REAL_FREEZE
921.43SchristosFILE *freezef = NULL;
931.43Schristosint thawing = 0;
941.43Schristos#endif
951.38Schristos
961.38Schristosstruct keyblk {
971.38Schristos        const char *knam;	/* keyword name */
981.38Schristos        int	ktyp;           /* keyword type */
991.38Schristos};
1001.2Sglass
1011.8Sglassstruct keyblk keywrds[] = {	/* m4 keywords to be installed */
1021.13Slukem	{ "include",      INCLTYPE },
1031.13Slukem	{ "sinclude",     SINCTYPE },
1041.13Slukem	{ "define",       DEFITYPE },
1051.13Slukem	{ "defn",         DEFNTYPE },
1061.27Stv	{ "divert",       DIVRTYPE | NOARGS },
1071.13Slukem	{ "expr",         EXPRTYPE },
1081.13Slukem	{ "eval",         EXPRTYPE },
1091.13Slukem	{ "substr",       SUBSTYPE },
1101.13Slukem	{ "ifelse",       IFELTYPE },
1111.13Slukem	{ "ifdef",        IFDFTYPE },
1121.13Slukem	{ "len",          LENGTYPE },
1131.13Slukem	{ "incr",         INCRTYPE },
1141.13Slukem	{ "decr",         DECRTYPE },
1151.27Stv	{ "dnl",          DNLNTYPE | NOARGS },
1161.27Stv	{ "changequote",  CHNQTYPE | NOARGS },
1171.27Stv	{ "changecom",    CHNCTYPE | NOARGS },
1181.13Slukem	{ "index",        INDXTYPE },
1191.8Sglass#ifdef EXTENDED
1201.13Slukem	{ "paste",        PASTTYPE },
1211.13Slukem	{ "spaste",       SPASTYPE },
1221.27Stv    	/* Newer extensions, needed to handle gnu-m4 scripts */
1231.27Stv	{ "indir",        INDIRTYPE},
1241.27Stv	{ "builtin",      BUILTINTYPE},
1251.27Stv	{ "patsubst",	  PATSTYPE},
1261.27Stv	{ "regexp",	  REGEXPTYPE},
1271.27Stv	{ "esyscmd",	  ESYSCMDTYPE},
1281.27Stv	{ "__file__",	  FILENAMETYPE | NOARGS},
1291.27Stv	{ "__line__",	  LINETYPE | NOARGS},
1301.8Sglass#endif
1311.13Slukem	{ "popdef",       POPDTYPE },
1321.13Slukem	{ "pushdef",      PUSDTYPE },
1331.27Stv	{ "dumpdef",      DUMPTYPE | NOARGS },
1341.27Stv	{ "shift",        SHIFTYPE | NOARGS },
1351.13Slukem	{ "translit",     TRNLTYPE },
1361.13Slukem	{ "undefine",     UNDFTYPE },
1371.27Stv	{ "undivert",     UNDVTYPE | NOARGS },
1381.27Stv	{ "divnum",       DIVNTYPE | NOARGS },
1391.13Slukem	{ "maketemp",     MKTMTYPE },
1401.27Stv	{ "errprint",     ERRPTYPE | NOARGS },
1411.27Stv	{ "m4wrap",       M4WRTYPE | NOARGS },
1421.27Stv	{ "m4exit",       EXITTYPE | NOARGS },
1431.13Slukem	{ "syscmd",       SYSCTYPE },
1441.27Stv	{ "sysval",       SYSVTYPE | NOARGS },
1451.27Stv	{ "traceon",	  TRACEONTYPE | NOARGS },
1461.27Stv	{ "traceoff",	  TRACEOFFTYPE | NOARGS },
1471.2Sglass
1481.27Stv#if defined(unix) || defined(__unix__)
1491.27Stv	{ "unix",         SELFTYPE | NOARGS },
1501.2Sglass#else
1511.8Sglass#ifdef vms
1521.27Stv	{ "vms",          SELFTYPE | NOARGS },
1531.1Scgd#endif
1541.1Scgd#endif
1551.8Sglass};
1561.8Sglass
1571.8Sglass#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
1581.1Scgd
1591.27Stv#define MAXRECORD 50
1601.27Stvstatic struct position {
1611.27Stv	char *name;
1621.27Stv	unsigned long line;
1631.27Stv} quotes[MAXRECORD], paren[MAXRECORD];
1641.27Stv
1651.38Schristosstatic void record(struct position *, int);
1661.38Schristosstatic void dump_stack(struct position *, int);
1671.27Stv
1681.38Schristosstatic void macro(void);
1691.38Schristosstatic void initkwds(void);
1701.38Schristosstatic ndptr inspect(int, char *);
1711.38Schristosstatic int do_look_ahead(int, const char *);
1721.38Schristosstatic void reallyoutputstr(const char *);
1731.38Schristosstatic void reallyputchar(int);
1741.27Stv
1751.38Schristosstatic void enlarge_stack(void);
1761.43Schristosstatic void help(void);
1771.27Stv
1781.43Schristosstatic void
1791.43Schristosusage(FILE *f)
1801.41Sjoerg{
1811.43Schristos	fprintf(f, "Usage: %s [-EGgiPQsv] [-Dname[=value]] [-d flags] "
1821.43Schristos	    "[-I dirname] [-o filename] [-L limit]\n"
1831.43Schristos	    "\t[-t macro] [-Uname] [file ...]\n", getprogname());
1841.41Sjoerg}
1851.41Sjoerg
1861.41Sjoerg__dead static void
1871.41Sjoergonintr(int signo)
1881.41Sjoerg{
1891.41Sjoerg	char intrmessage[] = "m4: interrupted.\n";
1901.41Sjoerg	write(STDERR_FILENO, intrmessage, sizeof(intrmessage)-1);
1911.41Sjoerg	_exit(1);
1921.41Sjoerg}
1931.1Scgd
1941.43Schristos#define OPT_HELP 1
1951.43Schristos
1961.43Schristosstruct option longopts[] = {
1971.43Schristos	{ "debug",		optional_argument,	0, 'd' },
1981.43Schristos	{ "define",		required_argument,	0, 'D' },
1991.43Schristos	{ "error-output",	required_argument,	0, 'e' },
2001.43Schristos	{ "fatal-warnings",	no_argument,		0, 'E' },
2011.43Schristos	{ "freeze-state",	required_argument,	0, 'F' },
2021.43Schristos	{ "gnu",		no_argument,		0, 'g' },
2031.43Schristos	{ "help",		no_argument,		0, OPT_HELP },
2041.43Schristos	{ "include",		required_argument,	0, 'I' },
2051.43Schristos	{ "interactive",	no_argument,		0, 'i' },
2061.43Schristos	{ "nesting-limit",	required_argument,	0, 'L' },
2071.43Schristos	{ "prefix-builtins",	no_argument,		0, 'P' },
2081.43Schristos	{ "quiet",		no_argument,		0, 'Q' },
2091.43Schristos	{ "reload-state",	required_argument,	0, 'R' },
2101.43Schristos	{ "silent",		no_argument,		0, 'Q' },
2111.43Schristos	{ "synclines",		no_argument,		0, 's' },
2121.43Schristos	{ "trace",		required_argument,	0, 't' },
2131.43Schristos	{ "traditional",	no_argument,		0, 'G' },
2141.43Schristos	{ "undefine",		required_argument,	0, 'U' },
2151.43Schristos	{ "version",		no_argument,		0, 'v' },
2161.43Schristos#ifdef notyet
2171.43Schristos	{ "arglength",		required_argument,	0, 'l' },
2181.43Schristos	{ "debugfile",		optional_argument, 	0, OPT_DEBUGFILE },
2191.43Schristos	{ "hashsize",		required_argument,	0, 'H' },
2201.43Schristos	{ "warn-macro-sequence",optional_argument,	0, OPT_WARN_SEQUENCE },
2211.43Schristos#endif
2221.43Schristos	{ 0,			0,			0, 0 },
2231.43Schristos};
2241.43Schristos
2251.8Sglassint
2261.38Schristosmain(int argc, char *argv[])
2271.8Sglass{
2281.13Slukem	int c;
2291.13Slukem	int n;
2301.8Sglass	char *p;
2311.8Sglass
2321.39Sjoerg	setprogname(argv[0]);
2331.39Sjoerg
2341.8Sglass	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
2351.8Sglass		signal(SIGINT, onintr);
2361.1Scgd
2371.38Schristos	init_macros();
2381.27Stv	initspaces();
2391.27Stv	STACKMAX = INITSTACKMAX;
2401.27Stv
2411.38Schristos	mstack = (stae *)xalloc(sizeof(stae) * STACKMAX, NULL);
2421.38Schristos	sstack = (char *)xalloc(STACKMAX, NULL);
2431.1Scgd
2441.27Stv	maxout = 0;
2451.27Stv	outfile = NULL;
2461.27Stv	resizedivs(MAXOUT);
2471.1Scgd
2481.45Schristos	while ((c = getopt_long(argc, argv, "D:d:e:EF:GgI:iL:o:PR:Qst:U:v",
2491.43Schristos	    longopts, NULL)) != -1)
2501.38Schristos		switch(c) {
2511.8Sglass		case 'D':               /* define something..*/
2521.8Sglass			for (p = optarg; *p; p++)
2531.8Sglass				if (*p == '=')
2541.8Sglass					break;
2551.8Sglass			if (*p)
2561.8Sglass				*p++ = EOS;
2571.8Sglass			dodefine(optarg, p);
2581.8Sglass			break;
2591.43Schristos		case 'd':
2601.43Schristos			set_trace_flags(optarg);
2611.43Schristos			break;
2621.43Schristos		case 'E':
2631.43Schristos			fatal_warnings++;
2641.43Schristos			break;
2651.43Schristos		case 'e':
2661.43Schristos			if (freopen(optarg, "w+", stderr) == NULL)
2671.43Schristos				err(EXIT_FAILURE, "Can't redirect errors to `%s'",
2681.43Schristos				    optarg);
2691.43Schristos			break;
2701.43Schristos		case 'F':
2711.43Schristos			freeze = optarg;
2721.43Schristos#ifndef REAL_FREEZE
2731.43Schristos			if ((freezef = fopen(freeze, "w")) == NULL)
2741.43Schristos				err(EXIT_FAILURE, "Can't open `%s'", freeze);
2751.43Schristos#endif
2761.43Schristos			break;
2771.27Stv		case 'I':
2781.27Stv			addtoincludepath(optarg);
2791.27Stv			break;
2801.43Schristos		case 'i':
2811.43Schristos			setvbuf(stdout, NULL, _IONBF, 0);
2821.43Schristos			signal(SIGINT, SIG_IGN);
2831.27Stv			break;
2841.43Schristos		case 'G':
2851.43Schristos			mimic_gnu = 0;
2861.38Schristos			break;
2871.38Schristos		case 'g':
2881.38Schristos			mimic_gnu = 1;
2891.8Sglass			break;
2901.43Schristos		case 'L':
2911.43Schristos			nesting_limit = atoi(optarg);
2921.43Schristos			break;
2931.43Schristos		case 'o':
2941.43Schristos			trace_file(optarg);
2951.43Schristos                        break;
2961.43Schristos		case 'P':
2971.43Schristos			prefix_builtins = 1;
2981.43Schristos			break;
2991.43Schristos		case 'Q':
3001.43Schristos			quiet++;
3011.43Schristos			break;
3021.43Schristos		case 'R':
3031.43Schristos			reload = optarg;
3041.27Stv			break;
3051.38Schristos		case 's':
3061.38Schristos			synch_lines = 1;
3071.38Schristos			break;
3081.38Schristos		case 't':
3091.38Schristos			mark_traced(optarg, 1);
3101.27Stv			break;
3111.43Schristos		case 'U':               /* undefine...       */
3121.43Schristos			macro_popdef(optarg);
3131.43Schristos			break;
3141.43Schristos		case 'v':
3151.43Schristos			fprintf(stderr, "%s version %d\n", getprogname(),
3161.43Schristos			    VERSION);
3171.43Schristos			return EXIT_SUCCESS;
3181.43Schristos		case OPT_HELP:
3191.43Schristos			help();
3201.43Schristos			return EXIT_SUCCESS;
3211.8Sglass		case '?':
3221.43Schristos		default:
3231.43Schristos			usage(stderr);
3241.43Schristos			return EXIT_FAILURE;
3251.2Sglass		}
3261.1Scgd
3271.43Schristos#ifdef REDIRECT
3281.46Schristos	/*
3291.46Schristos	 * This is meant only for debugging; it makes all output
3301.46Schristos	 * go to a known file, even if the command line options
3311.46Schristos	 * send it elsewhere. It should not be turned of in production code.
3321.46Schristos	 */
3331.43Schristos	if (freopen("/tmp/m4", "w+", stderr) == NULL)
3341.43Schristos		err(EXIT_FAILURE, "Can't redirect errors to `%s'",
3351.43Schristos		    "/tmp/m4");
3361.43Schristos#endif
3371.8Sglass        argc -= optind;
3381.8Sglass        argv += optind;
3391.2Sglass
3401.43Schristos
3411.38Schristos	initkwds();
3421.38Schristos	if (mimic_gnu)
3431.38Schristos		setup_builtin("format", FORMATTYPE);
3441.38Schristos
3451.8Sglass	active = stdout;		/* default active output     */
3461.8Sglass	bbase[0] = bufbase;
3471.43Schristos
3481.43Schristos	if (reload) {
3491.43Schristos#ifdef REAL_FREEZE
3501.43Schristos		thaw_state(reload);
3511.43Schristos#else
3521.43Schristos		if (fopen_trypath(infile, reload) == NULL)
3531.43Schristos			err(1, "Can't open `%s'", reload);
3541.43Schristos		sp = -1;
3551.43Schristos		fp = 0;
3561.43Schristos		thawing = 1;
3571.43Schristos		macro();
3581.43Schristos		thawing = 0;
3591.43Schristos		release_input(infile);
3601.43Schristos#endif
3611.43Schristos	}
3621.43Schristos
3631.8Sglass        if (!argc) {
3641.8Sglass 		sp = -1;		/* stack pointer initialized */
3651.8Sglass		fp = 0; 		/* frame pointer initialized */
3661.27Stv		set_input(infile+0, stdin, "stdin");
3671.27Stv					/* default input (naturally) */
3681.8Sglass		macro();
3691.8Sglass	} else
3701.8Sglass		for (; argc--; ++argv) {
3711.8Sglass			p = *argv;
3721.27Stv			if (p[0] == '-' && p[1] == EOS)
3731.27Stv				set_input(infile, stdin, "stdin");
3741.27Stv			else if (fopen_trypath(infile, p) == NULL)
3751.13Slukem				err(1, "%s", p);
3761.8Sglass			sp = -1;
3771.8Sglass			fp = 0;
3781.8Sglass			macro();
3791.27Stv		    	release_input(infile);
3801.8Sglass		}
3811.2Sglass
3821.38Schristos	if (wrapindex) {
3831.38Schristos		int i;
3841.38Schristos
3851.8Sglass		ilevel = 0;		/* in case m4wrap includes.. */
3861.8Sglass		bufbase = bp = buf;	/* use the entire buffer   */
3871.38Schristos		if (mimic_gnu) {
3881.38Schristos			while (wrapindex != 0) {
3891.38Schristos				for (i = 0; i < wrapindex; i++)
3901.38Schristos					pbstr(m4wraps[i]);
3911.38Schristos				wrapindex =0;
3921.38Schristos				macro();
3931.38Schristos			}
3941.38Schristos		} else {
3951.38Schristos			for (i = 0; i < wrapindex; i++) {
3961.38Schristos				pbstr(m4wraps[i]);
3971.38Schristos				macro();
3981.38Schristos		    	}
3991.38Schristos		}
4001.8Sglass	}
4011.2Sglass
4021.8Sglass	if (active != stdout)
4031.8Sglass		active = stdout;	/* reset output just in case */
4041.27Stv	for (n = 1; n < maxout; n++)	/* default wrap-up: undivert */
4051.8Sglass		if (outfile[n] != NULL)
4061.8Sglass			getdiv(n);
4071.27Stv					/* remove bitbucket if used  */
4081.27Stv	if (outfile[0] != NULL) {
4091.8Sglass		(void) fclose(outfile[0]);
4101.27Stv	}
4111.8Sglass
4121.43Schristos#ifdef REAL_FREEZE
4131.43Schristos	if (freeze)
4141.43Schristos		freeze_state(freeze);
4151.43Schristos#else
4161.43Schristos	if (freezef)
4171.43Schristos		fclose(freezef);
4181.43Schristos#endif
4191.43Schristos
4201.8Sglass	return 0;
4211.8Sglass}
4221.2Sglass
4231.8Sglass/*
4241.27Stv * Look ahead for `token'.
4251.11Spk * (on input `t == token[0]')
4261.11Spk * Used for comment and quoting delimiters.
4271.11Spk * Returns 1 if `token' present; copied to output.
4281.11Spk *         0 if `token' not found; all characters pushed back
4291.11Spk */
4301.27Stvstatic int
4311.38Schristosdo_look_ahead(int t, const char *token)
4321.11Spk{
4331.11Spk	int i;
4341.11Spk
4351.27Stv	assert((unsigned char)t == (unsigned char)token[0]);
4361.11Spk
4371.11Spk	for (i = 1; *++token; i++) {
4381.11Spk		t = gpbc();
4391.27Stv		if (t == EOF || (unsigned char)t != (unsigned char)*token) {
4401.38Schristos			pushback(t);
4411.11Spk			while (--i)
4421.38Schristos				pushback(*--token);
4431.11Spk			return 0;
4441.11Spk		}
4451.11Spk	}
4461.11Spk	return 1;
4471.11Spk}
4481.11Spk
4491.27Stv#define LOOK_AHEAD(t, token) (t != EOF && 		\
4501.27Stv    (unsigned char)(t)==(unsigned char)(token)[0] && 	\
4511.27Stv    do_look_ahead(t,token))
4521.11Spk
4531.11Spk/*
4541.8Sglass * macro - the work horse..
4551.8Sglass */
4561.27Stvstatic void
4571.38Schristosmacro(void)
4581.13Slukem{
4591.27Stv	char token[MAXTOK+1];
4601.13Slukem	int t, l;
4611.13Slukem	ndptr p;
4621.13Slukem	int  nlpar;
4631.1Scgd
4641.8Sglass	cycle {
4651.11Spk		t = gpbc();
4661.27Stv
4671.38Schristos		if (LOOK_AHEAD(t,lquote)) {	/* strip quotes */
4681.27Stv			nlpar = 0;
4691.27Stv			record(quotes, nlpar++);
4701.27Stv			/*
4711.27Stv			 * Opening quote: scan forward until matching
4721.27Stv			 * closing quote has been found.
4731.27Stv			 */
4741.8Sglass			do {
4751.12Scgd
4761.11Spk				l = gpbc();
4771.12Scgd				if (LOOK_AHEAD(l,rquote)) {
4781.27Stv					if (--nlpar > 0)
4791.27Stv						outputstr(rquote);
4801.12Scgd				} else if (LOOK_AHEAD(l,lquote)) {
4811.27Stv					record(quotes, nlpar++);
4821.27Stv					outputstr(lquote);
4831.27Stv				} else if (l == EOF) {
4841.43Schristos					if (!quiet) {
4851.43Schristos						if (nlpar == 1)
4861.43Schristos							warnx("unclosed quote:");
4871.43Schristos						else
4881.43Schristos							warnx(
4891.43Schristos							    "%d unclosed quotes:",
4901.43Schristos							    nlpar);
4911.43Schristos						dump_stack(quotes, nlpar);
4921.43Schristos					}
4931.43Schristos					exit(EXIT_FAILURE);
4941.27Stv				} else {
4951.27Stv					if (nlpar > 0) {
4961.27Stv						if (sp < 0)
4971.38Schristos							reallyputchar(l);
4981.27Stv						else
4991.27Stv							CHRSAVE(l);
5001.27Stv					}
5011.2Sglass				}
5021.8Sglass			}
5031.8Sglass			while (nlpar != 0);
5041.38Schristos		} else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
5051.38Schristos			reallyoutputstr(scommt);
5061.11Spk
5071.11Spk			for(;;) {
5081.11Spk				t = gpbc();
5091.11Spk				if (LOOK_AHEAD(t, ecommt)) {
5101.38Schristos					reallyoutputstr(ecommt);
5111.11Spk					break;
5121.11Spk				}
5131.11Spk				if (t == EOF)
5141.11Spk					break;
5151.38Schristos				reallyputchar(t);
5161.1Scgd			}
5171.38Schristos		} else if (t == '_' || isalpha(t)) {
5181.38Schristos			p = inspect(t, token);
5191.38Schristos			if (p != NULL)
5201.38Schristos				pushback(l = gpbc());
5211.38Schristos			if (p == NULL || (l != LPAREN &&
5221.38Schristos			    (macro_getdef(p)->type & NEEDARGS) != 0))
5231.38Schristos				outputstr(token);
5241.38Schristos			else {
5251.38Schristos		/*
5261.38Schristos		 * real thing.. First build a call frame:
5271.38Schristos		 */
5281.38Schristos				pushf(fp);	/* previous call frm */
5291.38Schristos				pushf(macro_getdef(p)->type); /* type of the call  */
5301.38Schristos				pushf(is_traced(p));
5311.38Schristos				pushf(0);	/* parenthesis level */
5321.38Schristos				fp = sp;	/* new frame pointer */
5331.38Schristos		/*
5341.38Schristos		 * now push the string arguments:
5351.38Schristos		 */
5361.38Schristos				pushs1(macro_getdef(p)->defn);	/* defn string */
5371.38Schristos				pushs1((char *)macro_name(p));	/* macro name  */
5381.38Schristos				pushs(ep);	      	/* start next..*/
5391.38Schristos
5401.38Schristos				if (l != LPAREN && PARLEV == 0)  {
5411.38Schristos				    /* no bracks  */
5421.38Schristos					chrsave(EOS);
5431.38Schristos
5441.38Schristos					if ((size_t)sp == STACKMAX)
5451.38Schristos						errx(1, "internal stack overflow");
5461.38Schristos					eval((const char **) mstack+fp+1, 2,
5471.38Schristos					    CALTYP, TRACESTATUS);
5481.11Spk
5491.38Schristos					ep = PREVEP;	/* flush strspace */
5501.38Schristos					sp = PREVSP;	/* previous sp..  */
5511.38Schristos					fp = PREVFP;	/* rewind stack...*/
5521.38Schristos				}
5531.38Schristos			}
5541.38Schristos		} else if (t == EOF) {
5551.38Schristos			if (sp > -1 && ilevel <= 0) {
5561.43Schristos				if (!quiet) {
5571.43Schristos					warnx("unexpected end of input, "
5581.43Schristos					    "unclosed parenthesis:");
5591.43Schristos					dump_stack(paren, PARLEV);
5601.43Schristos				}
5611.43Schristos				exit(EXIT_FAILURE);
5621.38Schristos			}
5631.38Schristos			if (ilevel <= 0)
5641.38Schristos				break;			/* all done thanks.. */
5651.38Schristos			release_input(infile+ilevel--);
5661.38Schristos			emit_synchline();
5671.38Schristos			bufbase = bbase[ilevel];
5681.38Schristos			continue;
5691.38Schristos		} else if (sp < 0) {		/* not in a macro at all */
5701.38Schristos			reallyputchar(t);	/* output directly..	 */
5711.8Sglass		}
5721.1Scgd
5731.8Sglass		else switch(t) {
5741.2Sglass
5751.8Sglass		case LPAREN:
5761.8Sglass			if (PARLEV > 0)
5771.8Sglass				chrsave(t);
5781.38Schristos			while (isspace(l = gpbc())) /* skip blank, tab, nl.. */
5791.38Schristos				if (PARLEV > 0)
5801.38Schristos					chrsave(l);
5811.38Schristos			pushback(l);
5821.27Stv			record(paren, PARLEV++);
5831.8Sglass			break;
5841.1Scgd
5851.8Sglass		case RPAREN:
5861.8Sglass			if (--PARLEV > 0)
5871.8Sglass				chrsave(t);
5881.8Sglass			else {			/* end of argument list */
5891.8Sglass				chrsave(EOS);
5901.8Sglass
5911.38Schristos				if ((size_t)sp == STACKMAX)
5921.13Slukem					errx(1, "internal stack overflow");
5931.8Sglass
5941.27Stv				eval((const char **) mstack+fp+1, sp-fp,
5951.38Schristos				    CALTYP, TRACESTATUS);
5961.8Sglass
5971.8Sglass				ep = PREVEP;	/* flush strspace */
5981.8Sglass				sp = PREVSP;	/* previous sp..  */
5991.8Sglass				fp = PREVFP;	/* rewind stack...*/
6001.8Sglass			}
6011.8Sglass			break;
6021.1Scgd
6031.8Sglass		case COMMA:
6041.8Sglass			if (PARLEV == 1) {
6051.8Sglass				chrsave(EOS);		/* new argument   */
6061.8Sglass				while (isspace(l = gpbc()))
6071.8Sglass					;
6081.38Schristos				pushback(l);
6091.8Sglass				pushs(ep);
6101.8Sglass			} else
6111.8Sglass				chrsave(t);
6121.8Sglass			break;
6131.2Sglass
6141.2Sglass		default:
6151.27Stv			if (LOOK_AHEAD(t, scommt)) {
6161.38Schristos				char *q;
6171.42Schristos				for (q = scommt; *q; q++)
6181.38Schristos					chrsave(*q);
6191.27Stv				for(;;) {
6201.27Stv					t = gpbc();
6211.27Stv					if (LOOK_AHEAD(t, ecommt)) {
6221.38Schristos						for (q = ecommt; *q; q++)
6231.38Schristos							chrsave(*q);
6241.27Stv						break;
6251.27Stv					}
6261.27Stv					if (t == EOF)
6271.27Stv					    break;
6281.27Stv					CHRSAVE(t);
6291.27Stv				}
6301.27Stv			} else
6311.27Stv				CHRSAVE(t);		/* stack the char */
6321.8Sglass			break;
6331.8Sglass		}
6341.2Sglass	}
6351.8Sglass}
6361.2Sglass
6371.27Stv/*
6381.27Stv * output string directly, without pushing it for reparses.
6391.27Stv */
6401.27Stvvoid
6411.38Schristosoutputstr(const char *s)
6421.27Stv{
6431.27Stv	if (sp < 0)
6441.38Schristos		reallyoutputstr(s);
6451.27Stv	else
6461.27Stv		while (*s)
6471.27Stv			CHRSAVE(*s++);
6481.27Stv}
6491.27Stv
6501.38Schristosvoid
6511.38Schristosreallyoutputstr(const char *s)
6521.38Schristos{
6531.38Schristos	if (synch_lines) {
6541.38Schristos		while (*s) {
6551.38Schristos			fputc(*s, active);
6561.38Schristos			if (*s++ == '\n') {
6571.38Schristos				infile[ilevel].synch_lineno++;
6581.38Schristos				if (infile[ilevel].synch_lineno !=
6591.38Schristos				    infile[ilevel].lineno)
6601.38Schristos					do_emit_synchline();
6611.38Schristos			}
6621.38Schristos		}
6631.38Schristos	} else
6641.38Schristos		fputs(s, active);
6651.38Schristos}
6661.38Schristos
6671.38Schristosvoid
6681.38Schristosreallyputchar(int c)
6691.38Schristos{
6701.38Schristos	putc(c, active);
6711.38Schristos	if (synch_lines && c == '\n') {
6721.38Schristos		infile[ilevel].synch_lineno++;
6731.38Schristos		if (infile[ilevel].synch_lineno != infile[ilevel].lineno)
6741.38Schristos			do_emit_synchline();
6751.38Schristos	}
6761.38Schristos}
6771.38Schristos
6781.8Sglass/*
6791.8Sglass * build an input token..
6801.38Schristos * consider only those starting with _ or A-Za-z.
6811.8Sglass */
6821.27Stvstatic ndptr
6831.38Schristosinspect(int c, char *tp)
6841.8Sglass{
6851.13Slukem	char *name = tp;
6861.13Slukem	char *etp = tp+MAXTOK;
6871.13Slukem	ndptr p;
6881.27Stv
6891.38Schristos	*tp++ = c;
6901.2Sglass
6911.27Stv	while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
6921.38Schristos		*tp++ = c;
6931.27Stv	if (c != EOF)
6941.38Schristos		PUSHBACK(c);
6951.8Sglass	*tp = EOS;
6961.27Stv	/* token is too long, it won't match anything, but it can still
6971.27Stv	 * be output. */
6981.27Stv	if (tp == ep) {
6991.27Stv		outputstr(name);
7001.27Stv		while (isalnum(c = gpbc()) || c == '_') {
7011.27Stv			if (sp < 0)
7021.38Schristos				reallyputchar(c);
7031.27Stv			else
7041.27Stv				CHRSAVE(c);
7051.27Stv		}
7061.27Stv		*name = EOS;
7071.38Schristos		return NULL;
7081.27Stv	}
7091.1Scgd
7101.40Schristos	p = ohash_find(&macros, ohash_qlookupi(&macros, name, (void *)&tp));
7111.38Schristos	if (p == NULL)
7121.38Schristos		return NULL;
7131.38Schristos	if (macro_getdef(p) == NULL)
7141.38Schristos		return NULL;
7151.8Sglass	return p;
7161.8Sglass}
7171.7Scgd
7181.8Sglass/*
7191.38Schristos * initkwds - initialise m4 keywords as fast as possible.
7201.8Sglass * This very similar to install, but without certain overheads,
7211.38Schristos * such as calling lookup. Malloc is not used for storing the
7221.13Slukem * keyword strings, since we simply use the static pointers
7231.8Sglass * within keywrds block.
7241.8Sglass */
7251.27Stvstatic void
7261.38Schristosinitkwds(void)
7271.13Slukem{
7281.38Schristos	unsigned int type;
7291.32Stv	size_t i;
7301.1Scgd
7311.8Sglass	for (i = 0; i < MAXKEYS; i++) {
7321.43Schristos		type = keywrds[i].ktyp;
7331.27Stv		if ((keywrds[i].ktyp & NOARGS) == 0)
7341.38Schristos			type |= NEEDARGS;
7351.38Schristos		setup_builtin(keywrds[i].knam, type);
7361.1Scgd	}
7371.27Stv}
7381.27Stv
7391.27Stvstatic void
7401.38Schristosrecord(struct position *t, int lev)
7411.27Stv{
7421.27Stv	if (lev < MAXRECORD) {
7431.27Stv		t[lev].name = CURRENT_NAME;
7441.27Stv		t[lev].line = CURRENT_LINE;
7451.27Stv	}
7461.27Stv}
7471.27Stv
7481.27Stvstatic void
7491.38Schristosdump_stack(struct position *t, int lev)
7501.27Stv{
7511.27Stv	int i;
7521.27Stv
7531.27Stv	for (i = 0; i < lev; i++) {
7541.27Stv		if (i == MAXRECORD) {
7551.27Stv			fprintf(stderr, "   ...\n");
7561.27Stv			break;
7571.27Stv		}
7581.27Stv		fprintf(stderr, "   %s at line %lu\n",
7591.27Stv			t[i].name, t[i].line);
7601.27Stv	}
7611.27Stv}
7621.27Stv
7631.27Stv
7641.27Stvstatic void
7651.38Schristosenlarge_stack(void)
7661.27Stv{
7671.38Schristos	STACKMAX += STACKMAX/2;
7681.38Schristos	mstack = xrealloc(mstack, sizeof(stae) * STACKMAX,
7691.38Schristos	    "Evaluation stack overflow (%lu)",
7701.38Schristos	    (unsigned long)STACKMAX);
7711.38Schristos	sstack = xrealloc(sstack, STACKMAX,
7721.38Schristos	    "Evaluation stack overflow (%lu)",
7731.38Schristos	    (unsigned long)STACKMAX);
7741.8Sglass}
7751.43Schristos
7761.43Schristosstatic const struct {
7771.43Schristos	const char *n;
7781.43Schristos	const char *d;
7791.43Schristos} nd [] = {
7801.43Schristos{ "-d, --debug[=flags]",	"set debug flags" },
7811.43Schristos{ "-D, --define=name[=value]",	"define macro" },
7821.43Schristos{ "-e, --error-output=file",	"send error output to file" },
7831.43Schristos{ "-E, --fatal-warnings",	"exit on warnings" },
7841.43Schristos{ "-F, --freeze-state=file",	"save state to file" },
7851.43Schristos{ "-g, --gnu",			"enable gnu extensions" },
7861.43Schristos{ "    --help",			"print this message and exit" },
7871.43Schristos{ "-I, --include=file",		"include file" },
7881.43Schristos{ "-i, --interactive",		"unbuffer output, ignore tty signals" },
7891.44Schristos{ "-L, --nesting-limit=num",	"macro expansion nesting limit (unimpl)" },
7901.43Schristos{ "-P, --prefix-builtins",	"prefix builtins with m4_" },
7911.43Schristos{ "-Q, --quiet",		"don't print warnings" },
7921.43Schristos{ "-R, --reload-state=file",	"restore state from file" },
7931.43Schristos{ "-Q, --silent",		"don't print warnings" },
7941.43Schristos{ "-s, --synclines",		"output line directives for cpp(1)" },
7951.43Schristos{ "-t, --trace=macro",		"trace the named macro" },
7961.43Schristos{ "-G, --traditional",		"disable gnu extensions" },
7971.43Schristos{ "-U, --undefine=name",	"undefine the named macro" },
7981.43Schristos{ "-v, --version",		"print the version number and exit" },
7991.43Schristos};
8001.43Schristos
8011.43Schristosstatic void
8021.43Schristoshelp(void)
8031.43Schristos{
8041.43Schristos	size_t i;
8051.43Schristos	fprintf(stdout, "%s version %d\n\n", getprogname(), VERSION);
8061.43Schristos	usage(stdout);
8071.43Schristos
8081.43Schristos	fprintf(stdout, "\nThe long options are:\n");
8091.43Schristos	for (i = 0; i < __arraycount(nd); i++)
8101.43Schristos		fprintf(stdout, "\t%-25.25s\t%s\n", nd[i].n, nd[i].d);
8111.43Schristos}
812