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