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