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