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