xref: /freebsd/usr.bin/m4/main.c (revision 6990ffd8a95caaba6858ad44ff1b3157d1efba8f)
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 <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 
70 ndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
71 unsigned char buf[BUFSIZE];              /* push-back buffer            */
72 unsigned char *bufbase = buf;            /* the base for current ilevel */
73 unsigned char *bbase[MAXINP];            /* the base for each ilevel    */
74 unsigned char *bp = buf;                 /* first available character   */
75 unsigned char *endpbb = buf+BUFSIZE;     /* end of push-back buffer     */
76 stae mstack[STACKMAX+1]; 	/* stack of m4 machine         */
77 char strspace[STRSPMAX+1];	/* string space for evaluation */
78 char *ep = strspace;		/* first free char in strspace */
79 char *endest= strspace+STRSPMAX;/* end of string space	       */
80 int sp; 			/* current m4  stack pointer   */
81 int fp; 			/* m4 call frame pointer       */
82 FILE *infile[MAXINP];		/* input file stack (0=stdin)  */
83 FILE *outfile[MAXOUT];		/* diversion array(0=bitbucket)*/
84 FILE *active;			/* active output file pointer  */
85 char *m4temp;			/* filename for diversions     */
86 char *m4dir;			/* directory for diversions    */
87 int ilevel = 0; 		/* input file stack pointer    */
88 int oindex = 0; 		/* diversion index..	       */
89 char *null = "";                /* as it says.. just a null..  */
90 char *m4wraps = "";             /* m4wrap string default..     */
91 char lquote = LQUOTE;		/* left quote character  (`)   */
92 char rquote = RQUOTE;		/* right quote character (')   */
93 char scommt = SCOMMT;		/* start character for comment */
94 char ecommt = ECOMMT;		/* end character for comment   */
95 
96 struct keyblk keywrds[] = {	/* m4 keywords to be installed */
97 	"include",      INCLTYPE,
98 	"sinclude",     SINCTYPE,
99 	"define",       DEFITYPE,
100 	"defn",         DEFNTYPE,
101 	"divert",       DIVRTYPE,
102 	"expr",         EXPRTYPE,
103 	"eval",         EXPRTYPE,
104 	"substr",       SUBSTYPE,
105 	"ifelse",       IFELTYPE,
106 	"ifdef",        IFDFTYPE,
107 	"len",          LENGTYPE,
108 	"incr",         INCRTYPE,
109 	"decr",         DECRTYPE,
110 	"dnl",          DNLNTYPE,
111 	"changequote",  CHNQTYPE,
112 	"changecom",    CHNCTYPE,
113 	"index",        INDXTYPE,
114 #ifdef EXTENDED
115 	"paste",        PASTTYPE,
116 	"spaste",       SPASTYPE,
117 #endif
118 	"popdef",       POPDTYPE,
119 	"pushdef",      PUSDTYPE,
120 	"dumpdef",      DUMPTYPE,
121 	"shift",        SHIFTYPE,
122 	"translit",     TRNLTYPE,
123 	"undefine",     UNDFTYPE,
124 	"undivert",     UNDVTYPE,
125 	"divnum",       DIVNTYPE,
126 	"maketemp",     MKTMTYPE,
127 	"errprint",     ERRPTYPE,
128 	"m4wrap",       M4WRTYPE,
129 	"m4exit",       EXITTYPE,
130 	"syscmd",       SYSCTYPE,
131 	"sysval",       SYSVTYPE,
132 
133 #ifdef unix
134 	"unix",         MACRTYPE,
135 #else
136 #ifdef vms
137 	"vms",          MACRTYPE,
138 #endif
139 #endif
140 };
141 
142 #define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
143 
144 void macro();
145 void initkwds();
146 
147 int
148 main(argc,argv)
149 	int argc;
150 	char *argv[];
151 {
152 	register int c;
153 	register int n;
154 	char *p;
155 	register FILE *ifp;
156 
157 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
158 		signal(SIGINT, onintr);
159 
160 	initkwds();
161 
162 	while ((c = getopt(argc, argv, "tD:U:o:")) != -1)
163 		switch(c) {
164 
165 		case 'D':               /* define something..*/
166 			for (p = optarg; *p; p++)
167 				if (*p == '=')
168 					break;
169 			if (*p)
170 				*p++ = EOS;
171 			dodefine(optarg, p);
172 			break;
173 		case 'U':               /* undefine...       */
174 			remhash(optarg, TOP);
175 			break;
176 		case 'o':		/* specific output   */
177 		case '?':
178 			usage();
179 		}
180 
181         argc -= optind;
182         argv += optind;
183 
184 	active = stdout;		/* default active output     */
185 	if ((p = strdup(_PATH_DIVDIRNAME)) == NULL)
186 		err(1, "strdup");
187 
188 					/* filename for diversions   */
189 	m4dir = mkdtemp(p);
190 	err_set_exit(cleanup);
191 	(void) asprintf(&m4temp, "%s/%s", m4dir, _PATH_DIVNAME);
192 
193 	bbase[0] = bufbase;
194         if (!argc) {
195  		sp = -1;		/* stack pointer initialized */
196 		fp = 0; 		/* frame pointer initialized */
197 		infile[0] = stdin;	/* default input (naturally) */
198 		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 
232 ndptr inspect();
233 
234 /*
235  * macro - the work horse..
236  */
237 void
238 macro() {
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  */
374 ndptr
375 inspect(tp)
376 register 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  */
405 void
406 initkwds() {
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