xref: /freebsd/usr.bin/m4/main.c (revision 817420dc8eac7df799c78f5309b75092b7f7cd40)
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 char *m4dir;			/* directory for diversions    */
86 int ilevel = 0; 		/* input file stack pointer    */
87 int oindex = 0; 		/* diversion index..	       */
88 char *null = "";                /* as it says.. just a null..  */
89 char *m4wraps = "";             /* m4wrap string default..     */
90 char lquote = LQUOTE;		/* left quote character  (`)   */
91 char rquote = RQUOTE;		/* right quote character (')   */
92 char scommt = SCOMMT;		/* start character for comment */
93 char ecommt = ECOMMT;		/* end character for comment   */
94 
95 struct keyblk keywrds[] = {	/* m4 keywords to be installed */
96 	"include",      INCLTYPE,
97 	"sinclude",     SINCTYPE,
98 	"define",       DEFITYPE,
99 	"defn",         DEFNTYPE,
100 	"divert",       DIVRTYPE,
101 	"expr",         EXPRTYPE,
102 	"eval",         EXPRTYPE,
103 	"substr",       SUBSTYPE,
104 	"ifelse",       IFELTYPE,
105 	"ifdef",        IFDFTYPE,
106 	"len",          LENGTYPE,
107 	"incr",         INCRTYPE,
108 	"decr",         DECRTYPE,
109 	"dnl",          DNLNTYPE,
110 	"changequote",  CHNQTYPE,
111 	"changecom",    CHNCTYPE,
112 	"index",        INDXTYPE,
113 #ifdef EXTENDED
114 	"paste",        PASTTYPE,
115 	"spaste",       SPASTYPE,
116 #endif
117 	"popdef",       POPDTYPE,
118 	"pushdef",      PUSDTYPE,
119 	"dumpdef",      DUMPTYPE,
120 	"shift",        SHIFTYPE,
121 	"translit",     TRNLTYPE,
122 	"undefine",     UNDFTYPE,
123 	"undivert",     UNDVTYPE,
124 	"divnum",       DIVNTYPE,
125 	"maketemp",     MKTMTYPE,
126 	"errprint",     ERRPTYPE,
127 	"m4wrap",       M4WRTYPE,
128 	"m4exit",       EXITTYPE,
129 	"syscmd",       SYSCTYPE,
130 	"sysval",       SYSVTYPE,
131 
132 #ifdef unix
133 	"unix",         MACRTYPE,
134 #else
135 #ifdef vms
136 	"vms",          MACRTYPE,
137 #endif
138 #endif
139 };
140 
141 #define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
142 
143 void macro();
144 void initkwds();
145 
146 int
147 main(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 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
157 		signal(SIGINT, onintr);
158 
159 	initkwds();
160 
161 	while ((c = getopt(argc, argv, "tD:U:o:")) != -1)
162 		switch(c) {
163 
164 		case 'D':               /* define something..*/
165 			for (p = optarg; *p; p++)
166 				if (*p == '=')
167 					break;
168 			if (*p)
169 				*p++ = EOS;
170 			dodefine(optarg, p);
171 			break;
172 		case 'U':               /* undefine...       */
173 			remhash(optarg, TOP);
174 			break;
175 		case 'o':		/* specific output   */
176 		case '?':
177 			usage();
178 		}
179 
180         argc -= optind;
181         argv += optind;
182 
183 	active = stdout;		/* default active output     */
184 					/* filename for diversions   */
185 	m4dir = mkdtemp(xstrdup(_PATH_DIVDIRNAME));
186 	(void) asprintf(&m4temp, "%s/%s", m4dir, _PATH_DIVNAME);
187 
188 	bbase[0] = bufbase;
189         if (!argc) {
190  		sp = -1;		/* stack pointer initialized */
191 		fp = 0; 		/* frame pointer initialized */
192 		infile[0] = stdin;	/* default input (naturally) */
193 		macro();
194 	} else
195 		for (; argc--; ++argv) {
196 			p = *argv;
197 			if (p[0] == '-' && p[1] == '\0')
198 				ifp = stdin;
199 			else if ((ifp = fopen(p, "r")) == NULL)
200 				err(1, "%s", p);
201 			sp = -1;
202 			fp = 0;
203 			infile[0] = ifp;
204 			macro();
205 			if (ifp != stdin)
206 				(void)fclose(ifp);
207 		}
208 
209 	if (*m4wraps) { 		/* anything for rundown ??   */
210 		ilevel = 0;		/* in case m4wrap includes.. */
211 		bufbase = bp = buf;	/* use the entire buffer   */
212 		putback(EOF);		/* eof is a must !!	     */
213 		pbstr(m4wraps); 	/* user-defined wrapup act   */
214 		macro();		/* last will and testament   */
215 	}
216 
217 	if (active != stdout)
218 		active = stdout;	/* reset output just in case */
219 	for (n = 1; n < MAXOUT; n++)	/* default wrap-up: undivert */
220 		if (outfile[n] != NULL)
221 			getdiv(n);
222 					/* remove bitbucket if used  */
223 	if (outfile[0] != NULL) {
224 		(void) fclose(outfile[0]);
225 		m4temp[UNIQUE] = '0';
226 #ifdef vms
227 		(void) remove(m4temp);
228 #else
229 		(void) unlink(m4temp);
230 		(void) rmdir(m4dir);
231 #endif
232 	}
233 
234 	return 0;
235 }
236 
237 ndptr inspect();
238 
239 /*
240  * macro - the work horse..
241  */
242 void
243 macro() {
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()) == '_' || (t != EOF && 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 				errx(1, "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 					errx(1, "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 ((l = gpbc()) != EOF && isspace(l))
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 					errx(1, "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 ((l = gpbc()) != EOF && isspace(l))
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  */
379 ndptr
380 inspect(tp)
381 register char *tp;
382 {
383 	register int 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 ((c = gpbc()) != EOF && (isalnum(c) || c == '_') && tp < etp)
390 		h = (h << 5) + h + (*tp++ = c);
391 	putback(c);
392 	if (tp == etp)
393 		errx(1, "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  */
410 void
411 initkwds() {
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