Commit | Line | Data |
---|---|---|
138ca30e KB |
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. | |
7 | * | |
8 | * Redistribution and use in source and binary forms are permitted | |
9 | * provided that the above copyright notice and this paragraph are | |
10 | * duplicated in all such forms and that any documentation, | |
11 | * advertising materials, and other materials related to such | |
12 | * distribution and use acknowledge that the software was developed | |
13 | * by the University of California, Berkeley. The name of the | |
14 | * University may not be used to endorse or promote products derived | |
15 | * from this software without specific prior written permission. | |
16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
17 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
19 | */ | |
20 | ||
21 | #ifndef lint | |
406aced9 | 22 | static char sccsid[] = "@(#)main.c 5.2 (Berkeley) %G%"; |
138ca30e KB |
23 | #endif /* not lint */ |
24 | ||
25 | /* | |
26 | * main.c | |
27 | * Facility: m4 macro processor | |
28 | * by: oz | |
29 | */ | |
30 | ||
31 | #include "mdef.h" | |
32 | ||
33 | /* | |
34 | * m4 - macro processor | |
35 | * | |
36 | * PD m4 is based on the macro tool distributed with the software | |
37 | * tools (VOS) package, and described in the "SOFTWARE TOOLS" and | |
38 | * "SOFTWARE TOOLS IN PASCAL" books. It has been expanded to include | |
39 | * most of the command set of SysV m4, the standard UN*X macro processor. | |
40 | * | |
41 | * Since both PD m4 and UN*X m4 are based on SOFTWARE TOOLS macro, | |
42 | * there may be certain implementation similarities between | |
43 | * the two. The PD m4 was produced without ANY references to m4 | |
44 | * sources. | |
45 | * | |
46 | * References: | |
47 | * | |
48 | * Software Tools distribution: macro | |
49 | * | |
50 | * Kernighan, Brian W. and P. J. Plauger, SOFTWARE | |
51 | * TOOLS IN PASCAL, Addison-Wesley, Mass. 1981 | |
52 | * | |
53 | * Kernighan, Brian W. and P. J. Plauger, SOFTWARE | |
54 | * TOOLS, Addison-Wesley, Mass. 1976 | |
55 | * | |
56 | * Kernighan, Brian W. and Dennis M. Ritchie, | |
57 | * THE M4 MACRO PROCESSOR, Unix Programmer's Manual, | |
58 | * Seventh Edition, Vol. 2, Bell Telephone Labs, 1979 | |
59 | * | |
60 | * System V man page for M4 | |
61 | * | |
62 | * Modification History: | |
63 | * | |
64 | * Jan 28 1986 Oz Break the whole thing into little | |
65 | * pieces, for easier (?) maintenance. | |
66 | * | |
67 | * Dec 12 1985 Oz Optimize the code, try to squeeze | |
68 | * few microseconds out.. | |
69 | * | |
70 | * Dec 05 1985 Oz Add getopt interface, define (-D), | |
71 | * undefine (-U) options. | |
72 | * | |
73 | * Oct 21 1985 Oz Clean up various bugs, add comment handling. | |
74 | * | |
75 | * June 7 1985 Oz Add some of SysV m4 stuff (m4wrap, pushdef, | |
76 | * popdef, decr, shift etc.). | |
77 | * | |
78 | * June 5 1985 Oz Initial cut. | |
79 | * | |
80 | * Implementation Notes: | |
81 | * | |
82 | * [1] PD m4 uses a different (and simpler) stack mechanism than the one | |
83 | * described in Software Tools and Software Tools in Pascal books. | |
84 | * The triple stack nonsense is replaced with a single stack containing | |
85 | * the call frames and the arguments. Each frame is back-linked to a | |
86 | * previous stack frame, which enables us to rewind the stack after | |
87 | * each nested call is completed. Each argument is a character pointer | |
88 | * to the beginning of the argument string within the string space. | |
89 | * The only exceptions to this are (*) arg 0 and arg 1, which are | |
90 | * the macro definition and macro name strings, stored dynamically | |
91 | * for the hash table. | |
92 | * | |
93 | * . . | |
94 | * | . | <-- sp | . | | |
95 | * +-------+ +-----+ | |
96 | * | arg 3 ------------------------------->| str | | |
97 | * +-------+ | . | | |
98 | * | arg 2 --------------+ . | |
99 | * +-------+ | | |
100 | * * | | | | |
101 | * +-------+ | +-----+ | |
102 | * | plev | <-- fp +---------------->| str | | |
103 | * +-------+ | . | | |
104 | * | type | . | |
105 | * +-------+ | |
106 | * | prcf -----------+ plev: paren level | |
107 | * +-------+ | type: call type | |
108 | * | . | | prcf: prev. call frame | |
109 | * . | | |
110 | * +-------+ | | |
111 | * | <----------+ | |
112 | * +-------+ | |
113 | * | |
114 | * [2] We have three types of null values: | |
115 | * | |
116 | * nil - nodeblock pointer type 0 | |
117 | * null - null string ("") | |
118 | * NULL - Stdio-defined NULL | |
119 | * | |
120 | */ | |
121 | ||
122 | ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ | |
123 | char buf[BUFSIZE]; /* push-back buffer */ | |
124 | char *bp = buf; /* first available character */ | |
125 | char *endpbb = buf+BUFSIZE; /* end of push-back buffer */ | |
126 | stae mstack[STACKMAX+1]; /* stack of m4 machine */ | |
127 | char strspace[STRSPMAX+1]; /* string space for evaluation */ | |
128 | char *ep = strspace; /* first free char in strspace */ | |
129 | char *endest= strspace+STRSPMAX;/* end of string space */ | |
130 | int sp; /* current m4 stack pointer */ | |
131 | int fp; /* m4 call frame pointer */ | |
132 | FILE *infile[MAXINP]; /* input file stack (0=stdin) */ | |
133 | FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/ | |
134 | FILE *active; /* active output file pointer */ | |
135 | char *m4temp; /* filename for diversions */ | |
136 | int ilevel = 0; /* input file stack pointer */ | |
137 | int oindex = 0; /* diversion index.. */ | |
138 | char *null = ""; /* as it says.. just a null.. */ | |
139 | char *m4wraps = ""; /* m4wrap string default.. */ | |
140 | char lquote = LQUOTE; /* left quote character (`) */ | |
141 | char rquote = RQUOTE; /* right quote character (') */ | |
142 | char scommt = SCOMMT; /* start character for comment */ | |
143 | char ecommt = ECOMMT; /* end character for comment */ | |
144 | struct keyblk keywrds[] = { /* m4 keywords to be installed */ | |
145 | "include", INCLTYPE, | |
146 | "sinclude", SINCTYPE, | |
147 | "define", DEFITYPE, | |
148 | "defn", DEFNTYPE, | |
149 | "divert", DIVRTYPE, | |
150 | "expr", EXPRTYPE, | |
151 | "eval", EXPRTYPE, | |
152 | "substr", SUBSTYPE, | |
153 | "ifelse", IFELTYPE, | |
154 | "ifdef", IFDFTYPE, | |
155 | "len", LENGTYPE, | |
156 | "incr", INCRTYPE, | |
157 | "decr", DECRTYPE, | |
158 | "dnl", DNLNTYPE, | |
159 | "changequote", CHNQTYPE, | |
160 | "changecom", CHNCTYPE, | |
161 | "index", INDXTYPE, | |
162 | #ifdef EXTENDED | |
163 | "paste", PASTTYPE, | |
164 | "spaste", SPASTYPE, | |
165 | #endif | |
166 | "popdef", POPDTYPE, | |
167 | "pushdef", PUSDTYPE, | |
168 | "dumpdef", DUMPTYPE, | |
169 | "shift", SHIFTYPE, | |
170 | "translit", TRNLTYPE, | |
171 | "undefine", UNDFTYPE, | |
172 | "undivert", UNDVTYPE, | |
173 | "divnum", DIVNTYPE, | |
174 | "maketemp", MKTMTYPE, | |
175 | "errprint", ERRPTYPE, | |
176 | "m4wrap", M4WRTYPE, | |
177 | "m4exit", EXITTYPE, | |
138ca30e KB |
178 | "syscmd", SYSCTYPE, |
179 | "sysval", SYSVTYPE, | |
138ca30e | 180 | "unix", MACRTYPE, |
138ca30e KB |
181 | }; |
182 | ||
183 | #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) | |
184 | ||
185 | extern ndptr lookup(); | |
186 | extern ndptr addent(); | |
187 | extern int onintr(); | |
188 | ||
189 | extern char *malloc(); | |
190 | extern char *mktemp(); | |
191 | ||
192 | extern int optind; | |
193 | extern char *optarg; | |
194 | ||
195 | main(argc,argv) | |
196 | char *argv[]; | |
197 | { | |
198 | register int c; | |
199 | register int n; | |
200 | char *p; | |
201 | ||
202 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) | |
203 | signal(SIGINT, onintr); | |
204 | #ifdef NONZEROPAGES | |
205 | initm4(); | |
206 | #endif | |
207 | initkwds(); | |
208 | ||
209 | while ((c = getopt(argc, argv, "tD:U:o:")) != EOF) | |
210 | switch(c) { | |
211 | ||
212 | case 'D': /* define something..*/ | |
213 | for (p = optarg; *p; p++) | |
214 | if (*p == '=') | |
215 | break; | |
216 | if (*p) | |
217 | *p++ = EOS; | |
218 | dodefine(optarg, p); | |
219 | break; | |
220 | case 'U': /* undefine... */ | |
221 | remhash(optarg, TOP); | |
222 | break; | |
223 | case 'o': /* specific output */ | |
224 | case '?': | |
225 | default: | |
226 | usage(); | |
227 | } | |
228 | ||
229 | infile[0] = stdin; /* default input (naturally) */ | |
230 | active = stdout; /* default active output */ | |
231 | m4temp = mktemp(DIVNAM); /* filename for diversions */ | |
232 | ||
233 | sp = -1; /* stack pointer initialized */ | |
234 | fp = 0; /* frame pointer initialized */ | |
235 | ||
236 | macro(); /* get some work done here */ | |
237 | ||
238 | if (*m4wraps) { /* anything for rundown ?? */ | |
239 | ilevel = 0; /* in case m4wrap includes.. */ | |
240 | putback(EOF); /* eof is a must !! */ | |
241 | pbstr(m4wraps); /* user-defined wrapup act */ | |
242 | macro(); /* last will and testament */ | |
243 | } | |
244 | else /* default wrap-up: undivert */ | |
245 | for (n = 1; n < MAXOUT; n++) | |
246 | if (outfile[n] != NULL) | |
247 | getdiv(n); | |
248 | ||
249 | /* remove bitbucket if used */ | |
250 | if (outfile[0] != NULL) { | |
251 | (void) fclose(outfile[0]); | |
252 | m4temp[UNIQUE] = '0'; | |
138ca30e | 253 | (void) unlink(m4temp); |
138ca30e KB |
254 | } |
255 | ||
256 | exit(0); | |
257 | } | |
258 | ||
259 | ndptr inspect(); /* forward ... */ | |
260 | ||
261 | /* | |
262 | * macro - the work horse.. | |
263 | * | |
264 | */ | |
265 | macro() { | |
266 | char token[MAXTOK]; | |
267 | register char *s; | |
268 | register int t, l; | |
269 | register ndptr p; | |
270 | register int nlpar; | |
271 | ||
272 | cycle { | |
273 | if ((t = gpbc()) == '_' || isalpha(t)) { | |
274 | putback(t); | |
275 | if ((p = inspect(s = token)) == nil) { | |
276 | if (sp < 0) | |
277 | while (*s) | |
278 | putc(*s++, active); | |
279 | else | |
280 | while (*s) | |
281 | chrsave(*s++); | |
282 | } | |
283 | else { | |
284 | /* | |
285 | * real thing.. First build a call frame: | |
286 | * | |
287 | */ | |
288 | pushf(fp); /* previous call frm */ | |
289 | pushf(p->type); /* type of the call */ | |
290 | pushf(0); /* parenthesis level */ | |
291 | fp = sp; /* new frame pointer */ | |
292 | /* | |
293 | * now push the string arguments: | |
294 | * | |
295 | */ | |
296 | pushs(p->defn); /* defn string */ | |
297 | pushs(p->name); /* macro name */ | |
298 | pushs(ep); /* start next..*/ | |
299 | ||
300 | putback(l = gpbc()); | |
301 | if (l != LPAREN) { /* add bracks */ | |
302 | putback(RPAREN); | |
303 | putback(LPAREN); | |
304 | } | |
305 | } | |
306 | } | |
307 | else if (t == EOF) { | |
308 | if (sp > -1) | |
309 | error("m4: unexpected end of input"); | |
310 | if (--ilevel < 0) | |
311 | break; /* all done thanks.. */ | |
312 | (void) fclose(infile[ilevel+1]); | |
313 | continue; | |
314 | } | |
315 | /* | |
316 | * non-alpha single-char token seen.. | |
317 | * [the order of else if .. stmts is | |
318 | * important.] | |
319 | * | |
320 | */ | |
321 | else if (t == lquote) { /* strip quotes */ | |
322 | nlpar = 1; | |
323 | do { | |
324 | if ((l = gpbc()) == rquote) | |
325 | nlpar--; | |
326 | else if (l == lquote) | |
327 | nlpar++; | |
328 | else if (l == EOF) | |
329 | error("m4: missing right quote"); | |
330 | if (nlpar > 0) { | |
331 | if (sp < 0) | |
332 | putc(l, active); | |
333 | else | |
334 | chrsave(l); | |
335 | } | |
336 | } | |
337 | while (nlpar != 0); | |
338 | } | |
339 | ||
340 | else if (sp < 0) { /* not in a macro at all */ | |
341 | if (t == scommt) { /* comment handling here */ | |
342 | putc(t, active); | |
343 | while ((t = gpbc()) != ecommt) | |
344 | putc(t, active); | |
345 | } | |
346 | putc(t, active); /* output directly.. */ | |
347 | } | |
348 | ||
349 | else switch(t) { | |
350 | ||
351 | case LPAREN: | |
352 | if (PARLEV > 0) | |
353 | chrsave(t); | |
354 | while (isspace(l = gpbc())) | |
355 | ; /* skip blank, tab, nl.. */ | |
356 | putback(l); | |
357 | PARLEV++; | |
358 | break; | |
359 | ||
360 | case RPAREN: | |
361 | if (--PARLEV > 0) | |
362 | chrsave(t); | |
363 | else { /* end of argument list */ | |
364 | chrsave(EOS); | |
365 | ||
366 | if (sp == STACKMAX) | |
367 | error("m4: internal stack overflow"); | |
368 | ||
369 | if (CALTYP == MACRTYPE) | |
370 | expand(mstack+fp+1, sp-fp); | |
371 | else | |
372 | eval(mstack+fp+1, sp-fp, CALTYP); | |
373 | ||
374 | ep = PREVEP; /* flush strspace */ | |
375 | sp = PREVSP; /* previous sp.. */ | |
376 | fp = PREVFP; /* rewind stack...*/ | |
377 | } | |
378 | break; | |
379 | ||
380 | case COMMA: | |
381 | if (PARLEV == 1) { | |
382 | chrsave(EOS); /* new argument */ | |
383 | while (isspace(l = gpbc())) | |
384 | ; | |
385 | putback(l); | |
386 | pushs(ep); | |
387 | } | |
388 | break; | |
389 | default: | |
390 | chrsave(t); /* stack the char */ | |
391 | break; | |
392 | } | |
393 | } | |
394 | } | |
395 | ||
396 | ||
397 | /* | |
398 | * build an input token.. | |
399 | * consider only those starting with _ or A-Za-z. This is a | |
400 | * combo with lookup to speed things up. | |
401 | */ | |
402 | ndptr | |
403 | inspect(tp) | |
404 | register char *tp; | |
405 | { | |
406 | register int h = 0; | |
407 | register char c; | |
408 | register char *name = tp; | |
409 | register char *etp = tp+MAXTOK; | |
410 | register ndptr p; | |
411 | ||
412 | while (tp < etp && (isalnum(c = gpbc()) || c == '_')) | |
413 | h += (*tp++ = c); | |
414 | putback(c); | |
415 | if (tp == etp) | |
416 | error("m4: token too long"); | |
417 | *tp = EOS; | |
418 | for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr) | |
419 | if (strcmp(name, p->name) == 0) | |
420 | break; | |
421 | return(p); | |
422 | } | |
423 | ||
424 | #ifdef NONZEROPAGES | |
425 | /* | |
426 | * initm4 - initialize various tables. Useful only if your system | |
427 | * does not know anything about demand-zero pages. | |
428 | * | |
429 | */ | |
430 | initm4() | |
431 | { | |
432 | register int i; | |
433 | ||
434 | for (i = 0; i < HASHSIZE; i++) | |
435 | hashtab[i] = nil; | |
436 | for (i = 0; i < MAXOUT; i++) | |
437 | outfile[i] = NULL; | |
438 | } | |
439 | #endif | |
440 | ||
441 | /* | |
442 | * initkwds - initialise m4 keywords as fast as possible. | |
443 | * This very similar to install, but without certain overheads, | |
444 | * such as calling lookup. Malloc is not used for storing the | |
445 | * keyword strings, since we simply use the static pointers | |
446 | * within keywrds block. We also assume that there is enough memory | |
447 | * to at least install the keywords (i.e. malloc won't fail). | |
448 | * | |
449 | */ | |
450 | initkwds() { | |
451 | register int i; | |
452 | register int h; | |
453 | register ndptr p; | |
454 | ||
455 | for (i = 0; i < MAXKEYS; i++) { | |
456 | h = hash(keywrds[i].knam); | |
457 | p = (ndptr) malloc(sizeof(struct ndblock)); | |
458 | p->nxtptr = hashtab[h]; | |
459 | hashtab[h] = p; | |
460 | p->name = keywrds[i].knam; | |
461 | p->defn = null; | |
462 | p->type = keywrds[i].ktyp | STATIC; | |
463 | } | |
464 | } |