From 5723312a6ea6f24139f7822b94d2efe74b1b8341 Mon Sep 17 00:00:00 2001 From: Oliver Lehmann Date: Mon, 11 Dec 1989 09:30:02 +0000 Subject: [PATCH] ING_DB --- WEGA/contrib/ingres/dbs/src/monitor/mac.c | 1857 +++++++++++++++++++++ 1 file changed, 1857 insertions(+) create mode 100644 WEGA/contrib/ingres/dbs/src/monitor/mac.c diff --git a/WEGA/contrib/ingres/dbs/src/monitor/mac.c b/WEGA/contrib/ingres/dbs/src/monitor/mac.c new file mode 100644 index 0000000..b4713f1 --- /dev/null +++ b/WEGA/contrib/ingres/dbs/src/monitor/mac.c @@ -0,0 +1,1857 @@ +# include +# include "gen.h" +# include "../h/dbs.h" +# include "buf.h" /* headers for buffer manip */ + +extern char *AA_iocv(); +extern char *macplkup(); + +# define ANYDELIM '\020' /* \| -- zero or more delims */ +# define ONEDELIM '\021' /* \^ -- exactly one delim */ +# define CHANGE '\022' /* \& -- token change */ + +# define PARAMN '\023' /* $ -- non-preprocessed param */ +# define PARAMP '\024' /* $$ -- preprocessed param */ + +# define PRESCANENABLE '@' /* character to enable prescan */ +# define LBRACE '{' /* left brace */ +# define RBRACE '}' /* right brace */ +# define BACKSLASH '\\' /* backslash */ +# define LQUOTE '`' /* left quote */ +# define RQUOTE '\'' /* right quote */ +# define SPACE ' ' +# define TAB '\t' +# define NEWLINE '\n' + +# define QUOTED 0200 /* pass right through bit */ +# define CHARMASK 0177 /* character part */ + +# define ITERTHRESH 100 /* iteration limit */ +# define NPRIMS (sizeof Macprims / sizeof Macprims[0]) + +/* token modes, used to compute token changes */ +# define NONE 0 /* guarantees a token change */ +# define ID 1 /* identifier */ +# define NUMBER 2 /* number (int or float) */ +# define DELIM 3 /* delimiter, guarantees a token change */ +# define QUOTEMODE 4 /* quoted construct */ +# define OP 5 /* operator */ +# define NOCHANGE 6 /* guarantees no token change */ + +/* macro definitions */ +struct macro +{ + struct macro *nextm; /* pointer to next macro header */ + char *template; /* pointer to macro template */ + char *substitute; /* pointer to substitution text */ +}; + +/* primitive declarations */ +static struct macro Macprims[] = { +&Macprims[1], "{define;\020\024t;\020\024s}", (char *) 1, +&Macprims[2], "{rawdefine;\020\024t;\020\024s}", (char *) 2, +&Macprims[3], "{remove;\020\024t}", (char *) 3, +&Macprims[4], "{dump}", (char *) 4, +&Macprims[5], "{type;\020\024m}", (char *) 5, +&Macprims[6], "{read;\020\024m}", (char *) 6, +&Macprims[7], "{readdefine;\020\024n;\020\024m}", (char *) 7, +&Macprims[8], "{ifsame;\020\024a;\020\024b;\020\023t;\020\023f}", (char *) 8, +&Macprims[9], "{ifeq;\020\024a;\020\024b;\020\023t;\020\023f}", (char *) 9, +&Macprims[10], "{ifgt;\020\024a;\020\024b;\020\023t;\020\023f}", (char *) 10, +&Macprims[11], "{same;\020\024a;\020\024b}", (char *) 11, +&Macprims[12], "{if;\020\024e;\020\023t;\020\023f}", (char *) 12, +&Macprims[13], "{expr;\020\024e}", (char *) 13, +&Macprims[14], "{exprdefine;\020\024n;\020\024e}", (char *) 14, +&Macprims[15], "{substr;\020\024f;\020\024t;\024s}", (char *) 15, +&Macprims[16], "{dnl}", (char *) 16, +&Macprims[17], "{remove}", (char *) 3, +0, "{dump;\020\024n}", (char *) 4, +}; + +static struct macro *Machead = &Macprims[0]; /* head of macro list */ + +/* parameters */ +struct param +{ + struct param *nextp; + int mode; + int name; + char *paramt; +}; + +/* the environment */ +struct env +{ + struct env *nexte; /* next environment */ + char (*rawget)(); /* raw character get routine */ + char **rawpar; /* a parameter to that routine */ + int prevchar; /* previous character read */ + int tokenmode; /* current token mode */ + int change; /* token change flag */ + int eof; /* eof flag */ + int newline; /* set if bol */ + int rawnewline; /* same for raw input */ + struct buf *pbuf; /* peek buffer */ + struct buf *mbuf; /* macro buffer */ + int endtrap; /* endtrap flag */ + int pass; /* pass flag */ + int pdelim; /* current parameter delimiter */ + struct param *params; /* parameter list */ + int itercount; /* iteration count */ + int quotelevel; /* quote nesting level */ +}; + +/* current environment pointer */ +static struct env *Macenv; + +/* +** MACRO PROCESSOR +** +** +** +** MACINIT -- initialize for macro processing +** +** *** EXTERNAL INTERFACE *** +** +** The macro processor is initialized. Any crap left over from +** previous processing (which will never occur normally, but may +** happen on an interrupt, for instance) will be cleaned up. The +** raw input is defined, and the 'endtrap' parameter tells whether +** this is "primary" processing or not; in other words, it tells +** whether to spring BEGINTRAP and ENDTRAP. +** +** This routine must always be called prior to any processing. +*/ +macinit(rawget, rawpar, endtrap) +char (*rawget)(); +char **rawpar; +int endtrap; +{ + static struct env env; + register struct env *e; + register struct env *f; + + /* clear out old crap */ + for (e = Macenv; e; e = f) + { + bufpurge(&e->mbuf); + bufpurge(&e->pbuf); + macpflush(e); + if (f = e->nexte) + buffree(e); + } + + /* set up the primary environment */ + Macenv = e = &env; + bufclear(e, sizeof *e); + + e->rawget = rawget; + e->rawpar = rawpar; + e->endtrap = endtrap; + e->newline = TRUE; + + if (endtrap) + macspring(BEGINTRAP); +} + + +/* +** MACGETCH -- get character after macro processing +** +** *** EXTERNAL INTERFACE ROUTINE *** +** +** The macro processor must have been previously initialized by a +** call to macinit(). +*/ +macgetch() +{ + register struct env *e; + register int c; + + e = Macenv; + for ( ; ; ) + { + /* get an input character and + ** check for end-of-file processing */ + if (!(c = macgch())) + { + /* check to see if we should spring ENDTRAP */ + if (e->endtrap) + { + e->endtrap = FALSE; + macspring(ENDTRAP); + continue; + } + + /* don't spring endtrap -- real end of file */ + return (0); + } + + /* not an end of file -- check for pass character through */ + if (e->pass) + { + e->pass = FALSE; + e->change = FALSE; + } + if (c & QUOTED || !e->change || e->tokenmode == DELIM) + { + /* the character is to be passed through */ + /* reset iteration count and purge macro buffer */ + e->itercount = 0; + bufflush(&e->mbuf); + e->newline = (c == NEWLINE); + return (c & CHARMASK); + } + + /* this character is a candidate for macro processing */ + macunget(FALSE); + bufflush(&e->mbuf); + + /* check for infinite loop */ + if (e->itercount > ITERTHRESH) + { + printf("Unendlicher Zyklus in der Makroexpansion\n"); + fflush(stdout); + e->pass = TRUE; + continue; + } + + /* see if we have a macro match */ + if (macallscan()) + /* yep -- count iterations and rescan it */ + e->itercount++; + else + /* nope -- pass the next token through raw */ + e->pass = TRUE; + } +} + + +/* +** MACGCH -- get input character, knowing about tokens +** +** The next input character is returned. In addition, the quote +** level info is maintained and the QUOTED bit is set if the +** returned character is (a) quoted or (b) backslash escaped. +** As a side effect the change flag is maintained. Also, the +** character is saved in mbuf. +*/ +macgch() +{ + register int c; + register struct env *e; + + e = Macenv; + + for ( ; ; ) + { + /* get virtual raw character, save in mbuf, and set change + ** and test for magic frotz */ + switch (c = macfetch(e->quotelevel > 0)) + { + case 0: /* end of file */ + return (0); + + case LQUOTE: + if (!e->quotelevel++) + continue; + break; + + case RQUOTE: + if (!e->quotelevel) + return (c); + if (!--e->quotelevel) + continue; + break; + + case BACKSLASH: + if (e->quotelevel > 0) + break; + /* handle special cases */ + if ((c = macfetch(TRUE)) == e->pdelim) + break; + /* do translations */ + switch (c) + { + case SPACE: /* space */ + case TAB: /* tab */ + case NEWLINE: /* newline */ + case RQUOTE: + case LQUOTE: + case '$': + case LBRACE: + case RBRACE: + case BACKSLASH: + break; + + default: + /* take character as is (unquoted) */ + c = 0; + break; + } + if (c) + break; + /* not an escapable character -- treat it normally */ + macunget(TRUE); + c = BACKSLASH; + /* do default character processing on backslash */ + + default: + if (e->quotelevel > 0) + break; + return (c); + } + + /* the character is quoted */ + return (c | QUOTED); + } +} + + +/* +** MACFETCH -- fetch virtual raw character +** +** A character is fetched from the peek buffer. If that buffer is +** empty, it is fetched from the raw input. The character is then +** saved away, and the change flag is set accordingly. +** The QUOTED bit on the character is set if the 'quote' flag +** parameter is set; used for backslash escapes. +** Note that the QUOTED bit appears only on the character which +** goes into the macro buffer; the character returned is normal. +*/ +macfetch(quote) +int quote; +{ + register struct env *e; + register int c; + register int escapech; + + e = Macenv; + escapech = 0; + + for ( ; ; ) + { + /* get character from peek buffer */ + if (!(c = bufget(&e->pbuf))) + { + /* peek buffer is empty */ + /* check for already raw eof */ + if (!e->eof) + { + /* note that c must be int so that the QUOTED bit is not negative */ + if ((c = (*e->rawget)(e->rawpar)) <= 0) + { + c = 0; + e->eof = TRUE; + } + else + { + if (e->rawnewline) + e->prevchar = NEWLINE; + e->rawnewline = (c == NEWLINE); + } + } + } + + /* test for escapable character */ + if (escapech) + { + switch (c) + { + case 't': /* become quoted tab */ + c = TAB | QUOTED; + break; + + case 'n': /* become quoted newline */ + c = NEWLINE | QUOTED; + break; + + default: + bufput(c, &e->pbuf); + c = BACKSLASH; + } + escapech = 0; + } + else + if (c == BACKSLASH) + { + escapech++; + continue; + } + break; + } + + /* quote the character if appropriate to mask change flag */ + /* ('escapech' now becomes the maybe quoted character) */ + escapech = c; + if (quote && c) + escapech |= QUOTED; + + /* set change flag */ + macschng(escapech); + + if (c) /* save the character in the macro buffer */ + bufput(escapech, &e->mbuf); + + return (c); +} + + +/* +** MACSCHNG -- set change flag and compute token type +** +** The change flag and token type is set. This does some tricky +** stuff to determine just when a new token begins. Most notably, +** notice that quoted stuff IS scanned, but the change flag is +** reset in a higher level routine so that quoted stuff looks +** like a single token, but any begin/end quote causes a token +** change. +*/ +macschng(c) +register int c; +{ + register struct env *e; + register int thismode; + register int changeflag; + + changeflag = 0; + thismode = macmode(c); + + switch ((e = Macenv)->tokenmode) + { + case NONE: + /* always cause token change */ + break; + + case QUOTEMODE: + /* change only on initial entry to quotes */ + break; + + case DELIM: + changeflag++; + break; + + case ID: + /* take any sequence of letters and numerals */ + if (thismode == NUMBER) + thismode = ID; + break; + + case NUMBER: + /* take string of digits and decimal points */ + if (c == '.') + thismode = NUMBER; + break; + + case OP: + switch (e->prevchar) + { + case '<': + case '>': + case '!': + if (c != '=') + changeflag++; + break; + + case '*': + if (c != '*' && c != '/') + changeflag++; + break; + + case '/': + if (c != '*') + changeflag++; + break; + + case '.': + if (thismode == NUMBER) + e->tokenmode = thismode; + break; + + default: + changeflag++; + break; + } + break; + + case NOCHANGE: /* never cause token change */ + e->tokenmode = thismode; + break; + } + + e->prevchar = c; + if (thismode != e->tokenmode) + changeflag++; + e->tokenmode = thismode; + e->change = changeflag; +} + + +/* +** MACMODE -- return mode of a character +*/ +macmode(c) +register int c; +{ + if (c & QUOTED) + return (QUOTEMODE); +/*?EBCDIC?*/ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_')) + return (ID); + if (c >= '0' && c <= '9') + return (NUMBER); + if (c == SPACE || c == TAB || c == NEWLINE) + return (DELIM); + return (OP); +} + + +/* +** MACALLSCAN -- scan to see if input matches a macro +** +** Returns true if there was a match, false if not. In any case, +** the virtual raw input (i.e., the peek buffer) will contain +** either the old raw input, or the substituted macro. +*/ +macallscan() +{ + register struct macro *m; + + for (m = Machead; m; m = m->nextm) + { + /* check to see if it matches this macro */ + if (macscan(m)) + { + /* it does -- substituted value is in mbuf */ + macrescan(); + return (TRUE); + } + + /* it doesn't match this macro -- try the next one */ + macrescan(); + } + + /* it doesn't match any of them -- tough luck */ + return (FALSE); +} + + +/* +** MACSCAN -- scan a single macro for a match +** +** As is scans it also collects parameters for possible future +** substitution. If it finds a match, it takes responsibility +** for doing the substitution. +*/ +macscan(m) +register struct macro *m; +{ + register int c; + register char *temp; + register int pname; + register int pdelim; + + /* check for anchored mode */ + if (*(temp = m->template) == ONEDELIM) + { + if (!Macenv->newline) + return (FALSE); + temp++; + } + + /* scan the template */ + for ( ; c = *temp; temp++) + { + if (c == PARAMN || c == PARAMP) + { + /* we have a parameter */ + pname = *++temp; + pdelim = *++temp; + if (macparam(c, pname, pdelim)) /* parameter ok */ + continue; + + /* failure on parameter scan */ + return (FALSE); + } + + if (!macmatch(c)) /* failure on literal match */ + return (FALSE); + } + + /* it matches!! substitute the macro */ + macsubs(m); + return (TRUE); +} + + +/* +** MACPARAM -- collect a parameter +** +** The parameter is collected and stored away "somewhere" with +** name 'name'. The delimiter is taken to be 'delim'. 'Mode' +** tells whether to prescan the parameter (done immediately before +** substitute time to avoid side effects if the macro actually +** turns out to not match). +*/ +macparam(mode, name, delim) +int mode; +int name; +int delim; +{ + register int c; + register struct env *e; + register struct param *p; + struct buf *b; + register int bracecount; + extern struct buf *bufalloc(); + extern char *bufcrunch(); + + b = (struct buf *) 0; + + (e = Macenv)->pdelim = delim; +# ifdef xMTRM + AAtTfp(70, 1, "MACPARAM(0%o, %c, %c)\n", mode, name, delim); +# endif + if (mode == PARAMP) + { + /* check for REALLY prescan */ + if ((c = macgch()) != PRESCANENABLE) + { + mode = PARAMN; + macunget(FALSE); + } + } + + bracecount = 0; + e->tokenmode = NOCHANGE; + while (!macmatch(delim)) + do + { + if (!(c = macgch()) || c == NEWLINE) + { + e->pdelim = 0; + bufpurge(&b); +# ifdef xMTRM + AAtTfp(70, 1, "MACPARAM: fails\n"); +# endif + return (FALSE); + } + bufput(c, &b); + if (c == LBRACE) + bracecount++; + else if (c == RBRACE && bracecount > 0) + bracecount--; + } while (bracecount > 0); + + e->pdelim = 0; + + /* allocate and store the parameter */ + p = (struct param *) bufalloc(sizeof *p); + p->mode = mode; + p->name = name; + p->nextp = e->params; + e->params = p; + p->paramt = bufcrunch(&b); + bufpurge(&b); +# ifdef xMTRM + AAtTfp(70, 1, "MACPARAM: |%s|\n", p->paramt); +# endif + + return (TRUE); +} + + +/* +** MACMATCH -- test for a match between template character and input. +** +** The parameter is the character from the template to match on. +** The input is read. The template character may be a meta- +** character. In all cases if the match occurs the input is +** thrown away; if no match occurs the input is left unchanged. +** +** Return value is true for a match, false for no match. +*/ +macmatch(t) +register int t; +{ + register int c; + +# ifdef xMTRM + AAtTfp(70, 1, "MACMATCH(%c)\n", t); +# endif + + switch (t) + { + case ANYDELIM: /* match zero or more delimiters */ + /* chew and chuck delimiters */ + while (macdelim()) + continue; + + /* as a side effect, must match a token change */ + if (!macckch()) + { +# ifdef xMTRM + AAtTfp(70, 1, "MACMATCH: fail\n"); +# endif + return (FALSE); + } +# ifdef xMTRM + AAtTfp(70, 1, "MACMATCH: succeed\n"); +# endif + return (TRUE); + + case ONEDELIM: /* match exactly one delimiter */ + return (macdelim()); + + case CHANGE: /* match a token change */ + case 0: /* end of template */ + return (macckch()); + + default: /* must have exact character match */ + c = macgch(); +# ifdef xMTRM + AAtTfp(70, 1, "MACMATCH: against %c\n", c); +# endif + if (c == t) + { +# ifdef xMTRM + AAtTfp(70, 1, "MACMATCH: succeed\n"); +# endif + return (TRUE); + } + + /* failure */ + macunget(FALSE); +# ifdef xMTRM + AAtTfp(70, 1, "MACMATCH: fail\n"); +# endif + return (FALSE); + } +} + + +/* +** MACDELIM -- test for next input character a delimiter +** +** Returns true if the next input character is a delimiter, false +** otherwise. Delimiters are chewed. +*/ +macdelim() +{ + register int c; + + c = macgch(); +# ifdef xMTRM + AAtTfp(70, 1, "MACDELIM: against %c\n", c); +# endif + if (macmode(c) == DELIM) + { +# ifdef xMTRM + AAtTfp(70, 1, "MACDELIM: succeed\n"); +# endif + return (TRUE); + } + macunget(FALSE); +# ifdef xMTRM + AAtTfp(70, 1, "MACDELIM: fail\n"); +# endif + return (FALSE); +} + + +/* +** MACCKCH -- check for token change +** +** Returns true if a token change occurs between this and the next +** character. No characters are ever chewed, however, the token +** change (if it exists) is always chewed. +*/ +macckch() +{ + register int change; +# ifdef xMTRM + register int c; +# endif + register struct env *e; + + if ((e = Macenv)->tokenmode == NONE) + /* then last character has been ungotten: take old change */ + change = e->change; + else + { +# ifdef xMTRM + c = +# endif + macgch(); + change = e->change; + macunget(FALSE); + } +# ifdef xMTRM + AAtTfp(70, 1, "MACCKCH: got %c ret %d\n", c, change); +# endif + + /* chew the change and return */ + e->tokenmode = NOCHANGE; + return (change); +} + + +/* +** MACSUBS -- substitute in macro substitution +** +** This routine prescans appropriate parameters and then either +** loads the substitution into the macro buffer or calls the +** correct primitive routine. +*/ +macsubs(mac) +struct macro *mac; +{ + register struct param *p; + register struct env *e; + register char *s; + extern char *macprim(); + + for (p = (e = Macenv)->params; p; p = p->nextp) + { + /* check to see if we should prescan */ + if (p->mode != PARAMP) + continue; + + /* prescan parameter */ + macprescan(&p->paramt); + p->mode = PARAMN; + } + + s = mac->substitute; + + /* clear out the macro call */ + bufflush(&e->mbuf); + + if (s <= (char *) NPRIMS) /* it is a primitive */ + macload(macprim((int) s)); + else /* it is a user-defined macro */ + macload(s); +} + + +/* +** MACPRESCAN -- prescan a parameter +** +** The parameter pointed to by 'pp' is fed once through the macro +** processor and replaced with the new version. +*/ +macprescan(pp) +register char **pp; +{ + register int c; + char *p; + struct buf *b; + extern int macsget(); + extern char *bufcrunch(); + + b = (struct buf *) 0; + p = *pp; + + /* set up a new environment */ + macnewev(macsget, &p); + + /* scan the parameter */ + while (c = macgetch()) + bufput(c, &b); + + /* free the old parameter */ + buffree(*pp); + + /* move in the new one */ + *pp = bufcrunch(&b); + bufpurge(&b); + + /* restore the old environment */ + macpopev(); +} + + +/* +** MACNEWEV -- set up new environment +** +** Parameters are raw get routine and parameter +*/ +macnewev(rawget, rawpar) +char (*rawget)(); +register char **rawpar; +{ + register struct env *e; + extern struct buf *bufalloc(); + + e = (struct env *) bufalloc(sizeof *e); + e->rawget = rawget; + e->rawpar = rawpar; + e->nexte = Macenv; + e->newline = TRUE; + Macenv = e; +} + + +/* +** MACPOPEV -- pop an environment +** +** Makes sure all buffers and stuff are purged +*/ +macpopev() +{ + register struct env *e; + + bufpurge(&(e = Macenv)->mbuf); + bufpurge(&e->pbuf); + macpflush(e); + Macenv = e->nexte; + buffree(e); +} + + +/* +** MACPFLUSH -- flush all parameters +** +** Used to deallocate all parameters in a given environment. +*/ +macpflush(e) +register struct env *e; +{ + register struct param *p; + register struct param *q; + + for (p = e->params; p; p = q) + { + buffree(p->paramt); + q = p->nextp; + buffree(p); + } + + e->params = (struct param *) 0; +} + + +/* +** MACSGET -- get from string +** +** Works like a getchar from a string. Used by macprescan(). +** The parameter is a pointer to the string. +*/ +macsget(p) +register char **p; +{ + register int c; + + if (c = ctou(**p)) + (*p)++; + return (c); +} + + +/* +** MACLOAD -- load a string into the macro buffer +** +** The parameters are a pointer to a string to be appended to +** the macro buffer and a flag telling whether parameter substi- +** tution can occur. +*/ +macload(s) +register char *s; +{ + register struct env *e; + register int c; + + if (!s) + return; + + e = Macenv; + + while (c = *s++) + if (c == PARAMN) + macload(macplkup(*s++)); + else + bufput(c & CHARMASK, &e->mbuf); +} + + +/* +** MACRESCAN -- rescan the macro buffer +** +** Copies the macro buffer into the peek buffer so that it will be +** reread. Also deallocates any parameters which may happen to be +** stored away. +*/ +macrescan() +{ + register struct env *e; + register int c; + + e = Macenv; + + while ((c = bufget(&e->mbuf) & CHARMASK)) + bufput(c, &e->pbuf); + + e->quotelevel = 0; + e->tokenmode = NONE; + macpflush(e); +} + + +/* +** MACUNGET -- unget a character +** +** Moves one character from the macro buffer to the peek buffer. +** If 'mask' is set, the character has the quote bit stripped off. +*/ +macunget(mask) +register int mask; +{ + register struct env *e; + register int c; + + if ((e = Macenv)->prevchar) + { + c = bufget(&e->mbuf); + if (mask) + c &= CHARMASK; + bufput(c, &e->pbuf); + e->tokenmode = NONE; + } +} + + +/* +** MACPLKUP -- look up parameter +** +** Returns a pointer to the named parameter. Returns null +** if the parameter is not found ("cannot happen"). +*/ +char *macplkup(name) +register int name; +{ + register struct param *p; + + for (p = Macenv->params; p; p = p->nextp) + if (p->name == name) + return (p->paramt); + + return ((char *) 0); +} + + +/* +** MACSPRING -- spring a trap +** +** The named trap is sprung, in other words, if the named macro is +** defined it is called, otherwise there is no replacement text. +*/ +macspring(trap) +register char *trap; +{ + register struct env *e; + extern char *macro(); + + bufflush(&(e = Macenv)->mbuf); + + /* fetch the macro */ + trap = macro(trap); + + /* if not defined, don't bother */ + if (!trap) + return; + + /* load the trap */ + macload(trap); + + /* insert a newline after the trap */ + bufput('\n', &e->mbuf); + + macrescan(); +} + + +/* +** MACPRIM -- do primitives +** +** The parameter is the primitive to execute. +*/ +char *macprim(n) +register int n; +{ + register char *c; + extern char *bufcrunch(); + extern char *macsstr(); + + switch (n) + { + case 1: /* {define; $t; $s} */ + case 2: /* {rawdefine; $t; $s} */ + macdnl(); + macdefine(macplkup('t'), macplkup('s'), n == 2); + break; + + case 3: /* {remove} */ + /* {remove; $t} */ + macdnl(); + macremove(macplkup('t')); + break; + + case 4: /* {dump} */ + /* {dump; $n} */ + macdnl(); + macdump(macplkup('n')); + break; + + case 5: /* {type; $m} */ + macdnl(); + c = macplkup('m'); + while (*c &= CHARMASK) + { + putchar(*c); + c++; + } + putchar('\n'); + break; + + case 6: /* {read; $m} */ + case 7: /* {readdefine; $n; $m} */ + printf("%s ", macplkup('m')); + fflush(stdout); + macread(); + if (n == 6) + break; + + macdefine(macplkup('n'), bufcrunch(&Macenv->mbuf), TRUE); + return (READCOUNT); + + case 8: /* {ifsame; $a; $b; $t; $f} */ + return (macplkup(AAsequal(macplkup('a'), macplkup('b'))? 't': 'f')); + + case 9: /* {ifeq; $a; $b; $t; $f} */ + return (macplkup(macexpr(macplkup('a')) == macexpr(macplkup('b'))? 't': 'f')); + + case 10: /* {ifgt; $a; $b; $t; $f} */ + return (macplkup(macexpr(macplkup('a')) > macexpr(macplkup('b'))? 't': 'f')); + + case 11: /* {same; $a; $b} */ + return (AA_iocv(AAscompare(macplkup('a'), 0, macplkup('b'), 0))); + + case 12: /* {if; $e; $t; $f} */ + return (macplkup((macexpr(macplkup('e')) > 0)? 't': 'f')); + + case 13: /* {expr; $e} */ + return (AA_iocv(macexpr(macplkup('e')))); + + case 14: /* {exprdefine; $n; $e} */ + macdnl(); + macdefine(macplkup('n'), AA_iocv(macexpr(macplkup('e'))), FALSE); + break; + + case 15: /* {substr; $f; $t; $s} */ + return (macsstr(macexpr(macplkup('f')), macexpr(macplkup('t')), macplkup('s'))); + + case 16: /* {dnl} */ + macdnl(); + break; + + default: + AAsyserr(16013, n); + } + + return (""); +} + + +/* +** MACDNL -- delete to newline +** +** Used in general after macro definitions to avoid embarrassing +** newlines. Just reads input until a newline character, and +** then throws it away. +*/ +macdnl() +{ + register int c; + + while ((c = macgch()) && c != NEWLINE) + continue; + + bufflush(&Macenv->mbuf); +} + + +/* +** MACDEFINE -- define primitive +** +** This function defines a macro. The parameters are the +** template, the substitution string, and a flag telling whether +** this is a raw define or not. Syntax checking is done. +*/ +macdefine(template, subs, raw) +char *template; +char *subs; +int raw; +{ + register struct env *e; + register int c; + register struct macro *m; + char *p; + struct buf *b; + char paramdefined[128]; + extern int macsget(); + extern struct buf *bufalloc(); + extern char *bufcrunch(); + extern char *mactcvt(); + +# ifdef xMTRM + AAtTfp(70, 0, "MACDEFINE: T='%s' S='%s'\n", template, subs); +# endif + + b = (struct buf *) 0; + + /* remove any old macro definition */ + macremove(template); + + /* get a new environment */ + macnewev(macsget, &p); + + /* undefine all parameters */ + bufclear(paramdefined, 128); + + /* avoid an initial token change */ + (e = Macenv)->tokenmode = NOCHANGE; + + /* allocate macro header and template */ + m = (struct macro *) bufalloc(sizeof *m); + + /* scan and convert template, collect available parameters */ + p = template; + m->template = mactcvt(raw, paramdefined); + if (!m->template) + { + /* some sort of syntax error */ + buffree(m); + macpopev(); + return; + } + + bufflush(&e->mbuf); + bufflush(&e->pbuf); + e->eof = FALSE; + + /* scan substitute string */ + for (p = subs; c = macfetch(FALSE); ) + { + if (c != '$') + { + /* substitute non-parameters literally */ + bufput(c & CHARMASK, &b); + continue; + } + + /* it's a parameter */ + bufput(PARAMN, &b); + + /* check to see if name is supplied */ + if (!paramdefined[c = macfetch(FALSE)]) + { + /* nope, it's not */ + printf("%sDer Parameter '%c' ist nicht definiert\n", ERROR, c); + fflush(stdout); + buffree(m->template); + buffree(m); + macpopev(); + bufpurge(&b); + return; + } + bufput(c & CHARMASK, &b); + } + + /* allocate substitution string */ + m->substitute = bufcrunch(&b); + + /* allocate it as a macro */ + m->nextm = Machead; + Machead = m; + + /* finished... */ + macpopev(); + bufpurge(&b); +} + + +/* +** MACTCVT -- convert template to internal form +** +** Converts the template from external form to internal form. +** +** Parameters: +** raw -- set if only raw type conversion should take place. +** paramdefined -- a map of flags to determine declaration of +** parameters, etc. If zero, no parameters are allowed. +** +** Return value: +** A character pointer off into mystic space. +** +** The characters of the template are read using macfetch, so +** a new environment should be created which will arrange to +** get this. +*/ +char *mactcvt(raw, paramdefined) +int raw; +char paramdefined[128]; +{ + register int c; + register int escapech; + register int d; + register char *p; + struct buf *b; + extern char *bufcrunch(); + + b = (struct buf *) 0; + escapech = 1; + + while (c = macfetch(FALSE)) + { + switch (c) + { + case '$': /* parameter */ + if (escapech < 0) + { + printf("%sJeder Parameter benoetigt ein Trennzeichen\n", ERROR); + fflush(stdout); + bufpurge(&b); + return ((char *) 0); + } + + /* skip delimiters before parameter in non-raw */ + if (Macenv->change && !escapech && !raw) + bufput(ANYDELIM, &b); + + escapech = 0; + d = PARAMN; + if ((c = macfetch(FALSE)) == '$') + { + /* prescanned parameter */ + d = PARAMP; + c = macfetch(FALSE); + } + + /* process parameter name */ + if (!c) + { + /* no parameter name */ + printf("%sParameter ohne Name\n", ERROR); + fflush(stdout); + bufpurge(&b); + return ((char *) 0); + } + + bufput(d, &b); + escapech = -1; + + /* check for legal parameter */ + if (!paramdefined) + break; + + if (paramdefined[c]) + { + printf("%sParameter '%c' redefiniert\n", ERROR, c); + fflush(stdout); + bufpurge(&b); + return ((char *) 0); + } + paramdefined[c]++; + + /* get parameter delimiter */ + break; + + case BACKSLASH: /* a backslash escape */ + escapech = 1; + switch (c = macfetch(FALSE)) + { + case '|': + c = ANYDELIM; + break; + + case '^': + c = ONEDELIM; + break; + + case '&': + c = CHANGE; + break; + + default: + escapech = 0; + c = BACKSLASH; + macunget(FALSE); + break; + } + break; + + case NEWLINE | QUOTED: + case TAB | QUOTED: + case SPACE | QUOTED: + if (escapech < 0) + c &= CHARMASK; + escapech = 1; + break; + + default: + /* change delimiters to ANYDELIM */ + if (macmode(c) == DELIM && !raw) + { + while (macmode(c = macfetch(FALSE)) == DELIM) + continue; + macunget(FALSE); + c = c? ANYDELIM: ONEDELIM; + escapech = 1; + } + else + { + if (Macenv->change && !escapech) + bufput(ANYDELIM, &b); + + if (escapech < 0) + /* parameter: don't allow quoted delimiters */ + c &= CHARMASK; + escapech = 0; + } + break; + } + bufput(c, &b); + } + if (escapech <= 0) + bufput(CHANGE, &b); + + p = bufcrunch(&b); + bufpurge(&b); +# ifdef xMTRM + AAtTfp(70, 1, "MACTCVT: '%s'\n", p); +# endif + return (p); +} + + +/* +** MACREMOVE -- remove macro +** +** The named macro is looked up. If it is found it is removed +** from the macro list. +*/ +macremove(name) +register char *name; +{ + register struct macro *m; + register struct macro **mp; + register char *cname; + char *p; + extern int macsget(); + extern char *mactcvt(); + extern struct macro *macmlkup(); + +# ifdef xMTRM + AAtTfp(70, 0, "MACREMOVE: '%s'\n", name); +# endif + + cname = (char *) 0; + if (name) + { + /* convert name to internal format */ + macnewev(macsget, &p); + p = name; + cname = mactcvt(FALSE, (char *) 0); + macpopev(); + if (!cname) /* some sort of syntax error */ + return; + } + + /* find macro */ + while (name? ((m = macmlkup(cname)) != (struct macro *) 0): ((m = Machead)->substitute > ((char *) NPRIMS))) + { + /* remove macro from list */ + mp = &Machead; + + /* find it's parent */ + while (*mp != m) + mp = &(*mp)->nextm; + + /* remove macro from list */ + *mp = m->nextm; + buffree(m->template); + buffree(m->substitute); + buffree(m); + } + if (cname) + buffree(cname); +} + + +/* +** MACMLKUP -- look up macro +** +** The named macro is looked up and a pointer to the macro header +** is returned. Zero is returned if the macro is not found. +** The name must be in internal form. +*/ +struct macro *macmlkup(n) +register char *n; +{ + register struct macro *m; + + /* scan the macro list for it */ + for (m = Machead; m; m = m->nextm) + if (macmmatch(n, m->template, FALSE)) + return (m); + return ((struct macro *) 0); +} + + +/* +** MACMMATCH -- check for macro name match +** +** The given 'name' and 'temp' are compared for equality. If they +** match true is returned, else false. +** Both must be converted to internal format before the call is +** given. +** +** "Match" is defined as two macros which might scan as equal. +** +** 'Flag' is set to indicate that the macros must match exactly, +** that is, neither may have any parameters and must end with both +** at end-of-template. This mode is used for getting traps and +** such. +*/ +macmmatch(name, temp, flag) +char *name; +char *temp; +int flag; +{ + register int ac; + register int bc; + char *ap; + char *bp; + + ap = name; + bp = temp; + + /* scan character by character */ + for ( ; ; ap++, bp++) + { + ac = *ap; + bc = *bp; +# ifdef xMTRM + AAtTfp(70, 1, "MACMMATCH: ac=%c/0%o, bc=%c/0%o\n", ac, ap, bc, bp); +# endif + + if (bc == ANYDELIM) + { + if (macmmchew(&ap)) + continue; + } + else + switch (ac) + { + case SPACE: + case NEWLINE: + case TAB: + if (ac == bc || bc == ONEDELIM) + continue; + break; + + case ONEDELIM: + if (ac == bc || macmode(bc) == DELIM) + continue; + break; + + case ANYDELIM: + if (macmmchew(&bp)) + continue; + break; + + case PARAMP: + case PARAMN: + case 0: + if (bc == PARAMN || bc == PARAMP || !bc || + bc == ANYDELIM || bc == ONEDELIM || + bc == CHANGE || macmode(bc) == DELIM) + if (!flag || (!ac && !bc)) /* success */ + return (TRUE); + break; + + default: + if (ac == bc) + continue; + break; + } + + /* failure */ + return (FALSE); + } +} + + +/* +** MACMMCHEW -- chew nonspecific match characters +** +** The pointer passed as parameter is scanned so as to skip over +** delimiters and pseudocharacters. +** At least one character must match. +*/ +macmmchew(pp) +register char **pp; +{ + register char *p; + register int c; + register int matchflag; + + p = *pp; + + for (matchflag = 0; ; matchflag++) + { + if ((c = *p) != ANYDELIM && c != ONEDELIM && c != CHANGE && macmode(c) != DELIM) + break; + p++; + } + + p--; + if (!matchflag) + return (FALSE); + *pp = p; + return (TRUE); +} + + +/* +** MACREAD -- read a terminal input line +** +** Reads one line from the user. Returns the line into mbuf, +** and a count of the number of characters read into the macro +** READCOUNT (-1 for end of file). +*/ +macread() +{ + register struct env *e; + register int count; + register int c; + + e = Macenv; + count = -1; + + while ((c = fgetchar()) > 0) + { + count++; + if (c == NEWLINE) + break; + bufput(c, &e->mbuf); + } + + macdefine(READCOUNT, AA_iocv(count), TRUE); +} +/* +** macexpr -- return converted expr +** +*/ +macexpr(p) +char *p; +{ + extern int macsget(); + + return (expr(macsget, &p)); +} + + +/* +** MACSUBSTR -- substring primitive +** +** The substring of 'string' from 'from' to 'to' is extracted. +** A pointer to the result is returned. Note that macsstr +** in the general case modifies 'string' in place. +*/ +char *macsstr(from, to, string) +register int from; +register int to; +register char *string; +{ + if (from < 1) + from = 1; + if (from > to || from > AAlength(string)) + return (""); + if (to < AAlength(string)) + string[to] = 0; + return (&string[--from]); +} + + +/* +** MACDUMP -- dump a macro definition to the terminal +** +** All macros matching 'name' are output to the buffer. If +** 'name' is the null pointer, all macros are printed. +*/ +macdump(n) +register char *n; +{ + register struct macro *m; + char *ptr; + extern int macsget(); + extern char *mactcvt(); + extern char *macmocv(); + + if (n) + { + macnewev(macsget, &ptr); + ptr = n; + n = mactcvt(FALSE, (char *) 0); + macpopev(); + if (!n) + return; + } + + for (m = Machead; m; m = m->nextm) + if (!n || macmmatch(n, m->template, FALSE)) + { + if (m->substitute <= (char *) NPRIMS) + continue; + printf("{rawdefine; %s;", macmocv(m->template)); + printf(" %s}\n", macmocv(m->substitute)); + fflush(stdout); + } + if (n) + buffree(n); +} + + +/* +** MACMOCV -- macro output conversion +** +** This routine converts the internal format of the named macro +** to an unambigous external representation. +** +** Note that much work can be done to this routine to make it +** produce cleaner output, for example, translate "\|" to " " +** in most cases. +*/ +char *macmocv(m) +char *m; +{ + register char *p; + register int c; + register int pc; + struct buf *b; + static char *lastbuf; + extern char *bufcrunch(); + + /* release last used buffer (as appropriate) */ + if (lastbuf) + { + buffree(lastbuf); + lastbuf = (char *) 0; + } + + if ((p = m) <= (char *) NPRIMS) + { + /* we have a primitive */ + p = "Primitive xxx"; + AA_itoa((int) m, &p[10]); + return (p); + } + + b = (struct buf *) 0; + pc = 0; + + for ( ; c = *p++; pc = c) + { + switch (c) + { + case BACKSLASH: + case '|': + case '&': + case '^': + break; + + case ANYDELIM: + c = '\\' | ('|' << 8); + break; + + case ONEDELIM: + c = '\\' | ('^' << 8); + break; + + case CHANGE: + c = '\\' | ('&' << 8); + break; + + case PARAMN: + c = '$'; + break; + + case PARAMP: + c = '$' | ('$' << 8); + break; + + case '$': + c = '\\' | ('$' << 8); + break; + + case NEWLINE: + c = ('\\' | QUOTED) | ('\n' << 8); + break; + + default: + bufput(c, &b); + continue; + } + + if (pc == BACKSLASH) + bufput(pc, &b); + bufput(pc = c & CHARMASK, &b); + if (pc = (c >> 8) & CHARMASK) + bufput(c = pc, &b); + } + + p = bufcrunch(&b); + bufpurge(&b); + lastbuf = p; + return (p); +} + + +/* +** MACRO -- get macro substitution value +** +** *** EXTERNAL INTERFACE *** +** +** This routine handles the rather specialized case of looking +** up a macro and returning the substitution value. The name +** must match EXACTLY, character for character. +** +** The null pointer is returned if the macro is not defined. +*/ +char *macro(name) +char *name; +{ + register struct macro *m; + register char *n; + char *p; + extern int macsget(); + extern char *mactcvt(); + + /* convert macro name to internal format */ + p = name; + macnewev(macsget, &p); + n = mactcvt(FALSE, (char *) 0); + macpopev(); + if (!n) /* some sort of syntax error */ + return ((char *) 0); + + for (m = Machead; m; m = m->nextm) + if (macmmatch(n, m->template, TRUE)) + { + buffree(n); + return (m->substitute); + } + + buffree(n); + return ((char *) 0); +}