main.c revision 80289
1228753Smm/*-
2228753Smm * Copyright (c) 1989, 1993
3228753Smm *	The Regents of the University of California.  All rights reserved.
4228753Smm *
5228753Smm * This code is derived from software contributed to Berkeley by
6228753Smm * Ozan Yigit at York University.
7228753Smm *
8228753Smm * Redistribution and use in source and binary forms, with or without
9228753Smm * modification, are permitted provided that the following conditions
10228753Smm * are met:
11228753Smm * 1. Redistributions of source code must retain the above copyright
12228753Smm *    notice, this list of conditions and the following disclaimer.
13228753Smm * 2. Redistributions in binary form must reproduce the above copyright
14228753Smm *    notice, this list of conditions and the following disclaimer in the
15228753Smm *    documentation and/or other materials provided with the distribution.
16228753Smm * 3. All advertising materials mentioning features or use of this software
17228753Smm *    must display the following acknowledgement:
18228753Smm *	This product includes software developed by the University of
19228753Smm *	California, Berkeley and its contributors.
20228753Smm * 4. Neither the name of the University nor the names of its contributors
21228753Smm *    may be used to endorse or promote products derived from this software
22228753Smm *    without specific prior written permission.
23228753Smm *
24228753Smm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25228753Smm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26228753Smm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27228753Smm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28228753Smm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29228753Smm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30228753Smm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31228753Smm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32228753Smm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33228753Smm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34228753Smm * SUCH DAMAGE.
35228753Smm */
36228753Smm
37228753Smm#ifndef lint
38228753Smmstatic const char copyright[] =
39228753Smm"@(#) Copyright (c) 1989, 1993\n\
40228753Smm	The Regents of the University of California.  All rights reserved.\n";
41228753Smm#endif /* not lint */
42228753Smm
43228753Smm#ifndef lint
44228753Smm#if 0
45228753Smmstatic char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
46228753Smm#endif
47228753Smmstatic const char rcsid[] =
48228753Smm  "$FreeBSD: head/usr.bin/m4/main.c 80289 2001-07-24 14:09:47Z obrien $";
49228753Smm#endif /* not lint */
50228753Smm
51228753Smm/*
52228753Smm * main.c
53228753Smm * Facility: m4 macro processor
54228753Smm * by: oz
55228753Smm */
56228753Smm
57228753Smm#include <sys/types.h>
58228753Smm#include <ctype.h>
59228753Smm#include <err.h>
60228753Smm#include <signal.h>
61228753Smm#include <stdio.h>
62228753Smm#include <stdlib.h>
63228753Smm#include <string.h>
64228753Smm#include <unistd.h>
65228753Smm#include "mdef.h"
66228753Smm#include "stdd.h"
67228753Smm#include "extern.h"
68228753Smm#include "pathnames.h"
69228753Smm
70228753Smmndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
71228753Smmunsigned char buf[BUFSIZE];              /* push-back buffer            */
72228753Smmunsigned char *bufbase = buf;            /* the base for current ilevel */
73228753Smmunsigned char *bbase[MAXINP];            /* the base for each ilevel    */
74228753Smmunsigned char *bp = buf;                 /* first available character   */
75228753Smmunsigned char *endpbb = buf+BUFSIZE;     /* end of push-back buffer     */
76228753Smmstae mstack[STACKMAX+1]; 	/* stack of m4 machine         */
77228753Smmchar strspace[STRSPMAX+1];	/* string space for evaluation */
78228753Smmchar *ep = strspace;		/* first free char in strspace */
79228753Smmchar *endest= strspace+STRSPMAX;/* end of string space	       */
80228753Smmint sp; 			/* current m4  stack pointer   */
81228753Smmint fp; 			/* m4 call frame pointer       */
82228753SmmFILE *infile[MAXINP];		/* input file stack (0=stdin)  */
83228753SmmFILE *outfile[MAXOUT];		/* diversion array(0=bitbucket)*/
84228753SmmFILE *active;			/* active output file pointer  */
85228753Smmchar *m4temp;			/* filename for diversions     */
86228753Smmchar *m4dir;			/* directory for diversions    */
87228753Smmint ilevel = 0; 		/* input file stack pointer    */
88228753Smmint oindex = 0; 		/* diversion index..	       */
89228753Smmchar *null = "";                /* as it says.. just a null..  */
90228753Smmchar *m4wraps = "";             /* m4wrap string default..     */
91228753Smmchar lquote = LQUOTE;		/* left quote character  (`)   */
92228753Smmchar rquote = RQUOTE;		/* right quote character (')   */
93228753Smmchar scommt = SCOMMT;		/* start character for comment */
94228753Smmchar ecommt = ECOMMT;		/* end character for comment   */
95228753Smm
96228753Smmstruct keyblk keywrds[] = {	/* m4 keywords to be installed */
97228753Smm	"include",      INCLTYPE,
98228753Smm	"sinclude",     SINCTYPE,
99228753Smm	"define",       DEFITYPE,
100228753Smm	"defn",         DEFNTYPE,
101228753Smm	"divert",       DIVRTYPE,
102228753Smm	"expr",         EXPRTYPE,
103228753Smm	"eval",         EXPRTYPE,
104228753Smm	"substr",       SUBSTYPE,
105228753Smm	"ifelse",       IFELTYPE,
106228753Smm	"ifdef",        IFDFTYPE,
107228753Smm	"len",          LENGTYPE,
108228753Smm	"incr",         INCRTYPE,
109228753Smm	"decr",         DECRTYPE,
110228753Smm	"dnl",          DNLNTYPE,
111228753Smm	"changequote",  CHNQTYPE,
112228753Smm	"changecom",    CHNCTYPE,
113228753Smm	"index",        INDXTYPE,
114228753Smm#ifdef EXTENDED
115228753Smm	"paste",        PASTTYPE,
116228753Smm	"spaste",       SPASTYPE,
117228753Smm#endif
118228753Smm	"popdef",       POPDTYPE,
119228753Smm	"pushdef",      PUSDTYPE,
120228753Smm	"dumpdef",      DUMPTYPE,
121228753Smm	"shift",        SHIFTYPE,
122228753Smm	"translit",     TRNLTYPE,
123228753Smm	"undefine",     UNDFTYPE,
124228753Smm	"undivert",     UNDVTYPE,
125228753Smm	"divnum",       DIVNTYPE,
126228753Smm	"maketemp",     MKTMTYPE,
127228753Smm	"errprint",     ERRPTYPE,
128228753Smm	"m4wrap",       M4WRTYPE,
129228753Smm	"m4exit",       EXITTYPE,
130228753Smm	"syscmd",       SYSCTYPE,
131228753Smm	"sysval",       SYSVTYPE,
132228753Smm
133228753Smm#ifdef unix
134228753Smm	"unix",         MACRTYPE,
135228753Smm#else
136228753Smm#ifdef vms
137228753Smm	"vms",          MACRTYPE,
138228753Smm#endif
139228753Smm#endif
140228753Smm};
141228753Smm
142228753Smm#define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
143228753Smm
144228753Smmvoid macro();
145228753Smmvoid initkwds();
146228753Smm
147228753Smmint
148228753Smmmain(argc,argv)
149228753Smm	int argc;
150228753Smm	char *argv[];
151228753Smm{
152228753Smm	register int c;
153228753Smm	register int n;
154228753Smm	char *p;
155228753Smm	register FILE *ifp;
156228753Smm
157228753Smm	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
158228753Smm		signal(SIGINT, onintr);
159228753Smm
160228753Smm	initkwds();
161228753Smm
162228753Smm	while ((c = getopt(argc, argv, "tD:U:o:")) != -1)
163228753Smm		switch(c) {
164228753Smm
165228753Smm		case 'D':               /* define something..*/
166228753Smm			for (p = optarg; *p; p++)
167228753Smm				if (*p == '=')
168228753Smm					break;
169228753Smm			if (*p)
170228753Smm				*p++ = EOS;
171228753Smm			dodefine(optarg, p);
172228753Smm			break;
173228753Smm		case 'U':               /* undefine...       */
174228753Smm			remhash(optarg, TOP);
175228753Smm			break;
176228753Smm		case 'o':		/* specific output   */
177228753Smm		case '?':
178228753Smm			usage();
179228753Smm		}
180228753Smm
181228753Smm        argc -= optind;
182228753Smm        argv += optind;
183228753Smm
184228753Smm	active = stdout;		/* default active output     */
185228753Smm	if ((p = strdup(_PATH_DIVDIRNAME)) == NULL)
186228753Smm		err(1, "strdup");
187228753Smm
188228753Smm					/* filename for diversions   */
189228753Smm	m4dir = mkdtemp(p);
190228753Smm	err_set_exit(cleanup);
191228753Smm	(void) asprintf(&m4temp, "%s/%s", m4dir, _PATH_DIVNAME);
192228753Smm
193228753Smm	bbase[0] = bufbase;
194228753Smm        if (!argc) {
195228753Smm 		sp = -1;		/* stack pointer initialized */
196228753Smm		fp = 0; 		/* frame pointer initialized */
197228753Smm		infile[0] = stdin;	/* default input (naturally) */
198228753Smm		macro();
199	} else
200		for (; argc--; ++argv) {
201			p = *argv;
202			if (p[0] == '-' && p[1] == '\0')
203				ifp = stdin;
204			else if ((ifp = fopen(p, "r")) == NULL)
205				err(1, "%s", p);
206			sp = -1;
207			fp = 0;
208			infile[0] = ifp;
209			macro();
210			if (ifp != stdin)
211				(void)fclose(ifp);
212		}
213
214	if (*m4wraps) { 		/* anything for rundown ??   */
215		ilevel = 0;		/* in case m4wrap includes.. */
216		bufbase = bp = buf;	/* use the entire buffer   */
217		putback(EOF);		/* eof is a must !!	     */
218		pbstr(m4wraps); 	/* user-defined wrapup act   */
219		macro();		/* last will and testament   */
220	}
221
222	if (active != stdout)
223		active = stdout;	/* reset output just in case */
224	for (n = 1; n < MAXOUT; n++)	/* default wrap-up: undivert */
225		if (outfile[n] != NULL)
226			getdiv(n);
227					/* remove bitbucket if used  */
228	cleanup(0);
229	return 0;
230}
231
232ndptr inspect();
233
234/*
235 * macro - the work horse..
236 */
237void
238macro() {
239	char token[MAXTOK];
240	register char *s;
241	register int t, l;
242	register ndptr p;
243	register int  nlpar;
244
245	cycle {
246		if ((t = gpbc()) == '_' || (t != EOF && isalpha(t))) {
247			putback(t);
248			if ((p = inspect(s = token)) == nil) {
249				if (sp < 0)
250					while (*s)
251						putc(*s++, active);
252				else
253					while (*s)
254						chrsave(*s++);
255			}
256			else {
257		/*
258		 * real thing.. First build a call frame:
259		 */
260				pushf(fp);	/* previous call frm */
261				pushf(p->type); /* type of the call  */
262				pushf(0);	/* parenthesis level */
263				fp = sp;	/* new frame pointer */
264		/*
265		 * now push the string arguments:
266		 */
267				pushs(p->defn);	      /* defn string */
268				pushs(p->name);	      /* macro name  */
269				pushs(ep);	      /* start next..*/
270
271				putback(l = gpbc());
272				if (l != LPAREN)  {   /* add bracks  */
273					putback(RPAREN);
274					putback(LPAREN);
275				}
276			}
277		}
278		else if (t == EOF) {
279			if (sp > -1)
280				errx(1, "unexpected end of input");
281			if (ilevel <= 0)
282				break;			/* all done thanks.. */
283			--ilevel;
284			(void) fclose(infile[ilevel+1]);
285			bufbase = bbase[ilevel];
286			continue;
287		}
288	/*
289	 * non-alpha single-char token seen..
290	 * [the order of else if .. stmts is important.]
291	 */
292		else if (t == lquote) { 		/* strip quotes */
293			nlpar = 1;
294			do {
295				if ((l = gpbc()) == rquote)
296					nlpar--;
297				else if (l == lquote)
298					nlpar++;
299				else if (l == EOF)
300					errx(1, "missing right quote");
301				if (nlpar > 0) {
302					if (sp < 0)
303						putc(l, active);
304					else
305						chrsave(l);
306				}
307			}
308			while (nlpar != 0);
309		}
310
311		else if (sp < 0) {		/* not in a macro at all */
312			if (t == scommt) {	/* comment handling here */
313				putc(t, active);
314				while ((t = gpbc()) != ecommt)
315					putc(t, active);
316			}
317			putc(t, active);	/* output directly..	 */
318		}
319
320		else switch(t) {
321
322		case LPAREN:
323			if (PARLEV > 0)
324				chrsave(t);
325			while ((l = gpbc()) != EOF && isspace(l))
326				;		/* skip blank, tab, nl.. */
327			putback(l);
328			PARLEV++;
329			break;
330
331		case RPAREN:
332			if (--PARLEV > 0)
333				chrsave(t);
334			else {			/* end of argument list */
335				chrsave(EOS);
336
337				if (sp == STACKMAX)
338					errx(1, "internal stack overflow");
339
340				if (CALTYP == MACRTYPE)
341					expand((char **) mstack+fp+1, sp-fp);
342				else
343					eval((char **) mstack+fp+1, sp-fp, CALTYP);
344
345				ep = PREVEP;	/* flush strspace */
346				sp = PREVSP;	/* previous sp..  */
347				fp = PREVFP;	/* rewind stack...*/
348			}
349			break;
350
351		case COMMA:
352			if (PARLEV == 1) {
353				chrsave(EOS);		/* new argument   */
354				while ((l = gpbc()) != EOF && isspace(l))
355					;
356				putback(l);
357				pushs(ep);
358			} else
359				chrsave(t);
360			break;
361
362		default:
363			chrsave(t);			/* stack the char */
364			break;
365		}
366	}
367}
368
369/*
370 * build an input token..
371 * consider only those starting with _ or A-Za-z. This is a
372 * combo with lookup to speed things up.
373 */
374ndptr
375inspect(tp)
376register char *tp;
377{
378	register int c;
379	register char *name = tp;
380	register char *etp = tp+MAXTOK;
381	register ndptr p;
382	register unsigned long h = 0;
383
384	while ((c = gpbc()) != EOF && (isalnum(c) || c == '_') && tp < etp)
385		h = (h << 5) + h + (*tp++ = c);
386	putback(c);
387	if (tp == etp)
388		errx(1, "token too long");
389
390	*tp = EOS;
391
392	for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
393		if (STREQ(name, p->name))
394			break;
395	return p;
396}
397
398/*
399 * initkwds - initialise m4 keywords as fast as possible.
400 * This very similar to install, but without certain overheads,
401 * such as calling lookup. Malloc is not used for storing the
402 * keyword strings, since we simply use the static  pointers
403 * within keywrds block.
404 */
405void
406initkwds() {
407	register int i;
408	register int h;
409	register ndptr p;
410
411	for (i = 0; i < MAXKEYS; i++) {
412		h = hash(keywrds[i].knam);
413		if ((p = malloc(sizeof(struct ndblock))) == NULL)
414			err(1, "malloc");
415		p->nxtptr = hashtab[h];
416		hashtab[h] = p;
417		p->name = keywrds[i].knam;
418		p->defn = null;
419		p->type = keywrds[i].ktyp | STATIC;
420	}
421}
422