xref: /original-bsd/usr.bin/m4/main.c (revision e59fb703)
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  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 static char sccsid[] = "@(#)main.c	5.9 (Berkeley) 11/20/91";
13 #endif /* not lint */
14 
15 /*
16  * main.c
17  * Facility: m4 macro processor
18  * by: oz
19  */
20 
21 #include <sys/types.h>
22 #include <signal.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <string.h>
28 #include "mdef.h"
29 #include "stdd.h"
30 #include "extern.h"
31 #include "pathnames.h"
32 
33 ndptr hashtab[HASHSIZE];	/* hash table for macros etc.  */
34 char buf[BUFSIZE];		/* push-back buffer	       */
35 char *bufbase = buf;		/* the base for current ilevel */
36 char *bbase[MAXINP];		/* the base for each ilevel    */
37 char *bp = buf; 		/* first available character   */
38 char *endpbb = buf+BUFSIZE;	/* end of push-back buffer     */
39 stae mstack[STACKMAX+1]; 	/* stack of m4 machine         */
40 char strspace[STRSPMAX+1];	/* string space for evaluation */
41 char *ep = strspace;		/* first free char in strspace */
42 char *endest= strspace+STRSPMAX;/* end of string space	       */
43 int sp; 			/* current m4  stack pointer   */
44 int fp; 			/* m4 call frame pointer       */
45 FILE *infile[MAXINP];		/* input file stack (0=stdin)  */
46 FILE *outfile[MAXOUT];		/* diversion array(0=bitbucket)*/
47 FILE *active;			/* active output file pointer  */
48 char *m4temp;			/* filename for diversions     */
49 int ilevel = 0; 		/* input file stack pointer    */
50 int oindex = 0; 		/* diversion index..	       */
51 char *null = "";                /* as it says.. just a null..  */
52 char *m4wraps = "";             /* m4wrap string default..     */
53 char *progname;			/* name of this program        */
54 char lquote = LQUOTE;		/* left quote character  (`)   */
55 char rquote = RQUOTE;		/* right quote character (')   */
56 char scommt = SCOMMT;		/* start character for comment */
57 char ecommt = ECOMMT;		/* end character for comment   */
58 
59 struct keyblk keywrds[] = {	/* m4 keywords to be installed */
60 	"include",      INCLTYPE,
61 	"sinclude",     SINCTYPE,
62 	"define",       DEFITYPE,
63 	"defn",         DEFNTYPE,
64 	"divert",       DIVRTYPE,
65 	"expr",         EXPRTYPE,
66 	"eval",         EXPRTYPE,
67 	"substr",       SUBSTYPE,
68 	"ifelse",       IFELTYPE,
69 	"ifdef",        IFDFTYPE,
70 	"len",          LENGTYPE,
71 	"incr",         INCRTYPE,
72 	"decr",         DECRTYPE,
73 	"dnl",          DNLNTYPE,
74 	"changequote",  CHNQTYPE,
75 	"changecom",    CHNCTYPE,
76 	"index",        INDXTYPE,
77 #ifdef EXTENDED
78 	"paste",        PASTTYPE,
79 	"spaste",       SPASTYPE,
80 #endif
81 	"popdef",       POPDTYPE,
82 	"pushdef",      PUSDTYPE,
83 	"dumpdef",      DUMPTYPE,
84 	"shift",        SHIFTYPE,
85 	"translit",     TRNLTYPE,
86 	"undefine",     UNDFTYPE,
87 	"undivert",     UNDVTYPE,
88 	"divnum",       DIVNTYPE,
89 	"maketemp",     MKTMTYPE,
90 	"errprint",     ERRPTYPE,
91 	"m4wrap",       M4WRTYPE,
92 	"m4exit",       EXITTYPE,
93 	"syscmd",       SYSCTYPE,
94 	"sysval",       SYSVTYPE,
95 
96 #ifdef unix
97 	"unix",         MACRTYPE,
98 #else
99 #ifdef vms
100 	"vms",          MACRTYPE,
101 #endif
102 #endif
103 };
104 
105 #define MAXKEYS	(sizeof(keywrds)/sizeof(struct keyblk))
106 
107 extern int optind;
108 extern char *optarg;
109 
110 void macro();
111 void initkwds();
112 extern int getopt();
113 
114 int
115 main(argc,argv)
116 int argc;
117 char *argv[];
118 {
119 	register int c;
120 	register int n;
121 	char *p;
122 	register FILE *ifp;
123 
124 	progname = basename(argv[0]);
125 
126 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
127 		signal(SIGINT, onintr);
128 
129 	initkwds();
130 
131 	while ((c = getopt(argc, argv, "tD:U:o:")) != EOF)
132 		switch(c) {
133 
134 		case 'D':               /* define something..*/
135 			for (p = optarg; *p; p++)
136 				if (*p == '=')
137 					break;
138 			if (*p)
139 				*p++ = EOS;
140 			dodefine(optarg, p);
141 			break;
142 		case 'U':               /* undefine...       */
143 			remhash(optarg, TOP);
144 			break;
145 		case 'o':		/* specific output   */
146 		case '?':
147 			usage();
148 		}
149 
150         argc -= optind;
151         argv += optind;
152 
153 	active = stdout;		/* default active output     */
154 					/* filename for diversions   */
155 	m4temp = mktemp(xstrdup(_PATH_DIVNAME));
156 
157 	bbase[0] = bufbase;
158         if (!argc) {
159  		sp = -1;		/* stack pointer initialized */
160 		fp = 0; 		/* frame pointer initialized */
161 		infile[0] = stdin;	/* default input (naturally) */
162 		macro();
163 	}
164         else
165                 while (argc--) {
166                         if ((ifp = fopen(*argv, "r")) == NULL)
167                                 oops("%s: %s", *argv, strerror(errno));
168                         else {
169 				sp = -1;
170 				fp = 0;
171 				infile[0] = ifp;
172 				macro();
173 				(void) fclose(ifp);
174 			}
175                         argv++;
176 		}
177 
178 
179 	if (*m4wraps) { 		/* anything for rundown ??   */
180 		ilevel = 0;		/* in case m4wrap includes.. */
181 		bufbase = bp = buf;	/* use the entire buffer   */
182 		putback(EOF);		/* eof is a must !!	     */
183 		pbstr(m4wraps); 	/* user-defined wrapup act   */
184 		macro();		/* last will and testament   */
185 	}
186 
187 	if (active != stdout)
188 		active = stdout;	/* reset output just in case */
189 	for (n = 1; n < MAXOUT; n++)	/* default wrap-up: undivert */
190 		if (outfile[n] != NULL)
191 			getdiv(n);
192 					/* remove bitbucket if used  */
193 	if (outfile[0] != NULL) {
194 		(void) fclose(outfile[0]);
195 		m4temp[UNIQUE] = '0';
196 #ifdef vms
197 		(void) remove(m4temp);
198 #else
199 		(void) unlink(m4temp);
200 #endif
201 	}
202 
203 	return 0;
204 }
205 
206 ndptr inspect();
207 
208 /*
209  * macro - the work horse..
210  */
211 void
212 macro() {
213 	char token[MAXTOK];
214 	register char *s;
215 	register int t, l;
216 	register ndptr p;
217 	register int  nlpar;
218 
219 	cycle {
220 		if ((t = gpbc()) == '_' || isalpha(t)) {
221 			putback(t);
222 			if ((p = inspect(s = token)) == nil) {
223 				if (sp < 0)
224 					while (*s)
225 						putc(*s++, active);
226 				else
227 					while (*s)
228 						chrsave(*s++);
229 			}
230 			else {
231 		/*
232 		 * real thing.. First build a call frame:
233 		 */
234 				pushf(fp);	/* previous call frm */
235 				pushf(p->type); /* type of the call  */
236 				pushf(0);	/* parenthesis level */
237 				fp = sp;	/* new frame pointer */
238 		/*
239 		 * now push the string arguments:
240 		 */
241 				pushs(p->defn);	      /* defn string */
242 				pushs(p->name);	      /* macro name  */
243 				pushs(ep);	      /* start next..*/
244 
245 				putback(l = gpbc());
246 				if (l != LPAREN)  {   /* add bracks  */
247 					putback(RPAREN);
248 					putback(LPAREN);
249 				}
250 			}
251 		}
252 		else if (t == EOF) {
253 			if (sp > -1)
254 				oops("unexpected end of input", "");
255 			if (--ilevel < 0)
256 				break;			/* all done thanks.. */
257 			(void) fclose(infile[ilevel+1]);
258 			bufbase = bbase[ilevel];
259 			continue;
260 		}
261 	/*
262 	 * non-alpha single-char token seen..
263 	 * [the order of else if .. stmts is important.]
264 	 */
265 		else if (t == lquote) { 		/* strip quotes */
266 			nlpar = 1;
267 			do {
268 				if ((l = gpbc()) == rquote)
269 					nlpar--;
270 				else if (l == lquote)
271 					nlpar++;
272 				else if (l == EOF)
273 					oops("missing right quote", "");
274 				if (nlpar > 0) {
275 					if (sp < 0)
276 						putc(l, active);
277 					else
278 						chrsave(l);
279 				}
280 			}
281 			while (nlpar != 0);
282 		}
283 
284 		else if (sp < 0) {		/* not in a macro at all */
285 			if (t == scommt) {	/* comment handling here */
286 				putc(t, active);
287 				while ((t = gpbc()) != ecommt)
288 					putc(t, active);
289 			}
290 			putc(t, active);	/* output directly..	 */
291 		}
292 
293 		else switch(t) {
294 
295 		case LPAREN:
296 			if (PARLEV > 0)
297 				chrsave(t);
298 			while (isspace(l = gpbc()))
299 				;		/* skip blank, tab, nl.. */
300 			putback(l);
301 			PARLEV++;
302 			break;
303 
304 		case RPAREN:
305 			if (--PARLEV > 0)
306 				chrsave(t);
307 			else {			/* end of argument list */
308 				chrsave(EOS);
309 
310 				if (sp == STACKMAX)
311 					oops("internal stack overflow", "");
312 
313 				if (CALTYP == MACRTYPE)
314 					expand((char **) mstack+fp+1, sp-fp);
315 				else
316 					eval((char **) mstack+fp+1, sp-fp, CALTYP);
317 
318 				ep = PREVEP;	/* flush strspace */
319 				sp = PREVSP;	/* previous sp..  */
320 				fp = PREVFP;	/* rewind stack...*/
321 			}
322 			break;
323 
324 		case COMMA:
325 			if (PARLEV == 1) {
326 				chrsave(EOS);		/* new argument   */
327 				while (isspace(l = gpbc()))
328 					;
329 				putback(l);
330 				pushs(ep);
331 			}
332 			break;
333 		default:
334 			chrsave(t);			/* stack the char */
335 			break;
336 		}
337 	}
338 }
339 
340 /*
341  * build an input token..
342  * consider only those starting with _ or A-Za-z. This is a
343  * combo with lookup to speed things up.
344  */
345 ndptr
346 inspect(tp)
347 register char *tp;
348 {
349 	register char c;
350 	register char *name = tp;
351 	register char *etp = tp+MAXTOK;
352 	register ndptr p;
353 	register unsigned long h = 0;
354 
355 	while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
356 		h = (h << 5) + h + (*tp++ = c);
357 	putback(c);
358 	if (tp == etp)
359 		oops("token too long", "");
360 
361 	*tp = EOS;
362 
363 	for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
364 		if (STREQ(name, p->name))
365 			break;
366 	return p;
367 }
368 
369 /*
370  * initkwds - initialise m4 keywords as fast as possible.
371  * This very similar to install, but without certain overheads,
372  * such as calling lookup. Malloc is not used for storing the
373  * keyword strings, since we simply use the static  pointers
374  * within keywrds block.
375  */
376 void
377 initkwds() {
378 	register int i;
379 	register int h;
380 	register ndptr p;
381 
382 	for (i = 0; i < MAXKEYS; i++) {
383 		h = hash(keywrds[i].knam);
384 		p = (ndptr) xalloc(sizeof(struct ndblock));
385 		p->nxtptr = hashtab[h];
386 		hashtab[h] = p;
387 		p->name = keywrds[i].knam;
388 		p->defn = null;
389 		p->type = keywrds[i].ktyp | STATIC;
390 	}
391 }
392