main.c revision 1591
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 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
44static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
45#endif /* not lint */
46
47/*
48 * main.c
49 * Facility: m4 macro processor
50 * by: oz
51 */
52
53#include <sys/types.h>
54#include <signal.h>
55#include <errno.h>
56#include <unistd.h>
57#include <stdio.h>
58#include <ctype.h>
59#include <string.h>
60#include "mdef.h"
61#include "stdd.h"
62#include "extern.h"
63#include "pathnames.h"
64
65ndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
66char buf[BUFSIZE];		/* push-back buffer	       */
67char *bufbase = buf;		/* the base for current ilevel */
68char *bbase[MAXINP];		/* the base for each ilevel    */
69char *bp = buf; 		/* first available character   */
70char *endpbb = buf+BUFSIZE;	/* end of push-back buffer     */
71stae mstack[STACKMAX+1]; 	/* stack of m4 machine         */
72char strspace[STRSPMAX+1];	/* string space for evaluation */
73char *ep = strspace;		/* first free char in strspace */
74char *endest= strspace+STRSPMAX;/* end of string space	       */
75int sp; 			/* current m4  stack pointer   */
76int fp; 			/* m4 call frame pointer       */
77FILE *infile[MAXINP];		/* input file stack (0=stdin)  */
78FILE *outfile[MAXOUT];		/* diversion array(0=bitbucket)*/
79FILE *active;			/* active output file pointer  */
80char *m4temp;			/* filename for diversions     */
81int ilevel = 0; 		/* input file stack pointer    */
82int oindex = 0; 		/* diversion index..	       */
83char *null = "";                /* as it says.. just a null..  */
84char *m4wraps = "";             /* m4wrap string default..     */
85char *progname;			/* name of this program        */
86char lquote = LQUOTE;		/* left quote character  (`)   */
87char rquote = RQUOTE;		/* right quote character (')   */
88char scommt = SCOMMT;		/* start character for comment */
89char ecommt = ECOMMT;		/* end character for comment   */
90
91struct keyblk keywrds[] = {	/* m4 keywords to be installed */
92	"include",      INCLTYPE,
93	"sinclude",     SINCTYPE,
94	"define",       DEFITYPE,
95	"defn",         DEFNTYPE,
96	"divert",       DIVRTYPE,
97	"expr",         EXPRTYPE,
98	"eval",         EXPRTYPE,
99	"substr",       SUBSTYPE,
100	"ifelse",       IFELTYPE,
101	"ifdef",        IFDFTYPE,
102	"len",          LENGTYPE,
103	"incr",         INCRTYPE,
104	"decr",         DECRTYPE,
105	"dnl",          DNLNTYPE,
106	"changequote",  CHNQTYPE,
107	"changecom",    CHNCTYPE,
108	"index",        INDXTYPE,
109#ifdef EXTENDED
110	"paste",        PASTTYPE,
111	"spaste",       SPASTYPE,
112#endif
113	"popdef",       POPDTYPE,
114	"pushdef",      PUSDTYPE,
115	"dumpdef",      DUMPTYPE,
116	"shift",        SHIFTYPE,
117	"translit",     TRNLTYPE,
118	"undefine",     UNDFTYPE,
119	"undivert",     UNDVTYPE,
120	"divnum",       DIVNTYPE,
121	"maketemp",     MKTMTYPE,
122	"errprint",     ERRPTYPE,
123	"m4wrap",       M4WRTYPE,
124	"m4exit",       EXITTYPE,
125	"syscmd",       SYSCTYPE,
126	"sysval",       SYSVTYPE,
127
128#ifdef unix
129	"unix",         MACRTYPE,
130#else
131#ifdef vms
132	"vms",          MACRTYPE,
133#endif
134#endif
135};
136
137#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
138
139extern int optind;
140extern char *optarg;
141
142void macro();
143void initkwds();
144extern int getopt();
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	progname = basename(argv[0]);
157
158	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
159		signal(SIGINT, onintr);
160
161	initkwds();
162
163	while ((c = getopt(argc, argv, "tD:U:o:")) != EOF)
164		switch(c) {
165
166		case 'D':               /* define something..*/
167			for (p = optarg; *p; p++)
168				if (*p == '=')
169					break;
170			if (*p)
171				*p++ = EOS;
172			dodefine(optarg, p);
173			break;
174		case 'U':               /* undefine...       */
175			remhash(optarg, TOP);
176			break;
177		case 'o':		/* specific output   */
178		case '?':
179			usage();
180		}
181
182        argc -= optind;
183        argv += optind;
184
185	active = stdout;		/* default active output     */
186					/* filename for diversions   */
187	m4temp = mktemp(xstrdup(_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				oops("%s: %s", p, strerror(errno));
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	if (outfile[0] != NULL) {
225		(void) fclose(outfile[0]);
226		m4temp[UNIQUE] = '0';
227#ifdef vms
228		(void) remove(m4temp);
229#else
230		(void) unlink(m4temp);
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()) == '_' || 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				oops("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					oops("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 (isspace(l = gpbc()))
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					oops("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 (isspace(l = gpbc()))
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 char 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 ((isalnum(c = gpbc()) || c == '_') && tp < etp)
390		h = (h << 5) + h + (*tp++ = c);
391	putback(c);
392	if (tp == etp)
393		oops("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