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