main.c revision 75551
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 75551 2001-04-16 18:36:35Z gshapiro $";
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	err_set_exit(cleanup);
187	(void) asprintf(&m4temp, "%s/%s", m4dir, _PATH_DIVNAME);
188
189	bbase[0] = bufbase;
190        if (!argc) {
191 		sp = -1;		/* stack pointer initialized */
192		fp = 0; 		/* frame pointer initialized */
193		infile[0] = stdin;	/* default input (naturally) */
194		macro();
195	} else
196		for (; argc--; ++argv) {
197			p = *argv;
198			if (p[0] == '-' && p[1] == '\0')
199				ifp = stdin;
200			else if ((ifp = fopen(p, "r")) == NULL)
201				err(1, "%s", p);
202			sp = -1;
203			fp = 0;
204			infile[0] = ifp;
205			macro();
206			if (ifp != stdin)
207				(void)fclose(ifp);
208		}
209
210	if (*m4wraps) { 		/* anything for rundown ??   */
211		ilevel = 0;		/* in case m4wrap includes.. */
212		bufbase = bp = buf;	/* use the entire buffer   */
213		putback(EOF);		/* eof is a must !!	     */
214		pbstr(m4wraps); 	/* user-defined wrapup act   */
215		macro();		/* last will and testament   */
216	}
217
218	if (active != stdout)
219		active = stdout;	/* reset output just in case */
220	for (n = 1; n < MAXOUT; n++)	/* default wrap-up: undivert */
221		if (outfile[n] != NULL)
222			getdiv(n);
223					/* remove bitbucket if used  */
224	cleanup(0);
225	return 0;
226}
227
228ndptr inspect();
229
230/*
231 * macro - the work horse..
232 */
233void
234macro() {
235	char token[MAXTOK];
236	register char *s;
237	register int t, l;
238	register ndptr p;
239	register int  nlpar;
240
241	cycle {
242		if ((t = gpbc()) == '_' || (t != EOF && isalpha(t))) {
243			putback(t);
244			if ((p = inspect(s = token)) == nil) {
245				if (sp < 0)
246					while (*s)
247						putc(*s++, active);
248				else
249					while (*s)
250						chrsave(*s++);
251			}
252			else {
253		/*
254		 * real thing.. First build a call frame:
255		 */
256				pushf(fp);	/* previous call frm */
257				pushf(p->type); /* type of the call  */
258				pushf(0);	/* parenthesis level */
259				fp = sp;	/* new frame pointer */
260		/*
261		 * now push the string arguments:
262		 */
263				pushs(p->defn);	      /* defn string */
264				pushs(p->name);	      /* macro name  */
265				pushs(ep);	      /* start next..*/
266
267				putback(l = gpbc());
268				if (l != LPAREN)  {   /* add bracks  */
269					putback(RPAREN);
270					putback(LPAREN);
271				}
272			}
273		}
274		else if (t == EOF) {
275			if (sp > -1)
276				errx(1, "unexpected end of input");
277			if (ilevel <= 0)
278				break;			/* all done thanks.. */
279			--ilevel;
280			(void) fclose(infile[ilevel+1]);
281			bufbase = bbase[ilevel];
282			continue;
283		}
284	/*
285	 * non-alpha single-char token seen..
286	 * [the order of else if .. stmts is important.]
287	 */
288		else if (t == lquote) { 		/* strip quotes */
289			nlpar = 1;
290			do {
291				if ((l = gpbc()) == rquote)
292					nlpar--;
293				else if (l == lquote)
294					nlpar++;
295				else if (l == EOF)
296					errx(1, "missing right quote");
297				if (nlpar > 0) {
298					if (sp < 0)
299						putc(l, active);
300					else
301						chrsave(l);
302				}
303			}
304			while (nlpar != 0);
305		}
306
307		else if (sp < 0) {		/* not in a macro at all */
308			if (t == scommt) {	/* comment handling here */
309				putc(t, active);
310				while ((t = gpbc()) != ecommt)
311					putc(t, active);
312			}
313			putc(t, active);	/* output directly..	 */
314		}
315
316		else switch(t) {
317
318		case LPAREN:
319			if (PARLEV > 0)
320				chrsave(t);
321			while ((l = gpbc()) != EOF && isspace(l))
322				;		/* skip blank, tab, nl.. */
323			putback(l);
324			PARLEV++;
325			break;
326
327		case RPAREN:
328			if (--PARLEV > 0)
329				chrsave(t);
330			else {			/* end of argument list */
331				chrsave(EOS);
332
333				if (sp == STACKMAX)
334					errx(1, "internal stack overflow");
335
336				if (CALTYP == MACRTYPE)
337					expand((char **) mstack+fp+1, sp-fp);
338				else
339					eval((char **) mstack+fp+1, sp-fp, CALTYP);
340
341				ep = PREVEP;	/* flush strspace */
342				sp = PREVSP;	/* previous sp..  */
343				fp = PREVFP;	/* rewind stack...*/
344			}
345			break;
346
347		case COMMA:
348			if (PARLEV == 1) {
349				chrsave(EOS);		/* new argument   */
350				while ((l = gpbc()) != EOF && isspace(l))
351					;
352				putback(l);
353				pushs(ep);
354			} else
355				chrsave(t);
356			break;
357
358		default:
359			chrsave(t);			/* stack the char */
360			break;
361		}
362	}
363}
364
365/*
366 * build an input token..
367 * consider only those starting with _ or A-Za-z. This is a
368 * combo with lookup to speed things up.
369 */
370ndptr
371inspect(tp)
372register char *tp;
373{
374	register int c;
375	register char *name = tp;
376	register char *etp = tp+MAXTOK;
377	register ndptr p;
378	register unsigned long h = 0;
379
380	while ((c = gpbc()) != EOF && (isalnum(c) || c == '_') && tp < etp)
381		h = (h << 5) + h + (*tp++ = c);
382	putback(c);
383	if (tp == etp)
384		errx(1, "token too long");
385
386	*tp = EOS;
387
388	for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
389		if (STREQ(name, p->name))
390			break;
391	return p;
392}
393
394/*
395 * initkwds - initialise m4 keywords as fast as possible.
396 * This very similar to install, but without certain overheads,
397 * such as calling lookup. Malloc is not used for storing the
398 * keyword strings, since we simply use the static  pointers
399 * within keywrds block.
400 */
401void
402initkwds() {
403	register int i;
404	register int h;
405	register ndptr p;
406
407	for (i = 0; i < MAXKEYS; i++) {
408		h = hash(keywrds[i].knam);
409		p = (ndptr) xalloc(sizeof(struct ndblock));
410		p->nxtptr = hashtab[h];
411		hashtab[h] = p;
412		p->name = keywrds[i].knam;
413		p->defn = null;
414		p->type = keywrds[i].ktyp | STATIC;
415	}
416}
417