main.c revision 80289
1/*-
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Ozan Yigit at York University.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38static const char copyright[] =
39"@(#) Copyright (c) 1989, 1993\n\
40	The Regents of the University of California.  All rights reserved.\n";
41#endif /* not lint */
42
43#ifndef lint
44#if 0
45static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
46#endif
47static const char rcsid[] =
48  "$FreeBSD: head/usr.bin/m4/main.c 80289 2001-07-24 14:09:47Z obrien $";
49#endif /* not lint */
50
51/*
52 * main.c
53 * Facility: m4 macro processor
54 * by: oz
55 */
56
57#include <sys/types.h>
58#include <ctype.h>
59#include <err.h>
60#include <signal.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
64#include <unistd.h>
65#include "mdef.h"
66#include "stdd.h"
67#include "extern.h"
68#include "pathnames.h"
69
70ndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
71unsigned char buf[BUFSIZE];              /* push-back buffer            */
72unsigned char *bufbase = buf;            /* the base for current ilevel */
73unsigned char *bbase[MAXINP];            /* the base for each ilevel    */
74unsigned char *bp = buf;                 /* first available character   */
75unsigned char *endpbb = buf+BUFSIZE;     /* end of push-back buffer     */
76stae mstack[STACKMAX+1]; 	/* stack of m4 machine         */
77char strspace[STRSPMAX+1];	/* string space for evaluation */
78char *ep = strspace;		/* first free char in strspace */
79char *endest= strspace+STRSPMAX;/* end of string space	       */
80int sp; 			/* current m4  stack pointer   */
81int fp; 			/* m4 call frame pointer       */
82FILE *infile[MAXINP];		/* input file stack (0=stdin)  */
83FILE *outfile[MAXOUT];		/* diversion array(0=bitbucket)*/
84FILE *active;			/* active output file pointer  */
85char *m4temp;			/* filename for diversions     */
86char *m4dir;			/* directory for diversions    */
87int ilevel = 0; 		/* input file stack pointer    */
88int oindex = 0; 		/* diversion index..	       */
89char *null = "";                /* as it says.. just a null..  */
90char *m4wraps = "";             /* m4wrap string default..     */
91char lquote = LQUOTE;		/* left quote character  (`)   */
92char rquote = RQUOTE;		/* right quote character (')   */
93char scommt = SCOMMT;		/* start character for comment */
94char ecommt = ECOMMT;		/* end character for comment   */
95
96struct keyblk keywrds[] = {	/* m4 keywords to be installed */
97	"include",      INCLTYPE,
98	"sinclude",     SINCTYPE,
99	"define",       DEFITYPE,
100	"defn",         DEFNTYPE,
101	"divert",       DIVRTYPE,
102	"expr",         EXPRTYPE,
103	"eval",         EXPRTYPE,
104	"substr",       SUBSTYPE,
105	"ifelse",       IFELTYPE,
106	"ifdef",        IFDFTYPE,
107	"len",          LENGTYPE,
108	"incr",         INCRTYPE,
109	"decr",         DECRTYPE,
110	"dnl",          DNLNTYPE,
111	"changequote",  CHNQTYPE,
112	"changecom",    CHNCTYPE,
113	"index",        INDXTYPE,
114#ifdef EXTENDED
115	"paste",        PASTTYPE,
116	"spaste",       SPASTYPE,
117#endif
118	"popdef",       POPDTYPE,
119	"pushdef",      PUSDTYPE,
120	"dumpdef",      DUMPTYPE,
121	"shift",        SHIFTYPE,
122	"translit",     TRNLTYPE,
123	"undefine",     UNDFTYPE,
124	"undivert",     UNDVTYPE,
125	"divnum",       DIVNTYPE,
126	"maketemp",     MKTMTYPE,
127	"errprint",     ERRPTYPE,
128	"m4wrap",       M4WRTYPE,
129	"m4exit",       EXITTYPE,
130	"syscmd",       SYSCTYPE,
131	"sysval",       SYSVTYPE,
132
133#ifdef unix
134	"unix",         MACRTYPE,
135#else
136#ifdef vms
137	"vms",          MACRTYPE,
138#endif
139#endif
140};
141
142#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
143
144void macro();
145void initkwds();
146
147int
148main(argc,argv)
149	int argc;
150	char *argv[];
151{
152	register int c;
153	register int n;
154	char *p;
155	register FILE *ifp;
156
157	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
158		signal(SIGINT, onintr);
159
160	initkwds();
161
162	while ((c = getopt(argc, argv, "tD:U:o:")) != -1)
163		switch(c) {
164
165		case 'D':               /* define something..*/
166			for (p = optarg; *p; p++)
167				if (*p == '=')
168					break;
169			if (*p)
170				*p++ = EOS;
171			dodefine(optarg, p);
172			break;
173		case 'U':               /* undefine...       */
174			remhash(optarg, TOP);
175			break;
176		case 'o':		/* specific output   */
177		case '?':
178			usage();
179		}
180
181        argc -= optind;
182        argv += optind;
183
184	active = stdout;		/* default active output     */
185	if ((p = strdup(_PATH_DIVDIRNAME)) == NULL)
186		err(1, "strdup");
187
188					/* filename for diversions   */
189	m4dir = mkdtemp(p);
190	err_set_exit(cleanup);
191	(void) asprintf(&m4temp, "%s/%s", m4dir, _PATH_DIVNAME);
192
193	bbase[0] = bufbase;
194        if (!argc) {
195 		sp = -1;		/* stack pointer initialized */
196		fp = 0; 		/* frame pointer initialized */
197		infile[0] = stdin;	/* default input (naturally) */
198		macro();
199	} else
200		for (; argc--; ++argv) {
201			p = *argv;
202			if (p[0] == '-' && p[1] == '\0')
203				ifp = stdin;
204			else if ((ifp = fopen(p, "r")) == NULL)
205				err(1, "%s", p);
206			sp = -1;
207			fp = 0;
208			infile[0] = ifp;
209			macro();
210			if (ifp != stdin)
211				(void)fclose(ifp);
212		}
213
214	if (*m4wraps) { 		/* anything for rundown ??   */
215		ilevel = 0;		/* in case m4wrap includes.. */
216		bufbase = bp = buf;	/* use the entire buffer   */
217		putback(EOF);		/* eof is a must !!	     */
218		pbstr(m4wraps); 	/* user-defined wrapup act   */
219		macro();		/* last will and testament   */
220	}
221
222	if (active != stdout)
223		active = stdout;	/* reset output just in case */
224	for (n = 1; n < MAXOUT; n++)	/* default wrap-up: undivert */
225		if (outfile[n] != NULL)
226			getdiv(n);
227					/* remove bitbucket if used  */
228	cleanup(0);
229	return 0;
230}
231
232ndptr inspect();
233
234/*
235 * macro - the work horse..
236 */
237void
238macro() {
239	char token[MAXTOK];
240	register char *s;
241	register int t, l;
242	register ndptr p;
243	register int  nlpar;
244
245	cycle {
246		if ((t = gpbc()) == '_' || (t != EOF && isalpha(t))) {
247			putback(t);
248			if ((p = inspect(s = token)) == nil) {
249				if (sp < 0)
250					while (*s)
251						putc(*s++, active);
252				else
253					while (*s)
254						chrsave(*s++);
255			}
256			else {
257		/*
258		 * real thing.. First build a call frame:
259		 */
260				pushf(fp);	/* previous call frm */
261				pushf(p->type); /* type of the call  */
262				pushf(0);	/* parenthesis level */
263				fp = sp;	/* new frame pointer */
264		/*
265		 * now push the string arguments:
266		 */
267				pushs(p->defn);	      /* defn string */
268				pushs(p->name);	      /* macro name  */
269				pushs(ep);	      /* start next..*/
270
271				putback(l = gpbc());
272				if (l != LPAREN)  {   /* add bracks  */
273					putback(RPAREN);
274					putback(LPAREN);
275				}
276			}
277		}
278		else if (t == EOF) {
279			if (sp > -1)
280				errx(1, "unexpected end of input");
281			if (ilevel <= 0)
282				break;			/* all done thanks.. */
283			--ilevel;
284			(void) fclose(infile[ilevel+1]);
285			bufbase = bbase[ilevel];
286			continue;
287		}
288	/*
289	 * non-alpha single-char token seen..
290	 * [the order of else if .. stmts is important.]
291	 */
292		else if (t == lquote) { 		/* strip quotes */
293			nlpar = 1;
294			do {
295				if ((l = gpbc()) == rquote)
296					nlpar--;
297				else if (l == lquote)
298					nlpar++;
299				else if (l == EOF)
300					errx(1, "missing right quote");
301				if (nlpar > 0) {
302					if (sp < 0)
303						putc(l, active);
304					else
305						chrsave(l);
306				}
307			}
308			while (nlpar != 0);
309		}
310
311		else if (sp < 0) {		/* not in a macro at all */
312			if (t == scommt) {	/* comment handling here */
313				putc(t, active);
314				while ((t = gpbc()) != ecommt)
315					putc(t, active);
316			}
317			putc(t, active);	/* output directly..	 */
318		}
319
320		else switch(t) {
321
322		case LPAREN:
323			if (PARLEV > 0)
324				chrsave(t);
325			while ((l = gpbc()) != EOF && isspace(l))
326				;		/* skip blank, tab, nl.. */
327			putback(l);
328			PARLEV++;
329			break;
330
331		case RPAREN:
332			if (--PARLEV > 0)
333				chrsave(t);
334			else {			/* end of argument list */
335				chrsave(EOS);
336
337				if (sp == STACKMAX)
338					errx(1, "internal stack overflow");
339
340				if (CALTYP == MACRTYPE)
341					expand((char **) mstack+fp+1, sp-fp);
342				else
343					eval((char **) mstack+fp+1, sp-fp, CALTYP);
344
345				ep = PREVEP;	/* flush strspace */
346				sp = PREVSP;	/* previous sp..  */
347				fp = PREVFP;	/* rewind stack...*/
348			}
349			break;
350
351		case COMMA:
352			if (PARLEV == 1) {
353				chrsave(EOS);		/* new argument   */
354				while ((l = gpbc()) != EOF && isspace(l))
355					;
356				putback(l);
357				pushs(ep);
358			} else
359				chrsave(t);
360			break;
361
362		default:
363			chrsave(t);			/* stack the char */
364			break;
365		}
366	}
367}
368
369/*
370 * build an input token..
371 * consider only those starting with _ or A-Za-z. This is a
372 * combo with lookup to speed things up.
373 */
374ndptr
375inspect(tp)
376register char *tp;
377{
378	register int c;
379	register char *name = tp;
380	register char *etp = tp+MAXTOK;
381	register ndptr p;
382	register unsigned long h = 0;
383
384	while ((c = gpbc()) != EOF && (isalnum(c) || c == '_') && tp < etp)
385		h = (h << 5) + h + (*tp++ = c);
386	putback(c);
387	if (tp == etp)
388		errx(1, "token too long");
389
390	*tp = EOS;
391
392	for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
393		if (STREQ(name, p->name))
394			break;
395	return p;
396}
397
398/*
399 * initkwds - initialise m4 keywords as fast as possible.
400 * This very similar to install, but without certain overheads,
401 * such as calling lookup. Malloc is not used for storing the
402 * keyword strings, since we simply use the static  pointers
403 * within keywrds block.
404 */
405void
406initkwds() {
407	register int i;
408	register int h;
409	register ndptr p;
410
411	for (i = 0; i < MAXKEYS; i++) {
412		h = hash(keywrds[i].knam);
413		if ((p = malloc(sizeof(struct ndblock))) == NULL)
414			err(1, "malloc");
415		p->nxtptr = hashtab[h];
416		hashtab[h] = p;
417		p->name = keywrds[i].knam;
418		p->defn = null;
419		p->type = keywrds[i].ktyp | STATIC;
420	}
421}
422