xref: /freebsd/usr.bin/m4/main.c (revision afe61c15161c324a7af299a9b8457aba5afc92db)
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 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 static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
45 #endif /* not lint */
46 
47 /*
48  * main.c
49  * Facility: m4 macro processor
50  * by: oz
51  */
52 
53 #include <sys/types.h>
54 #include <signal.h>
55 #include <errno.h>
56 #include <unistd.h>
57 #include <stdio.h>
58 #include <ctype.h>
59 #include <string.h>
60 #include "mdef.h"
61 #include "stdd.h"
62 #include "extern.h"
63 #include "pathnames.h"
64 
65 ndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
66 char buf[BUFSIZE];		/* push-back buffer	       */
67 char *bufbase = buf;		/* the base for current ilevel */
68 char *bbase[MAXINP];		/* the base for each ilevel    */
69 char *bp = buf; 		/* first available character   */
70 char *endpbb = buf+BUFSIZE;	/* end of push-back buffer     */
71 stae mstack[STACKMAX+1]; 	/* stack of m4 machine         */
72 char strspace[STRSPMAX+1];	/* string space for evaluation */
73 char *ep = strspace;		/* first free char in strspace */
74 char *endest= strspace+STRSPMAX;/* end of string space	       */
75 int sp; 			/* current m4  stack pointer   */
76 int fp; 			/* m4 call frame pointer       */
77 FILE *infile[MAXINP];		/* input file stack (0=stdin)  */
78 FILE *outfile[MAXOUT];		/* diversion array(0=bitbucket)*/
79 FILE *active;			/* active output file pointer  */
80 char *m4temp;			/* filename for diversions     */
81 int ilevel = 0; 		/* input file stack pointer    */
82 int oindex = 0; 		/* diversion index..	       */
83 char *null = "";                /* as it says.. just a null..  */
84 char *m4wraps = "";             /* m4wrap string default..     */
85 char *progname;			/* name of this program        */
86 char lquote = LQUOTE;		/* left quote character  (`)   */
87 char rquote = RQUOTE;		/* right quote character (')   */
88 char scommt = SCOMMT;		/* start character for comment */
89 char ecommt = ECOMMT;		/* end character for comment   */
90 
91 struct keyblk keywrds[] = {	/* m4 keywords to be installed */
92 	"include",      INCLTYPE,
93 	"sinclude",     SINCTYPE,
94 	"define",       DEFITYPE,
95 	"defn",         DEFNTYPE,
96 	"divert",       DIVRTYPE,
97 	"expr",         EXPRTYPE,
98 	"eval",         EXPRTYPE,
99 	"substr",       SUBSTYPE,
100 	"ifelse",       IFELTYPE,
101 	"ifdef",        IFDFTYPE,
102 	"len",          LENGTYPE,
103 	"incr",         INCRTYPE,
104 	"decr",         DECRTYPE,
105 	"dnl",          DNLNTYPE,
106 	"changequote",  CHNQTYPE,
107 	"changecom",    CHNCTYPE,
108 	"index",        INDXTYPE,
109 #ifdef EXTENDED
110 	"paste",        PASTTYPE,
111 	"spaste",       SPASTYPE,
112 #endif
113 	"popdef",       POPDTYPE,
114 	"pushdef",      PUSDTYPE,
115 	"dumpdef",      DUMPTYPE,
116 	"shift",        SHIFTYPE,
117 	"translit",     TRNLTYPE,
118 	"undefine",     UNDFTYPE,
119 	"undivert",     UNDVTYPE,
120 	"divnum",       DIVNTYPE,
121 	"maketemp",     MKTMTYPE,
122 	"errprint",     ERRPTYPE,
123 	"m4wrap",       M4WRTYPE,
124 	"m4exit",       EXITTYPE,
125 	"syscmd",       SYSCTYPE,
126 	"sysval",       SYSVTYPE,
127 
128 #ifdef unix
129 	"unix",         MACRTYPE,
130 #else
131 #ifdef vms
132 	"vms",          MACRTYPE,
133 #endif
134 #endif
135 };
136 
137 #define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
138 
139 extern int optind;
140 extern char *optarg;
141 
142 void macro();
143 void initkwds();
144 extern int getopt();
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 	progname = basename(argv[0]);
157 
158 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
159 		signal(SIGINT, onintr);
160 
161 	initkwds();
162 
163 	while ((c = getopt(argc, argv, "tD:U:o:")) != EOF)
164 		switch(c) {
165 
166 		case 'D':               /* define something..*/
167 			for (p = optarg; *p; p++)
168 				if (*p == '=')
169 					break;
170 			if (*p)
171 				*p++ = EOS;
172 			dodefine(optarg, p);
173 			break;
174 		case 'U':               /* undefine...       */
175 			remhash(optarg, TOP);
176 			break;
177 		case 'o':		/* specific output   */
178 		case '?':
179 			usage();
180 		}
181 
182         argc -= optind;
183         argv += optind;
184 
185 	active = stdout;		/* default active output     */
186 					/* filename for diversions   */
187 	m4temp = mktemp(xstrdup(_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 				oops("%s: %s", p, strerror(errno));
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 	if (outfile[0] != NULL) {
225 		(void) fclose(outfile[0]);
226 		m4temp[UNIQUE] = '0';
227 #ifdef vms
228 		(void) remove(m4temp);
229 #else
230 		(void) unlink(m4temp);
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()) == '_' || 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 				oops("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 					oops("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 (isspace(l = gpbc()))
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 					oops("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 (isspace(l = gpbc()))
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 char 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 ((isalnum(c = gpbc()) || c == '_') && tp < etp)
390 		h = (h << 5) + h + (*tp++ = c);
391 	putback(c);
392 	if (tp == etp)
393 		oops("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