rsalz@bbn.com (Rich Salz) (02/13/88)
Submitted-by: Ozan Yigit <yetti!oz> Posting-number: Volume 13, Issue 38 Archive-name: m4/part01 #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # makefile # mdef.h # extr.h # main.c # eval.c # serv.c # look.c # misc.c # expr.c export PATH; PATH=/bin:$PATH echo shar: extracting "'makefile'" '(1372 characters)' if test -f 'makefile' then echo shar: will not over-write existing file "'makefile'" else sed 's/^ X//' << \SHAR_EOF > 'makefile' X# X# pd m4 [oz] X# X# -DEXTENDED X# if you like to get paste & spaste macros. X# -DVOID X# if your C compiler does NOT support void. X# -DGETOPT X# if you STILL do not have getopt in your library. X# [This means your library is broken. Fix it.] X# -DDUFFCP X# if you do not have fast memcpy in your library. X# XCFLAGS = -O -DEXTENDED XDEST = /usr/local/bin XMANL = /usr/man/manl XOBJS = main.o eval.o serv.o look.o misc.o expr.o XCSRC = main.c eval.c serv.c look.c misc.c expr.c XINCL = mdef.h extr.h XMSRC = ack.m4 hanoi.m4 hash.m4 sqroot.m4 string.m4 test.m4 XDOCS = README MANIFEST m4.1 X XMBIN = /usr/bin X Xm4: ${OBJS} X @echo "loading m4.." X @cc -s -o m4 ${OBJS} X @size m4 X X${OBJS}: ${INCL} X Xlint: X lint -h ${CSRC} X Xinstall: m4 X install ./m4 ${DEST}/m4 X cp ./m4.1 ${MANL}/m4.l X Xdeinstall: X rm -f ${DEST}/m4 X rm -f ${MANL}/m4.l Xtime: m4 X @echo "timing comparisons.." X @echo "un*x m4:" X time ${MBIN}/m4 <test.m4 >unxm4.out X @echo "pd m4:" X time ./m4 <test.m4 >pdm4.out X @echo "un*x m4:" X time ${MBIN}/m4 <test.m4 >unxm4.out X @echo "pd m4:" X time ./m4 <test.m4 >pdm4.out X @echo "un*x m4:" X time ${MBIN}/m4 <test.m4 >unxm4.out X @echo "pd m4:" X time ./m4 <test.m4 >pdm4.out X @echo "output comparisons.." X -diff pdm4.out unxm4.out X @rm -f pdm4.out unxm4.out Xclean: X rm -f *.o core m4 *.out Xpack: X shar -a makefile ${INCL} ${CSRC} >M4MAIN.SHAR X shar -a ${MSRC} ${DOCS} >M4MSRC.SHAR SHAR_EOF if test 1372 -ne "`wc -c < 'makefile'`" then echo shar: error transmitting "'makefile'" '(should have been 1372 characters)' fi fi # end of overwriting check echo shar: extracting "'mdef.h'" '(4711 characters)' if test -f 'mdef.h' then echo shar: will not over-write existing file "'mdef.h'" else sed 's/^ X//' << \SHAR_EOF > 'mdef.h' X/* X * mdef.h X * Facility: m4 macro processor X * by: oz X */ X X X#ifndef unix X#define unix 0 X#endif X X#ifndef vms X#define vms 0 X#endif X X#if vms X X#include stdio X#include ctype X#include signal X X#else X X#include <stdio.h> X#include <ctype.h> X#include <signal.h> X X#endif X X/* X * X * m4 constants.. X * X */ X X#define MACRTYPE 1 X#define DEFITYPE 2 X#define EXPRTYPE 3 X#define SUBSTYPE 4 X#define IFELTYPE 5 X#define LENGTYPE 6 X#define CHNQTYPE 7 X#define SYSCTYPE 8 X#define UNDFTYPE 9 X#define INCLTYPE 10 X#define SINCTYPE 11 X#define PASTTYPE 12 X#define SPASTYPE 13 X#define INCRTYPE 14 X#define IFDFTYPE 15 X#define PUSDTYPE 16 X#define POPDTYPE 17 X#define SHIFTYPE 18 X#define DECRTYPE 19 X#define DIVRTYPE 20 X#define UNDVTYPE 21 X#define DIVNTYPE 22 X#define MKTMTYPE 23 X#define ERRPTYPE 24 X#define M4WRTYPE 25 X#define TRNLTYPE 26 X#define DNLNTYPE 27 X#define DUMPTYPE 28 X#define CHNCTYPE 29 X#define INDXTYPE 30 X#define SYSVTYPE 31 X#define EXITTYPE 32 X#define DEFNTYPE 33 X X#define STATIC 128 X X/* X * m4 special characters X */ X X#define ARGFLAG '$' X#define LPAREN '(' X#define RPAREN ')' X#define LQUOTE '`' X#define RQUOTE '\'' X#define COMMA ',' X#define SCOMMT '#' X#define ECOMMT '\n' X X/* X * definitions of diversion files. If the name of X * the file is changed, adjust UNIQUE to point to the X * wildcard (*) character in the filename. X */ X X#if unix X#define DIVNAM "/tmp/m4*XXXXXX" /* unix diversion files */ X#define UNIQUE 7 /* unique char location */ X#else X#if vms X#define DIVNAM "sys$login:m4*XXXXXX" /* vms diversion files */ X#define UNIQUE 12 /* unique char location */ X#else X#define DIVNAM "\M4*XXXXXX" /* msdos diversion files */ X#define UNIQUE 3 /* unique char location */ X#endif X#endif X X/* X * other important constants X */ X X#define EOS (char) 0 X#define MAXINP 10 /* maximum include files */ X#define MAXOUT 10 /* maximum # of diversions */ X#define MAXSTR 512 /* maximum size of string */ X#define BUFSIZE 4096 /* size of pushback buffer */ X#define STACKMAX 1024 /* size of call stack */ X#define STRSPMAX 4096 /* size of string space */ X#define MAXTOK MAXSTR /* maximum chars in a tokn */ X#define HASHSIZE 199 /* maximum size of hashtab */ X X#define ALL 1 X#define TOP 0 X X#define TRUE 1 X#define FALSE 0 X#define cycle for(;;) X X#ifdef VOID X#define void int /* define if void is void. */ X#endif X X/* X * m4 data structures X */ X Xtypedef struct ndblock *ndptr; X Xstruct ndblock { /* hastable structure */ X char *name; /* entry name.. */ X char *defn; /* definition.. */ X int type; /* type of the entry.. */ X ndptr nxtptr; /* link to next entry.. */ X}; X X#define nil ((ndptr) 0) X Xstruct keyblk { X char *knam; /* keyword name */ X int ktyp; /* keyword type */ X}; X Xtypedef union { /* stack structure */ X int sfra; /* frame entry */ X char *sstr; /* string entry */ X} stae; X X/* X * macros for readibility and/or speed X * X * gpbc() - get a possibly pushed-back character X * min() - select the minimum of two elements X * pushf() - push a call frame entry onto stack X * pushs() - push a string pointer onto stack X */ X#define gpbc() (bp > buf) ? *--bp : getc(infile[ilevel]) X#define min(x,y) ((x > y) ? y : x) X#define pushf(x) if (sp < STACKMAX) mstack[++sp].sfra = (x) X#define pushs(x) if (sp < STACKMAX) mstack[++sp].sstr = (x) X X/* X * . . X * | . | <-- sp | . | X * +-------+ +-----+ X * | arg 3 ----------------------->| str | X * +-------+ | . | X * | arg 2 ---PREVEP-----+ . X * +-------+ | X * . | | | X * +-------+ | +-----+ X * | plev | PARLEV +-------->| str | X * +-------+ | . | X * | type | CALTYP . X * +-------+ X * | prcf ---PREVFP--+ X * +-------+ | X * | . | PREVSP | X * . | X * +-------+ | X * | <----------+ X * +-------+ X * X */ X#define PARLEV (mstack[fp].sfra) X#define CALTYP (mstack[fp-1].sfra) X#define PREVEP (mstack[fp+3].sstr) X#define PREVSP (fp-3) X#define PREVFP (mstack[fp-2].sfra) SHAR_EOF if test 4711 -ne "`wc -c < 'mdef.h'`" then echo shar: error transmitting "'mdef.h'" '(should have been 4711 characters)' fi fi # end of overwriting check echo shar: extracting "'extr.h'" '(1136 characters)' if test -f 'extr.h' then echo shar: will not over-write existing file "'extr.h'" else sed 's/^ X//' << \SHAR_EOF > 'extr.h' Xextern ndptr hashtab[]; /* hash table for macros etc. */ Xextern char buf[]; /* push-back buffer */ Xextern char *bp; /* first available character */ Xextern char *endpbb; /* end of push-back buffer */ Xextern stae mstack[]; /* stack of m4 machine */ Xextern char *ep; /* first free char in strspace */ Xextern char *endest; /* end of string space */ Xint sp; /* current m4 stack pointer */ Xint fp; /* m4 call frame pointer */ Xextern FILE *infile[]; /* input file stack (0=stdin) */ Xextern FILE *outfile[]; /* diversion array(0=bitbucket)*/ Xextern FILE *active; /* active output file pointer */ Xextern char *m4temp; /* filename for diversions */ Xextern int ilevel; /* input file stack pointer */ Xextern int oindex; /* diversion index.. */ Xextern char *null; /* as it says.. just a null.. */ Xextern char *m4wraps; /* m4wrap string default.. */ Xextern char lquote; /* left quote character (`) */ Xextern char rquote; /* right quote character (') */ Xextern char scommt; /* start character for comment */ Xextern char ecommt; /* end character for comment */ SHAR_EOF if test 1136 -ne "`wc -c < 'extr.h'`" then echo shar: error transmitting "'extr.h'" '(should have been 1136 characters)' fi fi # end of overwriting check echo shar: extracting "'main.c'" if test -f 'main.c' then echo shar: will not over-write existing file "'main.c'" else 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 * */ 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; 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.. * */ 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) 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). * */ 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 fi # end of overwriting check echo shar: extracting "'eval.c'" '(5707 characters)' if test -f 'eval.c' then echo shar: will not over-write existing file "'eval.c'" else sed 's/^ X//' << \SHAR_EOF > 'eval.c' X/* X * eval.c X * Facility: m4 macro processor X * by: oz X */ X X#include "mdef.h" X#include "extr.h" X Xextern ndptr lookup(); Xextern char *strsave(); Xextern char *mktemp(); X X/* X * eval - evaluate built-in macros. X * argc - number of elements in argv. X * argv - element vector : X * argv[0] = definition of a user X * macro or nil if built-in. X * argv[1] = name of the macro or X * built-in. X * argv[2] = parameters to user-defined X * . macro or built-in. X * . X * X * Note that the minimum value for argc is 3. A call in the form X * of macro-or-builtin() will result in: X * argv[0] = nullstr X * argv[1] = macro-or-builtin X * argv[2] = nullstr X * X */ X Xeval (argv, argc, td) Xregister char *argv[]; Xregister int argc; Xregister int td; X{ X register int c, n; X static int sysval; X X#ifdef DEBUG X printf("argc = %d\n", argc); X for (n = 0; n < argc; n++) X printf("argv[%d] = %s\n", n, argv[n]); X#endif X /* X * if argc == 3 and argv[2] is null, X * then we have macro-or-builtin() type call. X * We adjust argc to avoid further checking.. X * X */ X if (argc == 3 && !*(argv[2])) X argc--; X X switch (td & ~STATIC) { X X case DEFITYPE: X if (argc > 2) X dodefine(argv[2], (argc > 3) ? argv[3] : null); X break; X X case PUSDTYPE: X if (argc > 2) X dopushdef(argv[2], (argc > 3) ? argv[3] : null); X break; X X case DUMPTYPE: X dodump(argv, argc); X break; X X case EXPRTYPE: X /* X * doexpr - evaluate arithmetic expression X * X */ X if (argc > 2) X pbnum(expr(argv[2])); X break; X X case IFELTYPE: X if (argc > 4) X doifelse(argv, argc); X break; X X case IFDFTYPE: X /* X * doifdef - select one of two alternatives based X * on the existence of another definition X */ X if (argc > 3) { X if (lookup(argv[2]) != nil) X pbstr(argv[3]); X else if (argc > 4) X pbstr(argv[4]); X } X break; X X case LENGTYPE: X /* X * dolen - find the length of the argument X * X */ X if (argc > 2) X pbnum((argc > 2) ? strlen(argv[2]) : 0); X break; X X case INCRTYPE: X /* X * doincr - increment the value of the argument X * X */ X if (argc > 2) X pbnum(atoi(argv[2]) + 1); X break; X X case DECRTYPE: X /* X * dodecr - decrement the value of the argument X * X */ X if (argc > 2) X pbnum(atoi(argv[2]) - 1); X break; X X#if unix || vms X X case SYSCTYPE: X /* X * dosys - execute system command X * X */ X if (argc > 2) X sysval = system(argv[2]); X break; X X case SYSVTYPE: X /* X * dosysval - return value of the last system call. X * X */ X pbnum(sysval); X break; X#endif X X case INCLTYPE: X if (argc > 2) X if (!doincl(argv[2])) { X fprintf(stderr,"m4: %s: ",argv[2]); X error("cannot open for read."); X } X break; X X case SINCTYPE: X if (argc > 2) X (void) doincl(argv[2]); X break; X#ifdef EXTENDED X case PASTTYPE: X if (argc > 2) X if (!dopaste(argv[2])) { X fprintf(stderr,"m4: %s: ",argv[2]); X error("cannot open for read."); X } X break; X X case SPASTYPE: X if (argc > 2) X (void) dopaste(argv[2]); X break; X#endif X case CHNQTYPE: X dochq(argv, argc); X break; X X case CHNCTYPE: X dochc(argv, argc); X break; X X case SUBSTYPE: X /* X * dosub - select substring X * X */ X if (argc > 3) X dosub(argv,argc); X break; X X case SHIFTYPE: X /* X * doshift - push back all arguments except the X * first one (i.e. skip argv[2]) X */ X if (argc > 3) { X for (n = argc-1; n > 3; n--) { X putback(rquote); X pbstr(argv[n]); X putback(lquote); X putback(','); X } X putback(rquote); X pbstr(argv[3]); X putback(lquote); X } X break; X X case DIVRTYPE: X if (argc > 2 && (n = atoi(argv[2])) != 0) X dodiv(n); X else { X active = stdout; X oindex = 0; X } X break; X X case UNDVTYPE: X doundiv(argv, argc); X break; X X case DIVNTYPE: X /* X * dodivnum - return the number of current X * output diversion X * X */ X pbnum(oindex); X break; X X case UNDFTYPE: X /* X * doundefine - undefine a previously defined X * macro(s) or m4 keyword(s). X */ X if (argc > 2) X for (n = 2; n < argc; n++) X remhash(argv[n], ALL); X break; X X case POPDTYPE: X /* X * dopopdef - remove the topmost definitions of X * macro(s) or m4 keyword(s). X */ X if (argc > 2) X for (n = 2; n < argc; n++) X remhash(argv[n], TOP); X break; X X case MKTMTYPE: X /* X * dotemp - create a temporary file X * X */ X if (argc > 2) X pbstr(mktemp(argv[2])); X break; X X case TRNLTYPE: X /* X * dotranslit - replace all characters in the X * source string that appears in X * the "from" string with the corresponding X * characters in the "to" string. X * X */ X if (argc > 3) { X char temp[MAXTOK]; X if (argc > 4) X map(temp, argv[2], argv[3], argv[4]); X else X map(temp, argv[2], argv[3], null); X pbstr(temp); X } X else X if (argc > 2) X pbstr(argv[2]); X break; X X case INDXTYPE: X /* X * doindex - find the index of the second argument X * string in the first argument string. X * -1 if not present. X */ X pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); X break; X X case ERRPTYPE: X /* X * doerrp - print the arguments to stderr file X * X */ X if (argc > 2) { X for (n = 2; n < argc; n++) X fprintf(stderr,"%s ", argv[n]); X fprintf(stderr, "\n"); X } X break; X X case DNLNTYPE: X /* X * dodnl - eat-up-to and including newline X * X */ X while ((c = gpbc()) != '\n' && c != EOF) X ; X break; X X case M4WRTYPE: X /* X * dom4wrap - set up for wrap-up/wind-down activity X * X */ X m4wraps = (argc > 2) ? strsave(argv[2]) : null; X break; X X case EXITTYPE: X /* X * doexit - immediate exit from m4. X * X */ X exit((argc > 2) ? atoi(argv[2]) : 0); X break; X X case DEFNTYPE: X if (argc > 2) X for (n = 2; n < argc; n++) X dodefn(argv[n]); X break; X X default: X error("m4: major botch in eval."); X break; X } X} SHAR_EOF if test 5707 -ne "`wc -c < 'eval.c'`" then echo shar: error transmitting "'eval.c'" '(should have been 5707 characters)' fi fi # end of overwriting check echo shar: extracting "'serv.c'" '(11554 characters)' if test -f 'serv.c' then echo shar: will not over-write existing file "'serv.c'" else sed 's/^ X//' << \SHAR_EOF > 'serv.c' X/* X * serv.c X * Facility: m4 macro processor X * by: oz X */ X X#include "mdef.h" X#include "extr.h" X Xextern ndptr lookup(); Xextern ndptr addent(); Xextern char *strsave(); X Xchar *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */ X X/* X * expand - user-defined macro expansion X * X */ Xexpand(argv, argc) Xregister char *argv[]; Xregister int argc; X{ X register char *t; X register char *p; X register int n; X register int argno; X X t = argv[0]; /* defn string as a whole */ X p = t; X while (*p) X p++; X p--; /* last character of defn */ X while (p > t) { X if (*(p-1) != ARGFLAG) X putback(*p); X else { X switch (*p) { X X case '#': X pbnum(argc-2); X break; X case '0': X case '1': X case '2': X case '3': X case '4': X case '5': X case '6': X case '7': X case '8': X case '9': X if ((argno = *p - '0') < argc-1) X pbstr(argv[argno+1]); X break; X case '*': X for (n = argc - 1; n > 2; n--) { X pbstr(argv[n]); X putback(','); X } X pbstr(argv[2]); X break; X default : X putback(*p); X break; X } X p--; X } X p--; X } X if (p == t) /* do last character */ X putback(*p); X} X X/* X * dodefine - install definition in the table X * X */ Xdodefine(name, defn) Xregister char *name; Xregister char *defn; X{ X register ndptr p; X X if (!*name) X error("m4: null definition."); X if (strcmp(name, defn) == 0) X error("m4: recursive definition."); X if ((p = lookup(name)) == nil) X p = addent(name); X else if (p->defn != null) X free(p->defn); X if (!*defn) X p->defn = null; X else X p->defn = strsave(defn); X p->type = MACRTYPE; X} X X/* X * dodefn - push back a quoted definition of X * the given name. X */ X Xdodefn(name) Xchar *name; X{ X register ndptr p; X X if ((p = lookup(name)) != nil && p->defn != null) { X putback(rquote); X pbstr(p->defn); X putback(lquote); X } X} X X/* X * dopushdef - install a definition in the hash table X * without removing a previous definition. Since X * each new entry is entered in *front* of the X * hash bucket, it hides a previous definition from X * lookup. X */ Xdopushdef(name, defn) Xregister char *name; Xregister char *defn; X{ X register ndptr p; X X if (!*name) X error("m4: null definition"); X if (strcmp(name, defn) == 0) X error("m4: recursive definition."); X p = addent(name); X if (!*defn) X p->defn = null; X else X p->defn = strsave(defn); X p->type = MACRTYPE; X} X X/* X * dodumpdef - dump the specified definitions in the hash X * table to stderr. If nothing is specified, the entire X * hash table is dumped. X * X */ Xdodump(argv, argc) Xregister char *argv[]; Xregister int argc; X{ X register int n; X ndptr p; X X if (argc > 2) { X for (n = 2; n < argc; n++) X if ((p = lookup(argv[n])) != nil) X fprintf(stderr, dumpfmt, p->name, X p->defn); X } X else { X for (n = 0; n < HASHSIZE; n++) X for (p = hashtab[n]; p != nil; p = p->nxtptr) X fprintf(stderr, dumpfmt, p->name, X p->defn); X } X} X X/* X * doifelse - select one of two alternatives - loop. X * X */ Xdoifelse(argv,argc) Xregister char *argv[]; Xregister int argc; X{ X cycle { X if (strcmp(argv[2], argv[3]) == 0) X pbstr(argv[4]); X else if (argc == 6) X pbstr(argv[5]); X else if (argc > 6) { X argv += 3; X argc -= 3; X continue; X } X break; X } X} X X/* X * doinclude - include a given file. X * X */ Xdoincl(ifile) Xchar *ifile; X{ X if (ilevel+1 == MAXINP) X error("m4: too many include files."); X if ((infile[ilevel+1] = fopen(ifile, "r")) != NULL) { X ilevel++; X return (1); X } X else X return (0); X} X X#ifdef EXTENDED X/* X * dopaste - include a given file without any X * macro processing. X */ Xdopaste(pfile) Xchar *pfile; X{ X FILE *pf; X register int c; X X if ((pf = fopen(pfile, "r")) != NULL) { X while((c = getc(pf)) != EOF) X putc(c, active); X (void) fclose(pf); X return(1); X } X else X return(0); X} X#endif X X/* X * dochq - change quote characters X * X */ Xdochq(argv, argc) Xregister char *argv[]; Xregister int argc; X{ X if (argc > 2) { X if (*argv[2]) X lquote = *argv[2]; X if (argc > 3) { X if (*argv[3]) X rquote = *argv[3]; X } X else X rquote = lquote; X } X else { X lquote = LQUOTE; X rquote = RQUOTE; X } X} X X/* X * dochc - change comment characters X * X */ Xdochc(argv, argc) Xregister char *argv[]; Xregister int argc; X{ X if (argc > 2) { X if (*argv[2]) X scommt = *argv[2]; X if (argc > 3) { X if (*argv[3]) X ecommt = *argv[3]; X } X else X ecommt = ECOMMT; X } X else { X scommt = SCOMMT; X ecommt = ECOMMT; X } X} X X/* X * dodivert - divert the output to a temporary file X * X */ Xdodiv(n) Xregister int n; X{ X if (n < 0 || n >= MAXOUT) X n = 0; /* bitbucket */ X if (outfile[n] == NULL) { X m4temp[UNIQUE] = n + '0'; X if ((outfile[n] = fopen(m4temp, "w")) == NULL) X error("m4: cannot divert."); X } X oindex = n; X active = outfile[n]; X} X X/* X * doundivert - undivert a specified output, or all X * other outputs, in numerical order. X */ Xdoundiv(argv, argc) Xregister char *argv[]; Xregister int argc; X{ X register int ind; X register int n; X X if (argc > 2) { X for (ind = 2; ind < argc; ind++) { X n = atoi(argv[ind]); X if (n > 0 && n < MAXOUT && outfile[n] != NULL) X getdiv(n); X X } X } X else X for (n = 1; n < MAXOUT; n++) X if (outfile[n] != NULL) X getdiv(n); X} X X/* X * dosub - select substring X * X */ Xdosub (argv, argc) Xregister char *argv[]; Xregister int argc; X{ X register char *ap, *fc, *k; X register int nc; X X if (argc < 5) X nc = MAXTOK; X else X#ifdef EXPR X nc = expr(argv[4]); X#else X nc = atoi(argv[4]); X#endif X ap = argv[2]; /* target string */ X#ifdef EXPR X fc = ap + expr(argv[3]); /* first char */ X#else X fc = ap + atoi(argv[3]); /* first char */ X#endif X if (fc >= ap && fc < ap+strlen(ap)) X for (k = fc+min(nc,strlen(fc))-1; k >= fc; k--) X putback(*k); X} X X/* X * map: X * map every character of s1 that is specified in from X * into s3 and replace in s. (source s1 remains untouched) X * X * This is a standard implementation of map(s,from,to) function of ICON X * language. Within mapvec, we replace every character of "from" with X * the corresponding character in "to". If "to" is shorter than "from", X * than the corresponding entries are null, which means that those X * characters dissapear altogether. Furthermore, imagine X * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, X * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' X * ultimately maps to `*'. In order to achieve this effect in an efficient X * manner (i.e. without multiple passes over the destination string), we X * loop over mapvec, starting with the initial source character. if the X * character value (dch) in this location is different than the source X * character (sch), sch becomes dch, once again to index into mapvec, until X * the character value stabilizes (i.e. sch = dch, in other words X * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary X * character, it will stabilize, since mapvec[0] == 0 at all times. At the X * end, we restore mapvec* back to normal where mapvec[n] == n for X * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is X * about 5 times faster than any algorithm that makes multiple passes over X * destination string. X * X */ X Xmap(dest,src,from,to) Xregister char *dest; Xregister char *src; Xregister char *from; Xregister char *to; X{ X register char *tmp; X register char sch, dch; X static char mapvec[128] = { X 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, X 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, X 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, X 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, X 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, X 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, X 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, X 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, X 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, X 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, X 120, 121, 122, 123, 124, 125, 126, 127 X }; X X if (*src) { X tmp = from; X /* X * create a mapping between "from" and "to" X */ X while (*from) X mapvec[*from++] = (*to) ? *to++ : (char) 0; X X while (*src) { X sch = *src++; X dch = mapvec[sch]; X while (dch != sch) { X sch = dch; X dch = mapvec[sch]; X } X if (*dest = dch) X dest++; X } X /* X * restore all the changed characters X */ X while (*tmp) { X mapvec[*tmp] = *tmp; X tmp++; X } X } X *dest = (char) 0; X} SHAR_EOF if test 11554 -ne "`wc -c < 'serv.c'`" then echo shar: error transmitting "'serv.c'" '(should have been 11554 characters)' fi fi # end of overwriting check echo shar: extracting "'look.c'" '(1617 characters)' if test -f 'look.c' then echo shar: will not over-write existing file "'look.c'" else sed 's/^ X//' << \SHAR_EOF > 'look.c' X/* X * look.c X * Facility: m4 macro processor X * by: oz X */ X X#include "mdef.h" X#include "extr.h" X Xextern char *strsave(); X X/* X * hash - compute hash value using the proverbial X * hashing function. Taken from K&R. X */ Xhash (name) Xregister char *name; X{ X register int h = 0; X while (*name) X h += *name++; X return (h % HASHSIZE); X} X X/* X * lookup - find name in the hash table X * X */ Xndptr lookup(name) Xchar *name; X{ X register ndptr p; X X for (p = hashtab[hash(name)]; p != nil; p = p->nxtptr) X if (strcmp(name, p->name) == 0) X break; X return (p); X} X X/* X * addent - hash and create an entry in the hash X * table. The new entry is added in front X * of a hash bucket. X */ Xndptr addent(name) Xchar *name; X{ X register int h; X ndptr p; X X h = hash(name); X if ((p = (ndptr) malloc(sizeof(struct ndblock))) != NULL) { X p->nxtptr = hashtab[h]; X hashtab[h] = p; X p->name = strsave(name); X } X else X error("m4: no more memory."); X return p; X} X X/* X * remhash - remove an entry from the hashtable X * X */ Xremhash(name, all) Xchar *name; Xint all; X{ X register int h; X register ndptr xp, tp, mp; X X h = hash(name); X mp = hashtab[h]; X tp = nil; X while (mp != nil) { X if (strcmp(mp->name, name) == 0) { X mp = mp->nxtptr; X if (tp == nil) { X freent(hashtab[h]); X hashtab[h] = mp; X } X else { X xp = tp->nxtptr; X tp->nxtptr = mp; X freent(xp); X } X if (!all) X break; X } X else { X tp = mp; X mp = mp->nxtptr; X } X } X} X X/* X * freent - free a hashtable information block X * X */ Xfreent(p) Xndptr p; X{ X if (!(p->type & STATIC)) { X free(p->name); X if (p->defn != null) X free(p->defn); X } X free(p); X} X SHAR_EOF if test 1617 -ne "`wc -c < 'look.c'`" then echo shar: error transmitting "'look.c'" '(should have been 1617 characters)' fi fi # end of overwriting check echo shar: extracting "'misc.c'" '(5005 characters)' if test -f 'misc.c' then echo shar: will not over-write existing file "'misc.c'" else sed 's/^ X//' << \SHAR_EOF > 'misc.c' X/* X * misc.c X * Facility: m4 macro processor X * by: oz X */ X X#include "mdef.h" X#include "extr.h" X Xextern char *malloc(); X X/* X * indx - find the index of second str in the X * first str. X */ Xindx(s1, s2) Xchar *s1; Xchar *s2; X{ X register char *t; X register char *p; X register char *m; X X for (p = s1; *p; p++) { X for (t = p, m = s2; *m && *m == *t; m++, t++) X ; X if (!*m) X return(p - s1); X } X return (-1); X} X X/* X * putback - push character back onto input X * X */ Xputback (c) Xchar c; X{ X if (bp < endpbb) X *bp++ = c; X else X error("m4: too many characters pushed back"); X} X X/* X * pbstr - push string back onto input X * putback is replicated to improve X * performance. X * X */ Xpbstr(s) Xregister char *s; X{ X register char *es; X register char *zp; X X es = s; X zp = bp; X X while (*es) X es++; X es--; X while (es >= s) X if (zp < endpbb) X *zp++ = *es--; X if ((bp = zp) == endpbb) X error("m4: too many characters pushed back"); X} X X/* X * pbnum - convert number to string, push back on input. X * X */ Xpbnum (n) Xint n; X{ X register int num; X X num = (n < 0) ? -n : n; X do { X putback(num % 10 + '0'); X } X while ((num /= 10) > 0); X X if (n < 0) putback('-'); X} X X/* X * chrsave - put single char on string space X * X */ Xchrsave (c) Xchar c; X{ X/*** if (sp < 0) X putc(c, active); X else ***/ if (ep < endest) X *ep++ = c; X else X error("m4: string space overflow"); X} X X/* X * getdiv - read in a diversion file, and X * trash it. X */ Xgetdiv(ind) { X register int c; X register FILE *dfil; X X if (active == outfile[ind]) X error("m4: undivert: diversion still active."); X (void) fclose(outfile[ind]); X outfile[ind] = NULL; X m4temp[UNIQUE] = ind + '0'; X if ((dfil = fopen(m4temp, "r")) == NULL) X error("m4: cannot undivert."); X else X while((c = getc(dfil)) != EOF) X putc(c, active); X (void) fclose(dfil); X X#if vms X if (remove(m4temp)) X#else X if (unlink(m4temp) == -1) X#endif X error("m4: cannot unlink."); X} X X/* X * Very fatal error. Close all files X * and die hard. X */ Xerror(s) Xchar *s; X{ X killdiv(); X fprintf(stderr,"%s\n",s); X exit(1); X} X X/* X * Interrupt handling X */ Xstatic char *msg = "\ninterrupted."; X Xonintr() { X error(msg); X} X X/* X * killdiv - get rid of the diversion files X * X */ Xkilldiv() { X register int n; X X for (n = 0; n < MAXOUT; n++) X if (outfile[n] != NULL) { X (void) fclose (outfile[n]); X m4temp[UNIQUE] = n + '0'; X#if vms X (void) remove (m4temp); X#else X (void) unlink (m4temp); X#endif X } X} X X/* X * save a string somewhere.. X * X */ Xchar *strsave(s) Xchar *s; X{ X register int n; X char *p; X X if ((p = malloc (n = strlen(s)+1)) != NULL) X (void) memcpy(p, s, n); X return (p); X} X Xusage() { X fprintf(stderr, "Usage: m4 [-Dname[=val]] [-Uname]\n"); X exit(1); X} X X#ifdef GETOPT X/* X * H. Spencer getopt - get option letter from argv X * X * X#include <stdio.h> X * X */ X Xchar *optarg; /* Global argument pointer. */ Xint optind = 0; /* Global argv index. */ X Xstatic char *scan = NULL; /* Private scan pointer. */ X Xextern char *index(); X Xint Xgetopt(argc, argv, optstring) Xint argc; Xchar *argv[]; Xchar *optstring; X{ X register char c; X register char *place; X X optarg = NULL; X X if (scan == NULL || *scan == '\0') { X if (optind == 0) X optind++; X X if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') X return(EOF); X if (strcmp(argv[optind], "--")==0) { X optind++; X return(EOF); X } X X scan = argv[optind]+1; X optind++; X } X X c = *scan++; X place = index(optstring, c); X X if (place == NULL || c == ':') { X fprintf(stderr, "%s: unknown option -%c\n", argv[0], c); X return('?'); X } X X place++; X if (*place == ':') { X if (*scan != '\0') { X optarg = scan; X scan = NULL; X } else { X optarg = argv[optind]; X optind++; X } X } X X return(c); X} X X#endif X X#ifdef DUFFCP X/* X * This code uses Duff's Device (tm Tom Duff) X * to unroll the copying loop: X * while (count-- > 0) X * *to++ = *from++; X */ X X#define COPYBYTE *to++ = *from++ X Xmemcpy(to, from, count) Xregister char *from, *to; Xregister int count; X{ X if (count > 0) { X register int loops = (count+8-1) >> 3; /* div 8 round up */ X X switch (count&(8-1)) { /* mod 8 */ X case 0: do { X COPYBYTE; X case 7: COPYBYTE; X case 6: COPYBYTE; X case 5: COPYBYTE; X case 4: COPYBYTE; X case 3: COPYBYTE; X case 2: COPYBYTE; X case 1: COPYBYTE; X } while (--loops > 0); X } X X } X} X X#endif SHAR_EOF if test 5005 -ne "`wc -c < 'misc.c'`" then echo shar: error transmitting "'misc.c'" '(should have been 5005 characters)' fi fi # end of overwriting check echo shar: extracting "'expr.c'" '(11531 characters)' if test -f 'expr.c' then echo shar: will not over-write existing file "'expr.c'" else sed 's/^ X//' << \SHAR_EOF > 'expr.c' X X/* X * expression evaluator: performs a standard recursive X * descent parse to evaluate any expression permissible X * within the following grammar: X * X * expr : query EOS X * query : lor X * | lor "?" query ":" query X * lor : land { "||" land } X * land : bor { "&&" bor } X * bor : bxor { "|" bxor } X * bxor : band { "^" band } X * band : eql { "&" eql } X * eql : relat { eqrel relat } X * relat : shift { rel shift } X * shift : primary { shop primary } X * primary : term { addop term } X * term : unary { mulop unary } X * unary : factor X * | unop unary X * factor : constant X * | "(" query ")" X * constant: num X * | "'" CHAR "'" X * num : DIGIT X * | DIGIT num X * shop : "<<" X * | ">>" X * eqlrel : "=" X * | "==" X * | "!=" X * rel : "<" X * | ">" X * | "<=" X * | ">=" X * X * X * This expression evaluator is lifted from a public-domain X * C Pre-Processor included with the DECUS C Compiler distribution. X * It is hacked somewhat to be suitable for m4. X * X * Originally by: Mike Lutz X * Bob Harper X */ X X#define TRUE 1 X#define FALSE 0 X#define EOS (char) 0 X#define EQL 0 X#define NEQ 1 X#define LSS 2 X#define LEQ 3 X#define GTR 4 X#define GEQ 5 X#define OCTAL 8 X#define DECIMAL 10 X Xstatic char *nxtch; /* Parser scan pointer */ X X/* X * For longjmp X */ X#include <setjmp.h> Xstatic jmp_buf expjump; X X/* X * macros: X * X * ungetch - Put back the last character examined. X * getch - return the next character from expr string. X */ X#define ungetch() nxtch-- X#define getch() *nxtch++ X Xexpr(expbuf) Xchar *expbuf; X{ X register int rval; X X nxtch = expbuf; X if (setjmp(expjump) != 0) X return (FALSE); X rval = query(); X if (skipws() == EOS) X return(rval); X experr("Ill-formed expression"); X} X X/* X * query : lor | lor '?' query ':' query X * X */ Xquery() X{ X register int bool, true_val, false_val; X X bool = lor(); X if (skipws() != '?') { X ungetch(); X return(bool); X } X X true_val = query(); X if (skipws() != ':') X experr("Bad query"); X X false_val = query(); X return(bool ? true_val : false_val); X} X X/* X * lor : land { '||' land } X * X */ Xlor() X{ X register int c, vl, vr; X X vl = land(); X while ((c = skipws()) == '|' && getch() == '|') { X vr = land(); X vl = vl || vr; X } X X if (c == '|') X ungetch(); X ungetch(); X return(vl); X} X X/* X * land : bor { '&&' bor } X * X */ Xland() X{ X register int c, vl, vr; X X vl = bor(); X while ((c = skipws()) == '&' && getch() == '&') { X vr = bor(); X vl = vl && vr; X } X X if (c == '&') X ungetch(); X ungetch(); X return(vl); X} X X/* X * bor : bxor { '|' bxor } X * X */ Xbor() X{ X register int vl, vr, c; X X vl = bxor(); X while ((c = skipws()) == '|' && getch() != '|') { X ungetch(); X vr = bxor(); X vl |= vr; X } X X if (c == '|') X ungetch(); X ungetch(); X return(vl); X} X X/* X * bxor : band { '^' band } X * X */ Xbxor() X{ X register int vl, vr; X X vl = band(); X while (skipws() == '^') { X vr = band(); X vl ^= vr; X } X X ungetch(); X return(vl); X} X X/* X * band : eql { '&' eql } X * X */ Xband() X{ X register int vl, vr, c; X X vl = eql(); X while ((c = skipws()) == '&' && getch() != '&') { X ungetch(); X vr = eql(); X vl &= vr; X } X X if (c == '&') X ungetch(); X ungetch(); X return(vl); X} X X/* X * eql : relat { eqrel relat } X * X */ Xeql() X{ X register int vl, vr, rel; X X vl = relat(); X while ((rel = geteql()) != -1) { X vr = relat(); X X switch (rel) { X X case EQL: X vl = (vl == vr); X break; X case NEQ: X vl = (vl != vr); X break; X } X } X return(vl); X} X X/* X * relat : shift { rel shift } X * X */ Xrelat() X{ X register int vl, vr, rel; X X vl = shift(); X while ((rel = getrel()) != -1) { X X vr = shift(); X switch (rel) { X X case LEQ: X vl = (vl <= vr); X break; X case LSS: X vl = (vl < vr); X break; X case GTR: X vl = (vl > vr); X break; X case GEQ: X vl = (vl >= vr); X break; X } X } X return(vl); X} X X/* X * shift : primary { shop primary } X * X */ Xshift() X{ X register int vl, vr, c; X X vl = primary(); X while (((c = skipws()) == '<' || c == '>') && c == getch()) { X vr = primary(); X X if (c == '<') X vl <<= vr; X else X vl >>= vr; X } X X if (c == '<' || c == '>') X ungetch(); X ungetch(); X return(vl); X} X X/* X * primary : term { addop term } X * X */ Xprimary() X{ X register int c, vl, vr; X X vl = term(); X while ((c = skipws()) == '+' || c == '-') { X vr = term(); X if (c == '+') X vl += vr; X else X vl -= vr; X } X X ungetch(); X return(vl); X} X X/* X * <term> := <unary> { <mulop> <unary> } X * X */ Xterm() X{ X register int c, vl, vr; X X vl = unary(); X while ((c = skipws()) == '*' || c == '/' || c == '%') { X vr = unary(); X X switch (c) { X case '*': X vl *= vr; X break; X case '/': X vl /= vr; X break; X case '%': X vl %= vr; X break; X } X } X ungetch(); X return(vl); X} X X/* X * unary : factor | unop unary X * X */ Xunary() X{ X register int val, c; X X if ((c = skipws()) == '!' || c == '~' || c == '-') { X val = unary(); X X switch (c) { X case '!': X return(! val); X case '~': X return(~ val); X case '-': X return(- val); X } X } X X ungetch(); X return(factor()); X} X X/* X * factor : constant | '(' query ')' X * X */ Xfactor() X{ X register int val; X X if (skipws() == '(') { X val = query(); X if (skipws() != ')') X experr("Bad factor"); X return(val); X } X X ungetch(); X return(constant()); X} X X/* X * constant: num | 'char' X * X */ Xconstant() X{ X /* X * Note: constant() handles multi-byte constants X */ X X register int i; X register int value; X register char c; X int v[sizeof (int)]; X X if (skipws() != '\'') { X ungetch(); X return(num()); X } X for (i = 0; i < sizeof(int); i++) { X if ((c = getch()) == '\'') { X ungetch(); X break; X } X if (c == '\\') { X switch (c = getch()) { X case '0': X case '1': X case '2': X case '3': X case '4': X case '5': X case '6': X case '7': X ungetch(); X c = num(); X break; X case 'n': X c = 012; X break; X case 'r': X c = 015; X break; X case 't': X c = 011; X break; X case 'b': X c = 010; X break; X case 'f': X c = 014; X break; X } X } X v[i] = c; X } X if (i == 0 || getch() != '\'') X experr("Illegal character constant"); X for (value = 0; --i >= 0;) { X value <<= 8; X value += v[i]; X } X return(value); X} X X/* X * num : digit | num digit X * X */ Xnum() X{ X register int rval, c, base; X int ndig; X X base = ((c = skipws()) == '0') ? OCTAL : DECIMAL; X rval = 0; X ndig = 0; X while (c >= '0' && c <= (base == OCTAL ? '7' : '9')) { X rval *= base; X rval += (c - '0'); X c = getch(); X ndig++; X } X ungetch(); X if (ndig) X return(rval); X experr("Bad constant"); X} X X/* X * eqlrel : '=' | '==' | '!=' X * X */ Xgeteql() X{ X register int c1, c2; X X c1 = skipws(); X c2 = getch(); X X switch (c1) { X X case '=': X if (c2 != '=') X ungetch(); X return(EQL); X X case '!': X if (c2 == '=') X return(NEQ); X ungetch(); X ungetch(); X return(-1); X X default: X ungetch(); X ungetch(); X return(-1); X } X} X X/* X * rel : '<' | '>' | '<=' | '>=' X * X */ Xgetrel() X{ X register int c1, c2; X X c1 = skipws(); X c2 = getch(); X X switch (c1) { X X case '<': X if (c2 == '=') X return(LEQ); X ungetch(); X return(LSS); X X case '>': X if (c2 == '=') X return(GEQ); X ungetch(); X return(GTR); X X default: X ungetch(); X ungetch(); X return(-1); X } X} X X/* X * Skip over any white space and return terminating char. X */ Xskipws() X{ X register char c; X X while ((c = getch()) <= ' ' && c > EOS) X ; X return(c); X} X X/* X * Error handler - resets environment to eval(), prints an error, X * and returns FALSE. X */ Xexperr(msg) Xchar *msg; X{ X printf("mp: %s\n",msg); X longjmp(expjump, -1); /* Force eval() to return FALSE */ X} SHAR_EOF if test 11531 -ne "`wc -c < 'expr.c'`" then echo shar: error transmitting "'expr.c'" '(should have been 11531 characters)' fi fi # end of overwriting check # End of shell archive exit 0 -- You see things, and you say "WHY?" Usenet: [decvax|ihnp4]!utzoo!yetti!oz But I dream things that never were; ......!seismo!mnetor!yetti!oz and say "WHY NOT?" Bitnet: oz@[yusol|yulibra|yuyetti] [Back To Methuselah] Bernard Shaw Phonet: [416] 736-5257 x 3976 -- For comp.sources.unix stuff, mail to sources@uunet.uu.net.