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