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