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