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