Path: utzoo!attcan!uunet!husc6!purdue!i.cc.purdue.edu!j.cc.purdue.edu!ain
From: ain@j.cc.purdue.edu (Patrick White)
Newsgroups: comp.sources.amiga
Subject: M4 (sources 1 of 2)
Keywords: m4, macro, processor
Message-ID: <7174@j.cc.purdue.edu>
Date: 31 May 88 23:00:23 GMT
Organization: PUCC Land, USA
Lines: 2022
Approved: ain@j.cc.purdue.edu (Patrick White)
Submitted by: kuhling!jonasf (Jonas Flygare)
Summary: unix m4 look-alike macro processor.
Poster Boy: Patrick White (ain@j.cc.purdue.edu)
Archive Name: sources/amiga/volume5/m4.src.sh1.Z
tested.
NOTES:
I undid the shar to undo the uuencoded compressed files, and to separate
the docs from everything else.
I nroffed the docs so everybody gets a readable copy of the docs.
A patch to some of the test files was included with the origional posting..
I applied the patch to the files and excluded the patch from this posting.
.
-- Pat White (co-moderator comp.sources/binaries.amiga)
ARPA/UUCP: j.cc.purdue.edu!ain BITNET: PATWHITE@PURCCVM PHONE: (317) 743-8421
U.S. Mail: 320 Brown St. apt. 406, West Lafayette, IN 47906
========================================
# This is a shell archive.
# Remove everything above and including the cut line.
# Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
# Run the following text with /bin/sh to create:
# manifest
# makefile
# m4.lnk
# extr.h
# mdef.h
# eval.c
# expr.c
# getopt.c
# look.c
# main.c
# mktemp.c
# This archive created: Mon May 16 09:27:04 1988
# By: Patrick White (PUCC Land, USA)
echo shar: extracting manifest '(230 characters)'
cat << \SHAR_EOF > manifest
mdef.h - definitions and structures
main.c - this file: driver routines
eval.c - general macro evaluator
serv.c - service routines (doxxxx)
misc.c - miscellaneous routines
expr.c - expression parser
look.c - hash table management
SHAR_EOF
if test 230 -ne "`wc -c manifest`"
then
echo shar: error transmitting manifest '(should have been 230 characters)'
fi
echo shar: extracting makefile '(1376 characters)'
cat << \SHAR_EOF > makefile
#
# pd m4 [oz]
#
# -DEXTENDED
# if you like to get paste & spaste macros.
# -DVOID
# if your C compiler does NOT support void.
# -DGETOPT
# if you STILL do not have getopt in your library.
# [This means your library is broken. Fix it.]
# -DDUFFCP
# if you do not have fast memcpy in your library.
#
CFLAGS = -DEXTENDED -DMYMKTMP -cw
DEST = :
MANL = :
OBJS = main.o eval.o serv.o look.o misc.o expr.o
CSRC = main.c eval.c serv.c look.c misc.c expr.c
INCL = mdef.h extr.h
MSRC = ack.m4 hanoi.m4 hash.m4 sqroot.m4 string.m4 test.m4
DOCS = README MANIFEST m4.1
LINKFILE = m4.lnk
MBIN = c:
m4: ${OBJS}
@echo "loading m4.."
@lc ${CFLAGS} ${OBJS}
@blink with ${LINKFILE}
@list m4
${OBJS}: ${INCL}
install: m4
copy ./m4 ${DEST}/m4
copy ./m4.1 ${MANL}/m4.l
deinstall:
delete ${DEST}/m4
delete ${MANL}/m4.l
time: m4
@echo "timing comparisons.."
@echo "un*x m4:"
time ${MBIN}/m4 unxm4.out
@echo "pd m4:"
time ./m4 pdm4.out
@echo "un*x m4:"
time ${MBIN}/m4 unxm4.out
@echo "pd m4:"
time ./m4 pdm4.out
@echo "un*x m4:"
time ${MBIN}/m4 unxm4.out
@echo "pd m4:"
time ./m4 pdm4.out
@echo "output comparisons.."
-diff pdm4.out unxm4.out
@rm -f pdm4.out unxm4.out
clean:
delete #?.o core m4 #?.out
pack:
shar -a makefile ${INCL} ${CSRC} >M4MAIN.SHAR
shar -a ${MSRC} ${DOCS} >M4MSRC.SHAR
SHAR_EOF
if test 1376 -ne "`wc -c makefile`"
then
echo shar: error transmitting makefile '(should have been 1376 characters)'
fi
echo shar: extracting m4.lnk '(119 characters)'
cat << \SHAR_EOF > m4.lnk
FROM LIB:c.o+main.o+eval.o+serv.o+look.o+misc.o+expr.o+getopt.o+mktemp.o
TO m4
LIB LIB:lc.lib+LIB:amiga.lib
MAP m4.map
SHAR_EOF
if test 119 -ne "`wc -c m4.lnk`"
then
echo shar: error transmitting m4.lnk '(should have been 119 characters)'
fi
echo shar: extracting extr.h '(1150 characters)'
cat << \SHAR_EOF > extr.h
extern ndptr hashtab[]; /* hash table for macros etc. */
extern char buf[]; /* push-back buffer */
extern char *bp; /* first available character */
extern char *endpbb; /* end of push-back buffer */
extern stae mstack[]; /* stack of m4 machine */
extern char *ep; /* first free char in strspace */
extern char *endest; /* end of string space */
extern int sp; /* current m4 stack pointer */
extern int fp; /* m4 call frame pointer */
extern FILE *infile[]; /* input file stack (0=stdin) */
extern FILE *outfile[]; /* diversion array(0=bitbucket)*/
extern FILE *active; /* active output file pointer */
extern char *m4temp; /* filename for diversions */
extern int ilevel; /* input file stack pointer */
extern int oindex; /* diversion index.. */
extern char *null; /* as it says.. just a null.. */
extern char *m4wraps; /* m4wrap string default.. */
extern char lquote; /* left quote character (`) */
extern char rquote; /* right quote character (') */
extern char scommt; /* start character for comment */
extern char ecommt; /* end character for comment */
SHAR_EOF
if test 1150 -ne "`wc -c extr.h`"
then
echo shar: error transmitting extr.h '(should have been 1150 characters)'
fi
echo shar: extracting mdef.h '(4970 characters)'
cat << \SHAR_EOF > mdef.h
/*
* mdef.h
* Facility: m4 macro processor
* by: oz
*/
#define unix 1 /* should be here so i don't have to rewrite all the code. */
#ifndef unix
#define unix 0
#endif
#ifndef vms
#define vms 0
#endif
#if vms
#include stdio
#include ctype
#include signal
#else
#include
#include
#include
#include
#endif
/*
*
* m4 constants..
*
*/
#define MACRTYPE 1
#define DEFITYPE 2
#define EXPRTYPE 3
#define SUBSTYPE 4
#define IFELTYPE 5
#define LENGTYPE 6
#define CHNQTYPE 7
#define SYSCTYPE 8
#define UNDFTYPE 9
#define INCLTYPE 10
#define SINCTYPE 11
#define PASTTYPE 12
#define SPASTYPE 13
#define INCRTYPE 14
#define IFDFTYPE 15
#define PUSDTYPE 16
#define POPDTYPE 17
#define SHIFTYPE 18
#define DECRTYPE 19
#define DIVRTYPE 20
#define UNDVTYPE 21
#define DIVNTYPE 22
#define MKTMTYPE 23
#define ERRPTYPE 24
#define M4WRTYPE 25
#define TRNLTYPE 26
#define DNLNTYPE 27
#define DUMPTYPE 28
#define CHNCTYPE 29
#define INDXTYPE 30
#define SYSVTYPE 31
#define EXITTYPE 32
#define DEFNTYPE 33
#define STATIC 128
/*
* m4 special characters
*/
#define ARGFLAG '$'
#define LPAREN '('
#define RPAREN ')'
#define LQUOTE '`'
#define RQUOTE '\''
#define COMMA ','
#define SCOMMT '#'
#define ECOMMT '\n'
/*
* definitions of diversion files. If the name of
* the file is changed, adjust UNIQUE to point to the
* wildcard (*) character in the filename.
*/
#if unix
#define DIVNAM "/tmp/m4*XXXXXX" /* unix diversion files */
#define UNIQUE 7 /* unique char location */
#else
#if vms
#define DIVNAM "sys$login:m4*XXXXXX" /* vms diversion files */
#define UNIQUE 12 /* unique char location */
#else
#if amiga
#define DIVNAM "t:M4*XXXXXX" /* msdos diversion files */
#define UNIQUE 4 /* unique char location */
#else
#define DIVNAM "/M4*XXXXXX" /* msdos diversion files */
#define UNIQUE 3 /* unique char location */
#endif
#endif
#endif
/*
* other important constants
*/
#define EOS (char) 0
#define MAXINP 10 /* maximum include files */
#define MAXOUT 10 /* maximum # of diversions */
#define MAXSTR 512 /* maximum size of string */
#define BUFSIZE 4096 /* size of pushback buffer */
#define STACKMAX 1024 /* size of call stack */
#define STRSPMAX 4096 /* size of string space */
#define MAXTOK MAXSTR /* maximum chars in a tokn */
#define HASHSIZE 199 /* maximum size of hashtab */
#define ALL 1
#define TOP 0
#define TRUE 1
#define FALSE 0
#define cycle for(;;)
#ifdef VOID
#define void int /* define if void is void. */
#endif
/*
* m4 data structures
*/
typedef struct ndblock *ndptr;
struct ndblock { /* hastable structure */
char *name; /* entry name.. */
char *defn; /* definition.. */
int type; /* type of the entry.. */
ndptr nxtptr; /* link to next entry.. */
};
#define nil ((ndptr) 0)
struct keyblk {
char *knam; /* keyword name */
int ktyp; /* keyword type */
};
typedef union { /* stack structure */
int sfra; /* frame entry */
char *sstr; /* string entry */
} stae;
/*
* macros for readibility and/or speed
*
* gpbc() - get a possibly pushed-back character
* min() - select the minimum of two elements
* pushf() - push a call frame entry onto stack
* pushs() - push a string pointer onto stack
*/
#define gpbc() (bp > buf) ? *--bp : getc(infile[ilevel])
/* #define min(x,y) ((x > y) ? y : x) defined in stdio.h.. */
#define pushf(x) if (sp < STACKMAX) mstack[++sp].sfra = (x)
#define pushs(x) if (sp < STACKMAX) mstack[++sp].sstr = (x)
/*
* . .
* | . | <-- sp | . |
* +-------+ +-----+
* | arg 3 ----------------------->| str |
* +-------+ | . |
* | arg 2 ---PREVEP-----+ .
* +-------+ |
* . | | |
* +-------+ | +-----+
* | plev | PARLEV +-------->| str |
* +-------+ | . |
* | type | CALTYP .
* +-------+
* | prcf ---PREVFP--+
* +-------+ |
* | . | PREVSP |
* . |
* +-------+ |
* | <----------+
* +-------+
*
*/
#define PARLEV (mstack[fp].sfra)
#define CALTYP (mstack[fp-1].sfra)
#define PREVEP (mstack[fp+3].sstr)
#define PREVSP (fp-3)
#define PREVFP (mstack[fp-2].sfra)
SHAR_EOF
if test 4970 -ne "`wc -c mdef.h`"
then
echo shar: error transmitting mdef.h '(should have been 4970 characters)'
fi
echo shar: extracting eval.c '(5714 characters)'
cat << \SHAR_EOF > eval.c
/*
* eval.c
* Facility: m4 macro processor
* by: oz
*/
#include "mdef.h"
#include "extr.h"
extern ndptr lookup();
extern char *strsave();
extern char *mktemp();
/*
* eval - evaluate built-in macros.
* argc - number of elements in argv.
* argv - element vector :
* argv[0] = definition of a user
* macro or nil if built-in.
* argv[1] = name of the macro or
* built-in.
* argv[2] = parameters to user-defined
* . macro or built-in.
* .
*
* Note that the minimum value for argc is 3. A call in the form
* of macro-or-builtin() will result in:
* argv[0] = nullstr
* argv[1] = macro-or-builtin
* argv[2] = nullstr
*
*/
int eval (argv, argc, td)
register char *argv[];
register int argc;
register int td;
{
register int c, n;
static int sysval;
#ifdef DEBUG
printf("argc = %d\n", argc);
for (n = 0; n < argc; n++)
printf("argv[%d] = %s\n", n, argv[n]);
#endif
/*
* if argc == 3 and argv[2] is null,
* then we have macro-or-builtin() type call.
* We adjust argc to avoid further checking..
*
*/
if (argc == 3 && !*(argv[2]))
argc--;
switch (td & ~STATIC) {
case DEFITYPE:
if (argc > 2)
dodefine(argv[2], (argc > 3) ? argv[3] : null);
break;
case PUSDTYPE:
if (argc > 2)
dopushdef(argv[2], (argc > 3) ? argv[3] : null);
break;
case DUMPTYPE:
dodump(argv, argc);
break;
case EXPRTYPE:
/*
* doexpr - evaluate arithmetic expression
*
*/
if (argc > 2)
pbnum(expr(argv[2]));
break;
case IFELTYPE:
if (argc > 4)
doifelse(argv, argc);
break;
case IFDFTYPE:
/*
* doifdef - select one of two alternatives based
* on the existence of another definition
*/
if (argc > 3) {
if (lookup(argv[2]) != nil)
pbstr(argv[3]);
else if (argc > 4)
pbstr(argv[4]);
}
break;
case LENGTYPE:
/*
* dolen - find the length of the argument
*
*/
if (argc > 2)
pbnum((argc > 2) ? strlen(argv[2]) : 0);
break;
case INCRTYPE:
/*
* doincr - increment the value of the argument
*
*/
if (argc > 2)
pbnum(atoi(argv[2]) + 1);
break;
case DECRTYPE:
/*
* dodecr - decrement the value of the argument
*
*/
if (argc > 2)
pbnum(atoi(argv[2]) - 1);
break;
#if unix || vms
case SYSCTYPE:
/*
* dosys - execute system command
*
*/
if (argc > 2)
sysval = system(argv[2]);
break;
case SYSVTYPE:
/*
* dosysval - return value of the last system call.
*
*/
pbnum(sysval);
break;
#endif
case INCLTYPE:
if (argc > 2)
if (!doincl(argv[2])) {
fprintf(stderr,"m4: %s: ",argv[2]);
error("cannot open for read.");
}
break;
case SINCTYPE:
if (argc > 2)
(void) doincl(argv[2]);
break;
#ifdef EXTENDED
case PASTTYPE:
if (argc > 2)
if (!dopaste(argv[2])) {
fprintf(stderr,"m4: %s: ",argv[2]);
error("cannot open for read.");
}
break;
case SPASTYPE:
if (argc > 2)
(void) dopaste(argv[2]);
break;
#endif
case CHNQTYPE:
dochq(argv, argc);
break;
case CHNCTYPE:
dochc(argv, argc);
break;
case SUBSTYPE:
/*
* dosub - select substring
*
*/
if (argc > 3)
dosub(argv,argc);
break;
case SHIFTYPE:
/*
* doshift - push back all arguments except the
* first one (i.e. skip argv[2])
*/
if (argc > 3) {
for (n = argc-1; n > 3; n--) {
putback(rquote);
pbstr(argv[n]);
putback(lquote);
putback(',');
}
putback(rquote);
pbstr(argv[3]);
putback(lquote);
}
break;
case DIVRTYPE:
if (argc > 2 && (n = atoi(argv[2])) != 0)
dodiv(n);
else {
active = stdout;
oindex = 0;
}
break;
case UNDVTYPE:
doundiv(argv, argc);
break;
case DIVNTYPE:
/*
* dodivnum - return the number of current
* output diversion
*
*/
pbnum(oindex);
break;
case UNDFTYPE:
/*
* doundefine - undefine a previously defined
* macro(s) or m4 keyword(s).
*/
if (argc > 2)
for (n = 2; n < argc; n++)
remhash(argv[n], ALL);
break;
case POPDTYPE:
/*
* dopopdef - remove the topmost definitions of
* macro(s) or m4 keyword(s).
*/
if (argc > 2)
for (n = 2; n < argc; n++)
remhash(argv[n], TOP);
break;
case MKTMTYPE:
/*
* dotemp - create a temporary file
*
*/
if (argc > 2)
pbstr(mktemp(argv[2]));
break;
case TRNLTYPE:
/*
* dotranslit - replace all characters in the
* source string that appears in
* the "from" string with the corresponding
* characters in the "to" string.
*
*/
if (argc > 3) {
char temp[MAXTOK];
if (argc > 4)
map(temp, argv[2], argv[3], argv[4]);
else
map(temp, argv[2], argv[3], null);
pbstr(temp);
}
else
if (argc > 2)
pbstr(argv[2]);
break;
case INDXTYPE:
/*
* doindex - find the index of the second argument
* string in the first argument string.
* -1 if not present.
*/
pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
break;
case ERRPTYPE:
/*
* doerrp - print the arguments to stderr file
*
*/
if (argc > 2) {
for (n = 2; n < argc; n++)
fprintf(stderr,"%s ", argv[n]);
fprintf(stderr, "\n");
}
break;
case DNLNTYPE:
/*
* dodnl - eat-up-to and including newline
*
*/
while ((c = gpbc()) != '\n' && c != EOF)
;
break;
case M4WRTYPE:
/*
* dom4wrap - set up for wrap-up/wind-down activity
*
*/
m4wraps = (argc > 2) ? strsave(argv[2]) : null;
break;
case EXITTYPE:
/*
* doexit - immediate exit from m4.
*
*/
exit((argc > 2) ? atoi(argv[2]) : 0);
break;
case DEFNTYPE:
if (argc > 2)
for (n = 2; n < argc; n++)
dodefn(argv[n]);
break;
default:
error("m4: major botch in eval.");
break;
}
}
SHAR_EOF
if test 5714 -ne "`wc -c eval.c`"
then
echo shar: error transmitting eval.c '(should have been 5714 characters)'
fi
echo shar: extracting expr.c '(11535 characters)'
cat << \SHAR_EOF > expr.c
/*
* expression evaluator: performs a standard recursive
* descent parse to evaluate any expression permissible
* within the following grammar:
*
* expr : query EOS
* query : lor
* | lor "?" query ":" query
* lor : land { "||" land }
* land : bor { "&&" bor }
* bor : bxor { "|" bxor }
* bxor : band { "^" band }
* band : eql { "&" eql }
* eql : relat { eqrel relat }
* relat : shift { rel shift }
* shift : primary { shop primary }
* primary : term { addop term }
* term : unary { mulop unary }
* unary : factor
* | unop unary
* factor : constant
* | "(" query ")"
* constant: num
* | "'" CHAR "'"
* num : DIGIT
* | DIGIT num
* shop : "<<"
* | ">>"
* eqlrel : "="
* | "=="
* | "!="
* rel : "<"
* | ">"
* | "<="
* | ">="
*
*
* This expression evaluator is lifted from a public-domain
* C Pre-Processor included with the DECUS C Compiler distribution.
* It is hacked somewhat to be suitable for m4.
*
* Originally by: Mike Lutz
* Bob Harper
*/
#define TRUE 1
#define FALSE 0
#define EOS (char) 0
#define EQL 0
#define NEQ 1
#define LSS 2
#define LEQ 3
#define GTR 4
#define GEQ 5
#define OCTAL 8
#define DECIMAL 10
static char *nxtch; /* Parser scan pointer */
/*
* For longjmp
*/
#include
static jmp_buf expjump;
/*
* macros:
*
* ungetch - Put back the last character examined.
* getch - return the next character from expr string.
*/
#define ungetch() nxtch--
#define getch() *nxtch++
expr(expbuf)
char *expbuf;
{
register int rval;
nxtch = expbuf;
if (setjmp(expjump) != 0)
return (FALSE);
rval = query();
if (skipws() == EOS)
return(rval);
experr("Ill-formed expression");
}
/*
* query : lor | lor '?' query ':' query
*
*/
query()
{
register int bool, true_val, false_val;
bool = lor();
if (skipws() != '?') {
ungetch();
return(bool);
}
true_val = query();
if (skipws() != ':')
experr("Bad query");
false_val = query();
return(bool ? true_val : false_val);
}
/*
* lor : land { '||' land }
*
*/
lor()
{
register int c, vl, vr;
vl = land();
while ((c = skipws()) == '|' && getch() == '|') {
vr = land();
vl = vl || vr;
}
if (c == '|')
ungetch();
ungetch();
return(vl);
}
/*
* land : bor { '&&' bor }
*
*/
land()
{
register int c, vl, vr;
vl = bor();
while ((c = skipws()) == '&' && getch() == '&') {
vr = bor();
vl = vl && vr;
}
if (c == '&')
ungetch();
ungetch();
return(vl);
}
/*
* bor : bxor { '|' bxor }
*
*/
bor()
{
register int vl, vr, c;
vl = bxor();
while ((c = skipws()) == '|' && getch() != '|') {
ungetch();
vr = bxor();
vl |= vr;
}
if (c == '|')
ungetch();
ungetch();
return(vl);
}
/*
* bxor : band { '^' band }
*
*/
bxor()
{
register int vl, vr;
vl = band();
while (skipws() == '^') {
vr = band();
vl ^= vr;
}
ungetch();
return(vl);
}
/*
* band : eql { '&' eql }
*
*/
band()
{
register int vl, vr, c;
vl = eql();
while ((c = skipws()) == '&' && getch() != '&') {
ungetch();
vr = eql();
vl &= vr;
}
if (c == '&')
ungetch();
ungetch();
return(vl);
}
/*
* eql : relat { eqrel relat }
*
*/
eql()
{
register int vl, vr, rel;
vl = relat();
while ((rel = geteql()) != -1) {
vr = relat();
switch (rel) {
case EQL:
vl = (vl == vr);
break;
case NEQ:
vl = (vl != vr);
break;
}
}
return(vl);
}
/*
* relat : shift { rel shift }
*
*/
relat()
{
register int vl, vr, rel;
vl = shift();
while ((rel = getrel()) != -1) {
vr = shift();
switch (rel) {
case LEQ:
vl = (vl <= vr);
break;
case LSS:
vl = (vl < vr);
break;
case GTR:
vl = (vl > vr);
break;
case GEQ:
vl = (vl >= vr);
break;
}
}
return(vl);
}
/*
* shift : primary { shop primary }
*
*/
shift()
{
register int vl, vr, c;
vl = primary();
while (((c = skipws()) == '<' || c == '>') && c == getch()) {
vr = primary();
if (c == '<')
vl <<= vr;
else
vl >>= vr;
}
if (c == '<' || c == '>')
ungetch();
ungetch();
return(vl);
}
/*
* primary : term { addop term }
*
*/
primary()
{
register int c, vl, vr;
vl = term();
while ((c = skipws()) == '+' || c == '-') {
vr = term();
if (c == '+')
vl += vr;
else
vl -= vr;
}
ungetch();
return(vl);
}
/*
* := { }
*
*/
term()
{
register int c, vl, vr;
vl = unary();
while ((c = skipws()) == '*' || c == '/' || c == '%') {
vr = unary();
switch (c) {
case '*':
vl *= vr;
break;
case '/':
vl /= vr;
break;
case '%':
vl %= vr;
break;
}
}
ungetch();
return(vl);
}
/*
* unary : factor | unop unary
*
*/
unary()
{
register int val, c;
if ((c = skipws()) == '!' || c == '~' || c == '-') {
val = unary();
switch (c) {
case '!':
return(! val);
case '~':
return(~ val);
case '-':
return(- val);
}
}
ungetch();
return(factor());
}
/*
* factor : constant | '(' query ')'
*
*/
factor()
{
register int val;
if (skipws() == '(') {
val = query();
if (skipws() != ')')
experr("Bad factor");
return(val);
}
ungetch();
return(constant());
}
/*
* constant: num | 'char'
*
*/
constant()
{
/*
* Note: constant() handles multi-byte constants
*/
register int i;
register int value;
register char c;
int v[sizeof (int)];
if (skipws() != '\'') {
ungetch();
return(num());
}
for (i = 0; i < sizeof(int); i++) {
if ((c = getch()) == '\'') {
ungetch();
break;
}
if (c == '\\') {
switch (c = getch()) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
ungetch();
c = num();
break;
case 'n':
c = 012;
break;
case 'r':
c = 015;
break;
case 't':
c = 011;
break;
case 'b':
c = 010;
break;
case 'f':
c = 014;
break;
}
}
v[i] = c;
}
if (i == 0 || getch() != '\'')
experr("Illegal character constant");
for (value = 0; --i >= 0;) {
value <<= 8;
value += v[i];
}
return(value);
}
/*
* num : digit | num digit
*
*/
num()
{
register int rval, c, base;
int ndig;
base = ((c = skipws()) == '0') ? OCTAL : DECIMAL;
rval = 0;
ndig = 0;
while (c >= '0' && c <= (base == OCTAL ? '7' : '9')) {
rval *= base;
rval += (c - '0');
c = getch();
ndig++;
}
ungetch();
if (ndig)
return(rval);
experr("Bad constant");
}
/*
* eqlrel : '=' | '==' | '!='
*
*/
geteql()
{
register int c1, c2;
c1 = skipws();
c2 = getch();
switch (c1) {
case '=':
if (c2 != '=')
ungetch();
return(EQL);
case '!':
if (c2 == '=')
return(NEQ);
ungetch();
ungetch();
return(-1);
default:
ungetch();
ungetch();
return(-1);
}
}
/*
* rel : '<' | '>' | '<=' | '>='
*
*/
getrel()
{
register int c1, c2;
c1 = skipws();
c2 = getch();
switch (c1) {
case '<':
if (c2 == '=')
return(LEQ);
ungetch();
return(LSS);
case '>':
if (c2 == '=')
return(GEQ);
ungetch();
return(GTR);
default:
ungetch();
ungetch();
return(-1);
}
}
/*
* Skip over any white space and return terminating char.
*/
skipws()
{
register char c;
while ((c = getch()) <= ' ' && c > EOS)
;
return(c);
}
/*
* Error handler - resets environment to eval(), prints an error,
* and returns FALSE.
*/
int experr(msg)
char *msg;
{
printf("mp: %s\n",msg);
longjmp(expjump, -1); /* Force eval() to return FALSE */
}
SHAR_EOF
if test 11535 -ne "`wc -c expr.c`"
then
echo shar: error transmitting expr.c '(should have been 11535 characters)'
fi
echo shar: extracting getopt.c '(2825 characters)'
cat << \SHAR_EOF > getopt.c
/* getopt.h - Get next option letter from argument vector.
v1.1 12-Dec-1987 aklevin
*/
#define GETOPT_H
#ifndef _STDIO_H
#include
#endif
/* optarg points to an option's argument (if any).
optind holds the index of the next argument vector element to parse.
Once all options have been parsed, points to the first non-option argument.
[If (optind > argc) then there are no more arguments].
opterr, if set to 0 will suppress getopt's error messages (default is 1).
optopt, while not usually documented, is used here to return the actual
option character found, even when getopt itself returns '?'.
*/
char *optarg;
int optind=1, opterr=1, optopt;
int
getopt(argc, argv, optstring)
int argc;
char *argv[], *optstring;
{
int any_more, i, result;
static int opthold, optsub=1;
/* Reset optarg upon entry */
*optarg = '\0';
/* Reset optsub if caller has changed optind. */
if (optind != opthold) optsub = 1;
/* Look at each element of the argument vector still unparsed. */
for ( ; optind < argc; optind++) {
/* Done if a non-option argument or single dash is reached.
However, don't skip over said argument. */
if (argv[optind][0] != '-' || argv[optind][1] == '\0') break;
/* Got an option. */
/* Done if "--" is reached. Skip over it, too. */
if (argv[optind][1] == '-') {
optind++;
break;
}
/* Look at each character in optstring. */
for (i=0; i < strlen(optstring); i++) {
if ( (optopt = argv[optind][optsub]) != optstring[i]) continue;
/* Got a match. */
/* Are there any more chars in this option? e.g. `-abc' */
any_more = strlen(argv[optind])-optsub-1;
/* Does this option require an argument? */
if (optstring[i+1] == ':') {
/* Yes. If this is the last argument, complain. */
if (optind == argc-1 && !any_more) {
if (opterr) fprintf(stderr, "%s: `-%c' option requires an argument.\n", argv[0], optopt);
optind++;
result='?';
goto leave;
} /* end if (opt */
/* Qualifier is either rest of this argument (if any)
or next argument. */
else {
if (!any_more) optarg = argv[++optind];
else optarg = &argv[optind][optsub+1];
optind++;
optsub=1;
} /* end else */
} /* end if (opt */
else {
/* No argument; just adjust indices. */
/* Advance to next argument. */
if (!any_more) {
optind++;
optsub=1;
} /* end if (! */
/* Advance to next character. */
else optsub++;
} /* end else */
result=optopt;
goto leave;
} /* end for (i=0 */
if (opterr) fprintf(stderr, "%s: Unrecognized option `-%c'.\n", argv[0], optopt);
if (strlen(argv[optind])-optsub-1) optsub++;
else {
optind++;
optsub=1;
}
result='?';
goto leave;
} /* end for ( ; */
result=EOF;
leave:
opthold = optind;
return(result);
} /* end getopt() */
SHAR_EOF
if test 2825 -ne "`wc -c getopt.c`"
then
echo shar: error transmitting getopt.c '(should have been 2825 characters)'
fi
echo shar: extracting look.c '(1649 characters)'
cat << \SHAR_EOF > look.c
/*
* look.c
* Facility: m4 macro processor
* by: oz
*/
#include "mdef.h"
#include "extr.h"
extern char *strsave();
/*
* hash - compute hash value using the proverbial
* hashing function. Taken from K&R.
*/
hash (name)
register char *name;
{
register int h = 0;
while (*name)
h += *name++;
return (h % HASHSIZE);
}
/*
* lookup - find name in the hash table
*
*/
ndptr lookup(name)
char *name;
{
register ndptr p;
for (p = hashtab[hash(name)]; p != nil; p = p->nxtptr)
if (strcmp(name, p->name) == 0)
break;
return (p);
}
/*
* addent - hash and create an entry in the hash
* table. The new entry is added in front
* of a hash bucket.
*/
ndptr addent(name)
char *name;
{
register int h;
ndptr p;
h = hash(name);
if ((p = (ndptr) malloc(sizeof(struct ndblock))) != NULL) {
p->nxtptr = hashtab[h];
hashtab[h] = p;
p->name = strsave(name);
}
else
error("m4: no more memory.");
return p;
}
/*
* remhash - remove an entry from the hashtable
*
*/
int remhash(name, all)
char *name;
int all;
{
register int h;
register ndptr xp, tp, mp;
h = hash(name);
mp = hashtab[h];
tp = nil;
while (mp != nil) {
if (strcmp(mp->name, name) == 0) {
mp = mp->nxtptr;
if (tp == nil) {
freent(hashtab[h]);
hashtab[h] = mp;
}
else {
xp = tp->nxtptr;
tp->nxtptr = mp;
freent(xp);
}
if (!all)
break;
}
else {
tp = mp;
mp = mp->nxtptr;
}
}
}
/*
* freent - free a hashtable information block
*
*/
int freent(p)
ndptr p;
{
if (!(p->type & STATIC)) {
free((char *)p->name);
if (p->defn != null)
free((char *)p->defn);
}
free((char *)p);
}
SHAR_EOF
if test 1649 -ne "`wc -c look.c`"
then
echo shar: error transmitting look.c '(should have been 1649 characters)'
fi
echo shar: extracting main.c '(11175 characters)'
cat << \SHAR_EOF > main.c
/*
* main.c
* Facility: m4 macro processor
* by: oz
*/
#include "mdef.h"
/*
* 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
* sources.
*
* References:
*
* 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
*
* Modification History:
*
* Jan 28 1986 Oz Break the whole thing into little
* pieces, for easier (?) maintenance.
*
* Dec 12 1985 Oz Optimize the code, try to squeeze
* few microseconds out..
*
* Dec 05 1985 Oz Add getopt interface, define (-D),
* undefine (-U) options.
*
* 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.
*
* Implementation Notes:
*
* [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
* for the hash table.
*
* . .
* | . | <-- sp | . |
* +-------+ +-----+
* | arg 3 ------------------------------->| str |
* +-------+ | . |
* | arg 2 --------------+ .
* +-------+ |
* * | | |
* +-------+ | +-----+
* | plev | <-- fp +---------------->| str |
* +-------+ | . |
* | type | .
* +-------+
* | 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
*
*/
#ifdef MYMKTMP
int mytmpnum=000000; /* used in mktemp() */
#endif
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 */
"include", INCLTYPE,
"sinclude", SINCTYPE,
"define", DEFITYPE,
"defn", DEFNTYPE,
"divert", DIVRTYPE,
"expr", EXPRTYPE,
"eval", EXPRTYPE,
"substr", SUBSTYPE,
"ifelse", IFELTYPE,
"ifdef", IFDFTYPE,
"len", LENGTYPE,
"incr", INCRTYPE,
"decr", DECRTYPE,
"dnl", DNLNTYPE,
"changequote", CHNQTYPE,
"changecom", CHNCTYPE,
"index", INDXTYPE,
#ifdef EXTENDED
"paste", PASTTYPE,
"spaste", SPASTYPE,
#endif
"popdef", POPDTYPE,
"pushdef", PUSDTYPE,
"dumpdef", DUMPTYPE,
"shift", SHIFTYPE,
"translit", TRNLTYPE,
"undefine", UNDFTYPE,
"undivert", UNDVTYPE,
"divnum", DIVNTYPE,
"maketemp", MKTMTYPE,
"errprint", ERRPTYPE,
"m4wrap", M4WRTYPE,
"m4exit", EXITTYPE,
#if unix || vms
"syscmd", SYSCTYPE,
"sysval", SYSVTYPE,
#endif
#if unix
"unix", MACRTYPE,
#else
#if vms
"vms", MACRTYPE,
#endif
#endif
};
#define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk))
extern ndptr lookup();
extern ndptr addent();
extern int onintr();
extern char *malloc();
extern char *mktemp();
extern int optind;
extern char *optarg;
void main(argc,argv)
char *argv[];
{
register int c;
register int n;
char *p;
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, onintr);
#ifdef NONZEROPAGES
initm4();
#endif
initkwds();
while ((c = getopt(argc, argv, "tD:U:o:")) != EOF)
switch(c) {
case 'D': /* define something..*/
for (p = optarg; *p; p++)
if (*p == '=')
break;
if (*p)
*p++ = EOS;
dodefine(optarg, p);
break;
case 'U': /* undefine... */
remhash(optarg, TOP);
break;
case 'o': /* specific output */
case '?':
default:
usage();
}
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 */
}
else /* default wrap-up: undivert */
for (n = 1; n < MAXOUT; n++)
if (outfile[n] != NULL)
getdiv(n);
/* remove bitbucket if used */
if (outfile[0] != NULL) {
(void) fclose(outfile[0]);
m4temp[UNIQUE] = '0';
#if vms
(void) remove(m4temp);
#else
(void) unlink(m4temp);
#endif
}
exit(0);
}
ndptr inspect(); /* forward ... */
/*
* macro - the work horse..
*
*/
int macro() {
char token[MAXTOK];
register char *s;
register int t, l;
register ndptr p;
register int nlpar;
cycle {
if ((t = gpbc()) == '_' || isalpha(t)) {
putback(t);
if ((p = inspect(s = token)) == nil) {
if (sp < 0)
while (*s)
putc(*s++, active);
else
while (*s)
chrsave(*s++);
}
else {
/*
* 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..*/
putback(l = gpbc());
if (l != LPAREN) { /* add bracks */
putback(RPAREN);
putback(LPAREN);
}
}
}
else if (t == EOF) {
if (sp > -1)
error("m4: unexpected end of input");
if (--ilevel < 0)
break; /* all done thanks.. */
(void) fclose(infile[ilevel+1]);
continue;
}
/*
* non-alpha single-char token seen..
* [the order of else if .. stmts is
* important.]
*
*/
else if (t == lquote) { /* strip quotes */
nlpar = 1;
do {
if ((l = gpbc()) == rquote)
nlpar--;
else if (l == lquote)
nlpar++;
else if (l == EOF)
error("m4: missing right quote");
if (nlpar > 0) {
if (sp < 0)
putc(l, active);
else
chrsave(l);
}
}
while (nlpar != 0);
}
else if (sp < 0) { /* not in a macro at all */
if (t == scommt) { /* comment handling here */
putc(t, active);
while ((t = gpbc()) != ecommt)
putc(t, active);
}
putc(t, active); /* output directly.. */
}
else switch(t) {
case LPAREN:
if (PARLEV > 0)
chrsave(t);
while (isspace(l = gpbc()))
; /* skip blank, tab, nl.. */
putback(l);
PARLEV++;
break;
case RPAREN:
if (--PARLEV > 0)
chrsave(t);
else { /* end of argument list */
chrsave(EOS);
if (sp == STACKMAX)
error("m4: internal stack overflow");
if (CALTYP == MACRTYPE)
expand(mstack+fp+1, sp-fp);
else
eval(mstack+fp+1, sp-fp, CALTYP);
ep = PREVEP; /* flush strspace */
sp = PREVSP; /* previous sp.. */
fp = PREVFP; /* rewind stack...*/
}
break;
case COMMA:
if (PARLEV == 1) {
chrsave(EOS); /* new argument */
while (isspace(l = gpbc()))
;
putback(l);
pushs(ep);
}
break;
default:
chrsave(t); /* stack the char */
break;
}
}
}
/*
* build an input token..
* consider only those starting with _ or A-Za-z. This is a
* combo with lookup to speed things up.
*/
ndptr
inspect(tp)
register char *tp;
{
register int h = 0;
register char c;
register char *name = tp;
register char *etp = tp+MAXTOK;
register ndptr p;
while (tp < etp && (isalnum(c = gpbc()) || c == '_'))
h += (*tp++ = c);
putback(c);
if (tp == etp)
error("m4: token too long");
*tp = EOS;
for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
if (strcmp(name, p->name) == 0)
break;
return(p);
}
#ifdef NONZEROPAGES
/*
* initm4 - initialize various tables. Useful only if your system
* does not know anything about demand-zero pages.
*
*/
initm4()
{
register int i;
for (i = 0; i < HASHSIZE; i++)
hashtab[i] = nil;
for (i = 0; i < MAXOUT; i++)
outfile[i] = NULL;
}
#endif
/*
* 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).
*
*/
int initkwds() {
register int i;
register int h;
register ndptr p;
for (i = 0; i < MAXKEYS; i++) {
h = hash(keywrds[i].knam);
p = (ndptr) malloc(sizeof(struct ndblock));
p->nxtptr = hashtab[h];
hashtab[h] = p;
p->name = keywrds[i].knam;
p->defn = null;
p->type = keywrds[i].ktyp | STATIC;
}
}
SHAR_EOF
if test 11175 -ne "`wc -c main.c`"
then
echo shar: error transmitting main.c '(should have been 11175 characters)'
fi
echo shar: extracting mktemp.c '(513 characters)'
cat << \SHAR_EOF > mktemp.c
#include
/* mktemp(0 shamelessly swiped off our vax, didn't find */
/* a (c) anywhere on this piece of code.. */
char *
mktemp(as)
char *as;
{
register char *s;
register int tmp;
register i;
extern int mytmpnum;
loop: tmp = mytmpnum%100000;
s = as;
while (*s++)
;
s--;
while (*--s == 'X') {
*s = (tmp%10) + '0';
tmp /= 10;
}
s++;
i = 'a';
while (access(as, 0) != -1) {
if (i=='z')
{
mytmpnum+=1;
goto loop;
}
*s = i++;
}
mytmpnum+=1;
return(as);
}
SHAR_EOF
if test 513 -ne "`wc -c mktemp.c`"
then
echo shar: error transmitting mktemp.c '(should have been 513 characters)'
fi
# End of shell archive
exit 0