main.c revision 65428
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 65428 2000-09-04 06:09:54Z imp $";
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 <string.h>
63#include <unistd.h>
64#include "mdef.h"
65#include "stdd.h"
66#include "extern.h"
67#include "pathnames.h"
68
69ndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
70unsigned char buf[BUFSIZE];              /* push-back buffer            */
71unsigned char *bufbase = buf;            /* the base for current ilevel */
72unsigned char *bbase[MAXINP];            /* the base for each ilevel    */
73unsigned char *bp = buf;                 /* first available character   */
74unsigned char *endpbb = buf+BUFSIZE;     /* end of push-back buffer     */
75stae mstack[STACKMAX+1]; 	/* stack of m4 machine         */
76char strspace[STRSPMAX+1];	/* string space for evaluation */
77char *ep = strspace;		/* first free char in strspace */
78char *endest= strspace+STRSPMAX;/* end of string space	       */
79int sp; 			/* current m4  stack pointer   */
80int fp; 			/* m4 call frame pointer       */
81FILE *infile[MAXINP];		/* input file stack (0=stdin)  */
82FILE *outfile[MAXOUT];		/* diversion array(0=bitbucket)*/
83FILE *active;			/* active output file pointer  */
84char *m4temp;			/* filename for diversions     */
85int ilevel = 0; 		/* input file stack pointer    */
86int oindex = 0; 		/* diversion index..	       */
87char *null = "";                /* as it says.. just a null..  */
88char *m4wraps = "";             /* m4wrap string default..     */
89char lquote = LQUOTE;		/* left quote character  (`)   */
90char rquote = RQUOTE;		/* right quote character (')   */
91char scommt = SCOMMT;		/* start character for comment */
92char ecommt = ECOMMT;		/* end character for comment   */
93
94struct keyblk keywrds[] = {	/* m4 keywords to be installed */
95	"include",      INCLTYPE,
96	"sinclude",     SINCTYPE,
97	"define",       DEFITYPE,
98	"defn",         DEFNTYPE,
99	"divert",       DIVRTYPE,
100	"expr",         EXPRTYPE,
101	"eval",         EXPRTYPE,
102	"substr",       SUBSTYPE,
103	"ifelse",       IFELTYPE,
104	"ifdef",        IFDFTYPE,
105	"len",          LENGTYPE,
106	"incr",         INCRTYPE,
107	"decr",         DECRTYPE,
108	"dnl",          DNLNTYPE,
109	"changequote",  CHNQTYPE,
110	"changecom",    CHNCTYPE,
111	"index",        INDXTYPE,
112#ifdef EXTENDED
113	"paste",        PASTTYPE,
114	"spaste",       SPASTYPE,
115#endif
116	"popdef",       POPDTYPE,
117	"pushdef",      PUSDTYPE,
118	"dumpdef",      DUMPTYPE,
119	"shift",        SHIFTYPE,
120	"translit",     TRNLTYPE,
121	"undefine",     UNDFTYPE,
122	"undivert",     UNDVTYPE,
123	"divnum",       DIVNTYPE,
124	"maketemp",     MKTMTYPE,
125	"errprint",     ERRPTYPE,
126	"m4wrap",       M4WRTYPE,
127	"m4exit",       EXITTYPE,
128	"syscmd",       SYSCTYPE,
129	"sysval",       SYSVTYPE,
130
131#ifdef unix
132	"unix",         MACRTYPE,
133#else
134#ifdef vms
135	"vms",          MACRTYPE,
136#endif
137#endif
138};
139
140#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
141
142void macro();
143void initkwds();
144
145int
146main(argc,argv)
147	int argc;
148	char *argv[];
149{
150	register int c;
151	register int n;
152	char *p;
153	register FILE *ifp;
154
155	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
156		signal(SIGINT, onintr);
157
158	initkwds();
159
160	while ((c = getopt(argc, argv, "tD:U:o:")) != -1)
161		switch(c) {
162
163		case 'D':               /* define something..*/
164			for (p = optarg; *p; p++)
165				if (*p == '=')
166					break;
167			if (*p)
168				*p++ = EOS;
169			dodefine(optarg, p);
170			break;
171		case 'U':               /* undefine...       */
172			remhash(optarg, TOP);
173			break;
174		case 'o':		/* specific output   */
175		case '?':
176			usage();
177		}
178
179        argc -= optind;
180        argv += optind;
181
182	active = stdout;		/* default active output     */
183					/* filename for diversions   */
184	m4temp = mktemp(xstrdup(_PATH_DIVNAME));
185
186	bbase[0] = bufbase;
187        if (!argc) {
188 		sp = -1;		/* stack pointer initialized */
189		fp = 0; 		/* frame pointer initialized */
190		infile[0] = stdin;	/* default input (naturally) */
191		macro();
192	} else
193		for (; argc--; ++argv) {
194			p = *argv;
195			if (p[0] == '-' && p[1] == '\0')
196				ifp = stdin;
197			else if ((ifp = fopen(p, "r")) == NULL)
198				err(1, "%s", p);
199			sp = -1;
200			fp = 0;
201			infile[0] = ifp;
202			macro();
203			if (ifp != stdin)
204				(void)fclose(ifp);
205		}
206
207	if (*m4wraps) { 		/* anything for rundown ??   */
208		ilevel = 0;		/* in case m4wrap includes.. */
209		bufbase = bp = buf;	/* use the entire buffer   */
210		putback(EOF);		/* eof is a must !!	     */
211		pbstr(m4wraps); 	/* user-defined wrapup act   */
212		macro();		/* last will and testament   */
213	}
214
215	if (active != stdout)
216		active = stdout;	/* reset output just in case */
217	for (n = 1; n < MAXOUT; n++)	/* default wrap-up: undivert */
218		if (outfile[n] != NULL)
219			getdiv(n);
220					/* remove bitbucket if used  */
221	if (outfile[0] != NULL) {
222		(void) fclose(outfile[0]);
223		m4temp[UNIQUE] = '0';
224#ifdef vms
225		(void) remove(m4temp);
226#else
227		(void) unlink(m4temp);
228#endif
229	}
230
231	return 0;
232}
233
234ndptr inspect();
235
236/*
237 * macro - the work horse..
238 */
239void
240macro() {
241	char token[MAXTOK];
242	register char *s;
243	register int t, l;
244	register ndptr p;
245	register int  nlpar;
246
247	cycle {
248		if ((t = gpbc()) == '_' || (t != EOF && isalpha(t))) {
249			putback(t);
250			if ((p = inspect(s = token)) == nil) {
251				if (sp < 0)
252					while (*s)
253						putc(*s++, active);
254				else
255					while (*s)
256						chrsave(*s++);
257			}
258			else {
259		/*
260		 * real thing.. First build a call frame:
261		 */
262				pushf(fp);	/* previous call frm */
263				pushf(p->type); /* type of the call  */
264				pushf(0);	/* parenthesis level */
265				fp = sp;	/* new frame pointer */
266		/*
267		 * now push the string arguments:
268		 */
269				pushs(p->defn);	      /* defn string */
270				pushs(p->name);	      /* macro name  */
271				pushs(ep);	      /* start next..*/
272
273				putback(l = gpbc());
274				if (l != LPAREN)  {   /* add bracks  */
275					putback(RPAREN);
276					putback(LPAREN);
277				}
278			}
279		}
280		else if (t == EOF) {
281			if (sp > -1)
282				errx(1, "unexpected end of input");
283			if (ilevel <= 0)
284				break;			/* all done thanks.. */
285			--ilevel;
286			(void) fclose(infile[ilevel+1]);
287			bufbase = bbase[ilevel];
288			continue;
289		}
290	/*
291	 * non-alpha single-char token seen..
292	 * [the order of else if .. stmts is important.]
293	 */
294		else if (t == lquote) { 		/* strip quotes */
295			nlpar = 1;
296			do {
297				if ((l = gpbc()) == rquote)
298					nlpar--;
299				else if (l == lquote)
300					nlpar++;
301				else if (l == EOF)
302					errx(1, "missing right quote");
303				if (nlpar > 0) {
304					if (sp < 0)
305						putc(l, active);
306					else
307						chrsave(l);
308				}
309			}
310			while (nlpar != 0);
311		}
312
313		else if (sp < 0) {		/* not in a macro at all */
314			if (t == scommt) {	/* comment handling here */
315				putc(t, active);
316				while ((t = gpbc()) != ecommt)
317					putc(t, active);
318			}
319			putc(t, active);	/* output directly..	 */
320		}
321
322		else switch(t) {
323
324		case LPAREN:
325			if (PARLEV > 0)
326				chrsave(t);
327			while ((l = gpbc()) != EOF && isspace(l))
328				;		/* skip blank, tab, nl.. */
329			putback(l);
330			PARLEV++;
331			break;
332
333		case RPAREN:
334			if (--PARLEV > 0)
335				chrsave(t);
336			else {			/* end of argument list */
337				chrsave(EOS);
338
339				if (sp == STACKMAX)
340					errx(1, "internal stack overflow");
341
342				if (CALTYP == MACRTYPE)
343					expand((char **) mstack+fp+1, sp-fp);
344				else
345					eval((char **) mstack+fp+1, sp-fp, CALTYP);
346
347				ep = PREVEP;	/* flush strspace */
348				sp = PREVSP;	/* previous sp..  */
349				fp = PREVFP;	/* rewind stack...*/
350			}
351			break;
352
353		case COMMA:
354			if (PARLEV == 1) {
355				chrsave(EOS);		/* new argument   */
356				while ((l = gpbc()) != EOF && isspace(l))
357					;
358				putback(l);
359				pushs(ep);
360			} else
361				chrsave(t);
362			break;
363
364		default:
365			chrsave(t);			/* stack the char */
366			break;
367		}
368	}
369}
370
371/*
372 * build an input token..
373 * consider only those starting with _ or A-Za-z. This is a
374 * combo with lookup to speed things up.
375 */
376ndptr
377inspect(tp)
378register char *tp;
379{
380	register int c;
381	register char *name = tp;
382	register char *etp = tp+MAXTOK;
383	register ndptr p;
384	register unsigned long h = 0;
385
386	while ((c = gpbc()) != EOF && (isalnum(c) || c == '_') && tp < etp)
387		h = (h << 5) + h + (*tp++ = c);
388	putback(c);
389	if (tp == etp)
390		errx(1, "token too long");
391
392	*tp = EOS;
393
394	for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
395		if (STREQ(name, p->name))
396			break;
397	return p;
398}
399
400/*
401 * initkwds - initialise m4 keywords as fast as possible.
402 * This very similar to install, but without certain overheads,
403 * such as calling lookup. Malloc is not used for storing the
404 * keyword strings, since we simply use the static  pointers
405 * within keywrds block.
406 */
407void
408initkwds() {
409	register int i;
410	register int h;
411	register ndptr p;
412
413	for (i = 0; i < MAXKEYS; i++) {
414		h = hash(keywrds[i].knam);
415		p = (ndptr) xalloc(sizeof(struct ndblock));
416		p->nxtptr = hashtab[h];
417		hashtab[h] = p;
418		p->name = keywrds[i].knam;
419		p->defn = null;
420		p->type = keywrds[i].ktyp | STATIC;
421	}
422}
423