xref: /freebsd/usr.bin/m4/main.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
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 	err_set_exit(cleanup);
187 	(void) asprintf(&m4temp, "%s/%s", m4dir, _PATH_DIVNAME);
188 
189 	bbase[0] = bufbase;
190         if (!argc) {
191  		sp = -1;		/* stack pointer initialized */
192 		fp = 0; 		/* frame pointer initialized */
193 		infile[0] = stdin;	/* default input (naturally) */
194 		macro();
195 	} else
196 		for (; argc--; ++argv) {
197 			p = *argv;
198 			if (p[0] == '-' && p[1] == '\0')
199 				ifp = stdin;
200 			else if ((ifp = fopen(p, "r")) == NULL)
201 				err(1, "%s", p);
202 			sp = -1;
203 			fp = 0;
204 			infile[0] = ifp;
205 			macro();
206 			if (ifp != stdin)
207 				(void)fclose(ifp);
208 		}
209 
210 	if (*m4wraps) { 		/* anything for rundown ??   */
211 		ilevel = 0;		/* in case m4wrap includes.. */
212 		bufbase = bp = buf;	/* use the entire buffer   */
213 		putback(EOF);		/* eof is a must !!	     */
214 		pbstr(m4wraps); 	/* user-defined wrapup act   */
215 		macro();		/* last will and testament   */
216 	}
217 
218 	if (active != stdout)
219 		active = stdout;	/* reset output just in case */
220 	for (n = 1; n < MAXOUT; n++)	/* default wrap-up: undivert */
221 		if (outfile[n] != NULL)
222 			getdiv(n);
223 					/* remove bitbucket if used  */
224 	cleanup(0);
225 	return 0;
226 }
227 
228 ndptr inspect();
229 
230 /*
231  * macro - the work horse..
232  */
233 void
234 macro() {
235 	char token[MAXTOK];
236 	register char *s;
237 	register int t, l;
238 	register ndptr p;
239 	register int  nlpar;
240 
241 	cycle {
242 		if ((t = gpbc()) == '_' || (t != EOF && isalpha(t))) {
243 			putback(t);
244 			if ((p = inspect(s = token)) == nil) {
245 				if (sp < 0)
246 					while (*s)
247 						putc(*s++, active);
248 				else
249 					while (*s)
250 						chrsave(*s++);
251 			}
252 			else {
253 		/*
254 		 * real thing.. First build a call frame:
255 		 */
256 				pushf(fp);	/* previous call frm */
257 				pushf(p->type); /* type of the call  */
258 				pushf(0);	/* parenthesis level */
259 				fp = sp;	/* new frame pointer */
260 		/*
261 		 * now push the string arguments:
262 		 */
263 				pushs(p->defn);	      /* defn string */
264 				pushs(p->name);	      /* macro name  */
265 				pushs(ep);	      /* start next..*/
266 
267 				putback(l = gpbc());
268 				if (l != LPAREN)  {   /* add bracks  */
269 					putback(RPAREN);
270 					putback(LPAREN);
271 				}
272 			}
273 		}
274 		else if (t == EOF) {
275 			if (sp > -1)
276 				errx(1, "unexpected end of input");
277 			if (ilevel <= 0)
278 				break;			/* all done thanks.. */
279 			--ilevel;
280 			(void) fclose(infile[ilevel+1]);
281 			bufbase = bbase[ilevel];
282 			continue;
283 		}
284 	/*
285 	 * non-alpha single-char token seen..
286 	 * [the order of else if .. stmts is important.]
287 	 */
288 		else if (t == lquote) { 		/* strip quotes */
289 			nlpar = 1;
290 			do {
291 				if ((l = gpbc()) == rquote)
292 					nlpar--;
293 				else if (l == lquote)
294 					nlpar++;
295 				else if (l == EOF)
296 					errx(1, "missing right quote");
297 				if (nlpar > 0) {
298 					if (sp < 0)
299 						putc(l, active);
300 					else
301 						chrsave(l);
302 				}
303 			}
304 			while (nlpar != 0);
305 		}
306 
307 		else if (sp < 0) {		/* not in a macro at all */
308 			if (t == scommt) {	/* comment handling here */
309 				putc(t, active);
310 				while ((t = gpbc()) != ecommt)
311 					putc(t, active);
312 			}
313 			putc(t, active);	/* output directly..	 */
314 		}
315 
316 		else switch(t) {
317 
318 		case LPAREN:
319 			if (PARLEV > 0)
320 				chrsave(t);
321 			while ((l = gpbc()) != EOF && isspace(l))
322 				;		/* skip blank, tab, nl.. */
323 			putback(l);
324 			PARLEV++;
325 			break;
326 
327 		case RPAREN:
328 			if (--PARLEV > 0)
329 				chrsave(t);
330 			else {			/* end of argument list */
331 				chrsave(EOS);
332 
333 				if (sp == STACKMAX)
334 					errx(1, "internal stack overflow");
335 
336 				if (CALTYP == MACRTYPE)
337 					expand((char **) mstack+fp+1, sp-fp);
338 				else
339 					eval((char **) mstack+fp+1, sp-fp, CALTYP);
340 
341 				ep = PREVEP;	/* flush strspace */
342 				sp = PREVSP;	/* previous sp..  */
343 				fp = PREVFP;	/* rewind stack...*/
344 			}
345 			break;
346 
347 		case COMMA:
348 			if (PARLEV == 1) {
349 				chrsave(EOS);		/* new argument   */
350 				while ((l = gpbc()) != EOF && isspace(l))
351 					;
352 				putback(l);
353 				pushs(ep);
354 			} else
355 				chrsave(t);
356 			break;
357 
358 		default:
359 			chrsave(t);			/* stack the char */
360 			break;
361 		}
362 	}
363 }
364 
365 /*
366  * build an input token..
367  * consider only those starting with _ or A-Za-z. This is a
368  * combo with lookup to speed things up.
369  */
370 ndptr
371 inspect(tp)
372 register char *tp;
373 {
374 	register int c;
375 	register char *name = tp;
376 	register char *etp = tp+MAXTOK;
377 	register ndptr p;
378 	register unsigned long h = 0;
379 
380 	while ((c = gpbc()) != EOF && (isalnum(c) || c == '_') && tp < etp)
381 		h = (h << 5) + h + (*tp++ = c);
382 	putback(c);
383 	if (tp == etp)
384 		errx(1, "token too long");
385 
386 	*tp = EOS;
387 
388 	for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
389 		if (STREQ(name, p->name))
390 			break;
391 	return p;
392 }
393 
394 /*
395  * initkwds - initialise m4 keywords as fast as possible.
396  * This very similar to install, but without certain overheads,
397  * such as calling lookup. Malloc is not used for storing the
398  * keyword strings, since we simply use the static  pointers
399  * within keywrds block.
400  */
401 void
402 initkwds() {
403 	register int i;
404 	register int h;
405 	register ndptr p;
406 
407 	for (i = 0; i < MAXKEYS; i++) {
408 		h = hash(keywrds[i].knam);
409 		p = (ndptr) xalloc(sizeof(struct ndblock));
410 		p->nxtptr = hashtab[h];
411 		hashtab[h] = p;
412 		p->name = keywrds[i].knam;
413 		p->defn = null;
414 		p->type = keywrds[i].ktyp | STATIC;
415 	}
416 }
417