3 * Facility: m4 macro processor
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
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
72 * | arg 3 ------------------------------->| str |
74 * | arg 2 --------------+ .
78 * | plev | <-- fp +---------------->| str |
82 * | prcf -----------+ plev: paren level
83 * +-------+ | type: call type
84 * | . | | prcf: prev. call frame
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 */
122 "sinclude", SINCTYPE
,
135 "changequote", CHNQTYPE
,
136 "changecom", CHNCTYPE
,
146 "translit", TRNLTYPE
,
147 "undefine", UNDFTYPE
,
148 "undivert", UNDVTYPE
,
150 "maketemp", MKTMTYPE
,
151 "errprint", ERRPTYPE
,
167 #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk))
179 static char divnam
[] = DIVNAM
;
181 if (signal(SIGINT
, SIG_IGN
) != SIG_IGN
)
182 signal(SIGINT
, onintr
);
188 while ((c
= getopt(argc
, argv
, "tD:U:o:")) != EOF
) {
191 case 'D': /* define something..*/
192 for (p
= optarg
; *p
; p
++)
199 case 'U': /* undefine... */
200 remhash(optarg
, TOP
);
202 case 'o': /* specific output */
212 if(argc
> 1) { usage(); }
213 infile
[0] = stdin
; /* default input (naturally) */
215 if(!(infile
[0] = fopen(argv
[0], "r"))) {
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
)
240 /* remove bitbucket if used */
241 if (outfile
[0] != NULL
) {
242 (void) fclose(outfile
[0]);
243 m4temp
[UNIQUE
] = '0';
245 (void) remove(m4temp
);
247 (void) unlink(m4temp
);
255 * macro - the work horse..
266 if ((t
= gpbc()) == '_' || isalpha(t
)) {
268 if ((p
= inspect(s
= token
)) == nil
) {
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..*/
294 if (l
!= LPAREN
) { /* add bracks */
302 error("m4: unexpected end of input");
304 break; /* all done thanks.. */
305 (void) fclose(infile
[ilevel
+1]);
309 * non-alpha single-char token seen..
310 * [the order of else if .. stmts is
314 else if (t
== lquote
) { /* strip quotes */
317 if ((l
= gpbc()) == rquote
)
319 else if (l
== lquote
)
322 error("m4: missing right quote");
333 else if (sp
< 0) { /* not in a macro at all */
334 if (t
== scommt
) { /* comment handling here */
336 while ((t
= gpbc()) != ecommt
)
339 putc(t
, active
); /* output directly.. */
347 while (isspace(l
= gpbc()))
348 ; /* skip blank, tab, nl.. */
356 else { /* end of argument list */
360 error("m4: internal stack overflow");
362 if (CALTYP
== MACRTYPE
)
363 expand((char**)mstack
+fp
+1,(int)sp
-fp
);
365 eval((char**)mstack
+fp
+1,sp
-fp
,CALTYP
);
367 ep
= PREVEP
; /* flush strspace */
368 sp
= PREVSP
; /* previous sp.. */
369 fp
= PREVFP
; /* rewind stack...*/
375 chrsave(EOS
); /* new argument */
376 while (isspace(l
= gpbc()))
383 chrsave(t
); /* stack the char */
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.
401 register char *name
= tp
;
402 register char *etp
= tp
+MAXTOK
;
405 while (tp
< etp
&& (isalnum(c
= gpbc()) || c
== '_'))
409 error("m4: token too long");
411 for (p
= hashtab
[h
%HASHSIZE
]; p
!= nil
; p
= p
->nxtptr
)
412 if (strcmp(name
, p
->name
) == 0)
419 * initm4 - initialize various tables. Useful only if your system
420 * does not know anything about demand-zero pages.
427 for (i
= 0; i
< HASHSIZE
; i
++)
429 for (i
= 0; i
< MAXOUT
; i
++)
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).
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
];
453 p
->name
= keywrds
[i
].knam
;
455 p
->type
= keywrds
[i
].ktyp
| STATIC
;