xref: /original-bsd/usr.bin/m4/main.c (revision a5c27708)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * 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 sccsid[] = "@(#)main.c	5.9 (Berkeley) 11/20/91";
39 #endif /* not lint */
40 
41 /*
42  * main.c
43  * Facility: m4 macro processor
44  * by: oz
45  */
46 
47 #include <sys/types.h>
48 #include <signal.h>
49 #include <errno.h>
50 #include <unistd.h>
51 #include <stdio.h>
52 #include <ctype.h>
53 #include <string.h>
54 #include "mdef.h"
55 #include "stdd.h"
56 #include "extern.h"
57 #include "pathnames.h"
58 
59 ndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
60 char buf[BUFSIZE];		/* push-back buffer	       */
61 char *bufbase = buf;		/* the base for current ilevel */
62 char *bbase[MAXINP];		/* the base for each ilevel    */
63 char *bp = buf; 		/* first available character   */
64 char *endpbb = buf+BUFSIZE;	/* end of push-back buffer     */
65 stae mstack[STACKMAX+1]; 	/* stack of m4 machine         */
66 char strspace[STRSPMAX+1];	/* string space for evaluation */
67 char *ep = strspace;		/* first free char in strspace */
68 char *endest= strspace+STRSPMAX;/* end of string space	       */
69 int sp; 			/* current m4  stack pointer   */
70 int fp; 			/* m4 call frame pointer       */
71 FILE *infile[MAXINP];		/* input file stack (0=stdin)  */
72 FILE *outfile[MAXOUT];		/* diversion array(0=bitbucket)*/
73 FILE *active;			/* active output file pointer  */
74 char *m4temp;			/* filename for diversions     */
75 int ilevel = 0; 		/* input file stack pointer    */
76 int oindex = 0; 		/* diversion index..	       */
77 char *null = "";                /* as it says.. just a null..  */
78 char *m4wraps = "";             /* m4wrap string default..     */
79 char *progname;			/* name of this program        */
80 char lquote = LQUOTE;		/* left quote character  (`)   */
81 char rquote = RQUOTE;		/* right quote character (')   */
82 char scommt = SCOMMT;		/* start character for comment */
83 char ecommt = ECOMMT;		/* end character for comment   */
84 
85 struct keyblk keywrds[] = {	/* m4 keywords to be installed */
86 	"include",      INCLTYPE,
87 	"sinclude",     SINCTYPE,
88 	"define",       DEFITYPE,
89 	"defn",         DEFNTYPE,
90 	"divert",       DIVRTYPE,
91 	"expr",         EXPRTYPE,
92 	"eval",         EXPRTYPE,
93 	"substr",       SUBSTYPE,
94 	"ifelse",       IFELTYPE,
95 	"ifdef",        IFDFTYPE,
96 	"len",          LENGTYPE,
97 	"incr",         INCRTYPE,
98 	"decr",         DECRTYPE,
99 	"dnl",          DNLNTYPE,
100 	"changequote",  CHNQTYPE,
101 	"changecom",    CHNCTYPE,
102 	"index",        INDXTYPE,
103 #ifdef EXTENDED
104 	"paste",        PASTTYPE,
105 	"spaste",       SPASTYPE,
106 #endif
107 	"popdef",       POPDTYPE,
108 	"pushdef",      PUSDTYPE,
109 	"dumpdef",      DUMPTYPE,
110 	"shift",        SHIFTYPE,
111 	"translit",     TRNLTYPE,
112 	"undefine",     UNDFTYPE,
113 	"undivert",     UNDVTYPE,
114 	"divnum",       DIVNTYPE,
115 	"maketemp",     MKTMTYPE,
116 	"errprint",     ERRPTYPE,
117 	"m4wrap",       M4WRTYPE,
118 	"m4exit",       EXITTYPE,
119 	"syscmd",       SYSCTYPE,
120 	"sysval",       SYSVTYPE,
121 
122 #ifdef unix
123 	"unix",         MACRTYPE,
124 #else
125 #ifdef vms
126 	"vms",          MACRTYPE,
127 #endif
128 #endif
129 };
130 
131 #define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
132 
133 extern int optind;
134 extern char *optarg;
135 
136 void macro();
137 void initkwds();
138 extern int getopt();
139 
140 int
141 main(argc,argv)
142 int argc;
143 char *argv[];
144 {
145 	register int c;
146 	register int n;
147 	char *p;
148 	register FILE *ifp;
149 
150 	progname = basename(argv[0]);
151 
152 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
153 		signal(SIGINT, onintr);
154 
155 	initkwds();
156 
157 	while ((c = getopt(argc, argv, "tD:U:o:")) != EOF)
158 		switch(c) {
159 
160 		case 'D':               /* define something..*/
161 			for (p = optarg; *p; p++)
162 				if (*p == '=')
163 					break;
164 			if (*p)
165 				*p++ = EOS;
166 			dodefine(optarg, p);
167 			break;
168 		case 'U':               /* undefine...       */
169 			remhash(optarg, TOP);
170 			break;
171 		case 'o':		/* specific output   */
172 		case '?':
173 			usage();
174 		}
175 
176         argc -= optind;
177         argv += optind;
178 
179 	active = stdout;		/* default active output     */
180 					/* filename for diversions   */
181 	m4temp = mktemp(xstrdup(_PATH_DIVNAME));
182 
183 	bbase[0] = bufbase;
184         if (!argc) {
185  		sp = -1;		/* stack pointer initialized */
186 		fp = 0; 		/* frame pointer initialized */
187 		infile[0] = stdin;	/* default input (naturally) */
188 		macro();
189 	}
190         else
191                 while (argc--) {
192                         if ((ifp = fopen(*argv, "r")) == NULL)
193                                 oops("%s: %s", *argv, strerror(errno));
194                         else {
195 				sp = -1;
196 				fp = 0;
197 				infile[0] = ifp;
198 				macro();
199 				(void) fclose(ifp);
200 			}
201                         argv++;
202 		}
203 
204 
205 	if (*m4wraps) { 		/* anything for rundown ??   */
206 		ilevel = 0;		/* in case m4wrap includes.. */
207 		bufbase = bp = buf;	/* use the entire buffer   */
208 		putback(EOF);		/* eof is a must !!	     */
209 		pbstr(m4wraps); 	/* user-defined wrapup act   */
210 		macro();		/* last will and testament   */
211 	}
212 
213 	if (active != stdout)
214 		active = stdout;	/* reset output just in case */
215 	for (n = 1; n < MAXOUT; n++)	/* default wrap-up: undivert */
216 		if (outfile[n] != NULL)
217 			getdiv(n);
218 					/* remove bitbucket if used  */
219 	if (outfile[0] != NULL) {
220 		(void) fclose(outfile[0]);
221 		m4temp[UNIQUE] = '0';
222 #ifdef vms
223 		(void) remove(m4temp);
224 #else
225 		(void) unlink(m4temp);
226 #endif
227 	}
228 
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()) == '_' || 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 				oops("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 					oops("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 (isspace(l = gpbc()))
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 					oops("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 (isspace(l = gpbc()))
355 					;
356 				putback(l);
357 				pushs(ep);
358 			}
359 			break;
360 		default:
361 			chrsave(t);			/* stack the char */
362 			break;
363 		}
364 	}
365 }
366 
367 /*
368  * build an input token..
369  * consider only those starting with _ or A-Za-z. This is a
370  * combo with lookup to speed things up.
371  */
372 ndptr
373 inspect(tp)
374 register char *tp;
375 {
376 	register char c;
377 	register char *name = tp;
378 	register char *etp = tp+MAXTOK;
379 	register ndptr p;
380 	register unsigned long h = 0;
381 
382 	while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
383 		h = (h << 5) + h + (*tp++ = c);
384 	putback(c);
385 	if (tp == etp)
386 		oops("token too long", "");
387 
388 	*tp = EOS;
389 
390 	for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
391 		if (STREQ(name, p->name))
392 			break;
393 	return p;
394 }
395 
396 /*
397  * initkwds - initialise m4 keywords as fast as possible.
398  * This very similar to install, but without certain overheads,
399  * such as calling lookup. Malloc is not used for storing the
400  * keyword strings, since we simply use the static  pointers
401  * within keywrds block.
402  */
403 void
404 initkwds() {
405 	register int i;
406 	register int h;
407 	register ndptr p;
408 
409 	for (i = 0; i < MAXKEYS; i++) {
410 		h = hash(keywrds[i].knam);
411 		p = (ndptr) xalloc(sizeof(struct ndblock));
412 		p->nxtptr = hashtab[h];
413 		hashtab[h] = p;
414 		p->name = keywrds[i].knam;
415 		p->defn = null;
416 		p->type = keywrds[i].ktyp | STATIC;
417 	}
418 }
419