new version from Eamonn McManus <emcmanus@cs.tcd.ie>
[unix-history] / usr / src / usr.bin / m4 / main.c
CommitLineData
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 22static 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
122ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */
123char buf[BUFSIZE]; /* push-back buffer */
124char *bp = buf; /* first available character */
125char *endpbb = buf+BUFSIZE; /* end of push-back buffer */
126stae mstack[STACKMAX+1]; /* stack of m4 machine */
127char strspace[STRSPMAX+1]; /* string space for evaluation */
128char *ep = strspace; /* first free char in strspace */
129char *endest= strspace+STRSPMAX;/* end of string space */
130int sp; /* current m4 stack pointer */
131int fp; /* m4 call frame pointer */
132FILE *infile[MAXINP]; /* input file stack (0=stdin) */
133FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/
134FILE *active; /* active output file pointer */
135char *m4temp; /* filename for diversions */
136int ilevel = 0; /* input file stack pointer */
137int oindex = 0; /* diversion index.. */
138char *null = ""; /* as it says.. just a null.. */
139char *m4wraps = ""; /* m4wrap string default.. */
140char lquote = LQUOTE; /* left quote character (`) */
141char rquote = RQUOTE; /* right quote character (') */
142char scommt = SCOMMT; /* start character for comment */
143char ecommt = ECOMMT; /* end character for comment */
144struct 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
185extern ndptr lookup();
186extern ndptr addent();
187extern int onintr();
188
189extern char *malloc();
190extern char *mktemp();
191
192extern int optind;
193extern char *optarg;
194
195main(argc,argv)
196char *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
259ndptr inspect(); /* forward ... */
260
261/*
262 * macro - the work horse..
263 *
264 */
265macro() {
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 */
402ndptr
403inspect(tp)
404register 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 */
430initm4()
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 */
450initkwds() {
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}