* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
static char sccsid
[] = "@(#)main.c 5.3 (Berkeley) %G%";
* Facility: m4 macro processor
* PD m4 is based on the macro tool distributed with the software
* tools (VOS) package, and described in the "SOFTWARE TOOLS" and
* "SOFTWARE TOOLS IN PASCAL" books. It has been expanded to include
* most of the command set of SysV m4, the standard UN*X macro processor.
* Since both PD m4 and UN*X m4 are based on SOFTWARE TOOLS macro,
* there may be certain implementation similarities between
* the two. The PD m4 was produced without ANY references to m4
* Software Tools distribution: macro
* Kernighan, Brian W. and P. J. Plauger, SOFTWARE
* TOOLS IN PASCAL, Addison-Wesley, Mass. 1981
* Kernighan, Brian W. and P. J. Plauger, SOFTWARE
* TOOLS, Addison-Wesley, Mass. 1976
* Kernighan, Brian W. and Dennis M. Ritchie,
* THE M4 MACRO PROCESSOR, Unix Programmer's Manual,
* Seventh Edition, Vol. 2, Bell Telephone Labs, 1979
* System V man page for M4
* Jan 28 1986 Oz Break the whole thing into little
* pieces, for easier (?) maintenance.
* Dec 12 1985 Oz Optimize the code, try to squeeze
* Dec 05 1985 Oz Add getopt interface, define (-D),
* Oct 21 1985 Oz Clean up various bugs, add comment handling.
* June 7 1985 Oz Add some of SysV m4 stuff (m4wrap, pushdef,
* popdef, decr, shift etc.).
* June 5 1985 Oz Initial cut.
* [1] PD m4 uses a different (and simpler) stack mechanism than the one
* described in Software Tools and Software Tools in Pascal books.
* The triple stack nonsense is replaced with a single stack containing
* the call frames and the arguments. Each frame is back-linked to a
* previous stack frame, which enables us to rewind the stack after
* each nested call is completed. Each argument is a character pointer
* to the beginning of the argument string within the string space.
* The only exceptions to this are (*) arg 0 and arg 1, which are
* the macro definition and macro name strings, stored dynamically
* | arg 3 ------------------------------->| str |
* | arg 2 --------------+ .
* | plev | <-- fp +---------------->| str |
* | prcf -----------+ plev: paren level
* +-------+ | type: call type
* | . | | prcf: prev. call frame
* [2] We have three types of null values:
* nil - nodeblock pointer type 0
* null - null string ("")
* NULL - Stdio-defined NULL
ndptr hashtab
[HASHSIZE
]; /* hash table for macros etc. */
char buf
[BUFSIZE
]; /* push-back buffer */
char *bp
= buf
; /* first available character */
char *endpbb
= buf
+BUFSIZE
; /* end of push-back buffer */
stae mstack
[STACKMAX
+1]; /* stack of m4 machine */
char strspace
[STRSPMAX
+1]; /* string space for evaluation */
char *ep
= strspace
; /* first free char in strspace */
char *endest
= strspace
+STRSPMAX
;/* end of string space */
int sp
; /* current m4 stack pointer */
int fp
; /* m4 call frame pointer */
FILE *infile
[MAXINP
]; /* input file stack (0=stdin) */
FILE *outfile
[MAXOUT
]; /* diversion array(0=bitbucket)*/
FILE *active
; /* active output file pointer */
char *m4temp
; /* filename for diversions */
int ilevel
= 0; /* input file stack pointer */
int oindex
= 0; /* diversion index.. */
char *null
= ""; /* as it says.. just a null.. */
char *m4wraps
= ""; /* m4wrap string default.. */
char lquote
= LQUOTE
; /* left quote character (`) */
char rquote
= RQUOTE
; /* right quote character (') */
char scommt
= SCOMMT
; /* start character for comment */
char ecommt
= ECOMMT
; /* end character for comment */
struct keyblk keywrds
[] = { /* m4 keywords to be installed */
#define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk))
if (signal(SIGINT
, SIG_IGN
) != SIG_IGN
)
while ((c
= getopt(argc
, argv
, "tD:U:o:")) != EOF
)
case 'D': /* define something..*/
for (p
= optarg
; *p
; p
++)
case 'U': /* undefine... */
case 'o': /* specific output */
infile
[0] = stdin
; /* default input (naturally) */
active
= stdout
; /* default active output */
m4temp
= mktemp(DIVNAM
); /* filename for diversions */
sp
= -1; /* stack pointer initialized */
fp
= 0; /* frame pointer initialized */
macro(); /* get some work done here */
if (*m4wraps
) { /* anything for rundown ?? */
ilevel
= 0; /* in case m4wrap includes.. */
putback(EOF
); /* eof is a must !! */
pbstr(m4wraps
); /* user-defined wrapup act */
macro(); /* last will and testament */
active
= stdout
; /* reset output just in case */
for (n
= 1; n
< MAXOUT
; n
++) /* default wrap-up: undivert */
/* remove bitbucket if used */
if (outfile
[0] != NULL
) {
(void) fclose(outfile
[0]);
ndptr
inspect(); /* forward ... */
* macro - the work horse..
if ((t
= gpbc()) == '_' || isalpha(t
)) {
if ((p
= inspect(s
= token
)) == nil
) {
* real thing.. First build a call frame:
pushf(fp
); /* previous call frm */
pushf(p
->type
); /* type of the call */
pushf(0); /* parenthesis level */
fp
= sp
; /* new frame pointer */
* now push the string arguments:
pushs(p
->defn
); /* defn string */
pushs(p
->name
); /* macro name */
pushs(ep
); /* start next..*/
if (l
!= LPAREN
) { /* add bracks */
error("m4: unexpected end of input");
break; /* all done thanks.. */
(void) fclose(infile
[ilevel
+1]);
* non-alpha single-char token seen..
* [the order of else if .. stmts is
else if (t
== lquote
) { /* strip quotes */
if ((l
= gpbc()) == rquote
)
error("m4: missing right quote");
else if (sp
< 0) { /* not in a macro at all */
if (t
== scommt
) { /* comment handling here */
while ((t
= gpbc()) != ecommt
)
putc(t
, active
); /* output directly.. */
while (isspace(l
= gpbc()))
; /* skip blank, tab, nl.. */
else { /* end of argument list */
error("m4: internal stack overflow");
expand(mstack
+fp
+1, sp
-fp
);
eval(mstack
+fp
+1, sp
-fp
, CALTYP
);
ep
= PREVEP
; /* flush strspace */
sp
= PREVSP
; /* previous sp.. */
fp
= PREVFP
; /* rewind stack...*/
chrsave(EOS
); /* new argument */
while (isspace(l
= gpbc()))
chrsave(t
); /* stack the char */
* consider only those starting with _ or A-Za-z. This is a
* combo with lookup to speed things up.
register char *name
= tp
;
register char *etp
= tp
+MAXTOK
;
while (tp
< etp
&& (isalnum(c
= gpbc()) || c
== '_'))
error("m4: token too long");
for (p
= hashtab
[h
%HASHSIZE
]; p
!= nil
; p
= p
->nxtptr
)
if (strcmp(name
, p
->name
) == 0)
* initm4 - initialize various tables. Useful only if your system
* does not know anything about demand-zero pages.
for (i
= 0; i
< HASHSIZE
; i
++)
for (i
= 0; i
< MAXOUT
; i
++)
* initkwds - initialise m4 keywords as fast as possible.
* This very similar to install, but without certain overheads,
* such as calling lookup. Malloc is not used for storing the
* keyword strings, since we simply use the static pointers
* within keywrds block. We also assume that there is enough memory
* to at least install the keywords (i.e. malloc won't fail).
for (i
= 0; i
< MAXKEYS
; i
++) {
h
= hash(keywrds
[i
].knam
);
p
= (ndptr
) malloc(sizeof(struct ndblock
));
p
->name
= keywrds
[i
].knam
;
p
->type
= keywrds
[i
].ktyp
| STATIC
;