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