home *** CD-ROM | disk | FTP | other *** search
- # include <useful.h>
- # include <sccs.h>
-
- SCCSID(@(#)mac.c 8.1 12/31/84)
-
-
- # define TRACE if (FALSE) printf
-
- /*
- ** MACRO PROCESSOR
- */
-
-
- # 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 BYTEMASK 0377 /* one byte */
-
- # 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 */
-
-
-
- # include "buf.h" /* headers for buffer manip */
-
-
- /* 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 */
- 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], "{eval\020\024e}", (char *) 11,
- &Macprims[12], "{substr;\020\024f;\020\024t;\024s}", (char *) 12,
- &Macprims[13], "{dnl}", (char *) 13,
- &Macprims[14], "{remove}", (char *) 3,
- 0, "{dump;\020\024n}", (char *) 4,
- };
-
- struct macro *Machead = &Macprims[0]; /* head of macro list */
-
-
- /* parameters */
- struct param
- {
- struct param *nextp;
- char mode;
- char name;
- char *paramt;
- };
-
-
-
- /* the environment */
- struct env
- {
- struct env *nexte; /* next environment */
- int (*rawget)(); /* raw character get routine */
- char **rawpar; /* a parameter to that routine */
- char prevchar; /* previous character read */
- char tokenmode; /* current token mode */
- char change; /* token change flag */
- char eof; /* eof flag */
- char newline; /* set if bol */
- char rawnewline; /* same for raw input */
- struct buf *pbuf; /* peek buffer */
- struct buf *mbuf; /* macro buffer */
- char endtrap; /* endtrap flag */
- char pass; /* pass flag */
- char pdelim; /* current parameter delimiter */
- struct param *params; /* parameter list */
- int itercount; /* iteration count */
- int quotelevel; /* quote nesting level */
- };
-
- /* current environment pointer */
- struct env *Macenv;
- /*
- ** 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)
- int (*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 != 0; e = f)
- {
- bufpurge(&e->mbuf);
- bufpurge(&e->pbuf);
- macpflush(e);
- f = e->nexte;
- if (f != 0)
- buffree(e);
- }
-
- /* set up the primary environment */
- Macenv = e = &env;
- clrmem(e, sizeof *e);
-
- e->rawget = rawget;
- e->rawpar = rawpar;
- e->endtrap = endtrap;
- e->newline = 1;
-
- 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 */
- c = macgch();
-
- /* check for end-of-file processing */
- if (c == 0)
- {
- /* check to see if we should spring {endtrap} */
- if (e->endtrap)
- {
- e->endtrap = 0;
- 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 = 0;
- e->change = 0;
- }
- if ((c & QUOTED) != 0 || !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(0);
- bufflush(&e->mbuf);
-
- /* check for infinite loop */
- if (e->itercount > ITERTHRESH)
- {
- printf("Infinite loop in macro\n");
- e->pass++;
- 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++;
- }
- }
- }
- /*
- ** 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;
- register int i;
-
- e = Macenv;
-
- for (;;)
- {
- /* get virtual raw character, save in mbuf, and set change */
- c = macfetch(e->quotelevel > 0);
-
- /* test for magic frotz */
- switch (c)
- {
- case 0: /* end of file */
- return (0);
-
- case LQUOTE:
- if (e->quotelevel++ == 0)
- continue;
- break;
-
- case RQUOTE:
- if (e->quotelevel == 0)
- return (c);
- if (--e->quotelevel == 0)
- {
- continue;
- }
- break;
-
- case BACKSLASH:
- if (e->quotelevel > 0)
- break;
- c = macfetch(1);
-
- /* handle special cases */
- if (c == 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 != 0)
- break;
-
- /* not an escapable character -- treat it normally */
- macunget(1);
- 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 */
- c = bufget(&e->pbuf);
-
- if (c == 0)
- {
- /* 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 */
- c = (*e->rawget)(e->rawpar);
- if (c <= 0)
- {
- c = 0;
- e->eof++;
- }
- 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 != 0)
- escapech |= QUOTED;
-
- /* set change flag */
- macschng(escapech);
-
- if (c != 0)
- {
- /* 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(ch)
- char ch;
- {
- register struct env *e;
- register char c;
- register int thismode;
- int changeflag;
-
- e = Macenv;
- c = ch;
- changeflag = 0;
- thismode = macmode(c);
-
- switch (e->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(ch)
- char ch;
- {
- register char c;
-
- c = ch;
-
- if ((c & QUOTED) != 0)
- return (QUOTEMODE);
- 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 != 0; m = m->nextm)
- {
- /* check to see if it matches this macro */
- if (macscan(m))
- {
- /* it does -- substituted value is in mbuf */
- macrescan();
- return (1);
- }
-
- /* it doesn't match this macro -- try the next one */
- macrescan();
- }
-
- /* it doesn't match any of them -- tough luck */
- return (0);
- }
- /*
- ** 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(mac)
- struct macro *mac;
- {
- register struct macro *m;
- register char c;
- register char *temp;
- char pname, pdelim;
-
- m = mac;
-
- /* check for anchored mode */
- temp = m->template;
- if (*temp == ONEDELIM)
- {
- if (!Macenv->newline)
- return (0);
- 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 (0);
- }
-
- if (!macmatch(c))
- {
- /* failure on literal match */
- return (0);
- }
- }
-
- /* it matches!! substitute the macro */
- macsubs(m);
- return (1);
- }
- /*
- ** 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)
- char mode;
- char name;
- char delim;
- {
- register char c;
- register struct env *e;
- struct buf *b;
- register struct param *p;
- int bracecount;
- extern char *bufalloc(),*bufcrunch();
- e = Macenv;
- b = 0;
-
- e->pdelim = delim;
- TRACE("\nmacparam(%d, %c, %c):\n", mode, name, delim);
- if (mode == PARAMP)
- {
- /* check for REALLY prescan */
- c = macgch();
- if (c != PRESCANENABLE)
- {
- mode = PARAMN;
- macunget(0);
- }
- }
-
- bracecount = 0;
- e->tokenmode = NOCHANGE;
- while (!macmatch(delim))
- {
- do
- {
- c = macgch();
- if (c == 0 || c == NEWLINE)
- {
- e->pdelim = 0;
- bufpurge(&b);
- TRACE("macparam fails\n");
- return (0);
- }
- 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);
- TRACE("macparam: |%s|\n", p->paramt);
-
- return (1);
- }
- /*
- ** 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(template)
- char template;
- {
- register char t;
- register char c;
- register int res;
-
- t = template;
- TRACE("\tmacmatch(%c)", t);
-
- switch (t)
- {
- case ANYDELIM: /* match zero or more delimiters */
- /* chew and chuck delimiters */
- while (macdelim())
- ;
-
- /* as a side effect, must match a token change */
- if (!macckch())
- {
- TRACE(" fail\n");
- return (0);
- }
- TRACE(" succeed\n");
- return (1);
-
- case ONEDELIM: /* match exactly one delimiter */
- TRACE(":\n");
- res = macdelim();
- return (res);
-
- case CHANGE: /* match a token change */
- case 0: /* end of template */
- TRACE(":\n");
- res = macckch();
- return (res);
-
- default: /* must have exact character match */
- c = macgch();
- TRACE(" against %c ", c);
- if (c == t)
- {
- TRACE("succeed\n");
- return (1);
- }
-
- /* failure */
- macunget(0);
- TRACE("fail\n");
- return (0);
- }
- }
- /*
- ** 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 char c;
-
- c = macgch();
- TRACE("\t\tmacdelim against %c: ", c);
- if (macmode(c) == DELIM)
- {
- TRACE("succeed\n");
- return (1);
- }
- macunget(0);
- TRACE("fail\n");
- return (0);
- }
- /*
- ** 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;
- register char c;
- register struct env *e;
-
- e = Macenv;
-
- if (e->tokenmode == NONE)
- {
- /* then last character has been ungotten: take old change */
- change = e->change;
- }
- else
- {
- c = macgch();
- change = Macenv->change;
- macunget(0);
- }
- TRACE("macckch got %c ret %d\n", c, change);
-
- /* 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;
- char *macprim();
-
- e = Macenv;
-
- for (p = e->params; p != 0; 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(s), 0);
- }
- else
- {
- /* it is a user-defined macro */
- macload(s, 1);
- }
- }
- /*
- ** 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)
- char **pp;
- {
- struct buf *b;
- char *p;
- register struct env *e;
- register char c;
- extern int macsget();
-
- b = 0;
- p = *pp;
-
- /* set up a new environment */
- macnewev(macsget, &p);
- e = Macenv;
-
- /* scan the parameter */
- while ((c = macgetch()) != 0)
- 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)
- int (*rawget)();
- char **rawpar;
- {
- register struct env *e;
- extern char *bufalloc();
-
- e = (struct env *) bufalloc(sizeof *e);
- e->rawget = rawget;
- e->rawpar = rawpar;
- e->nexte = Macenv;
- e->newline = 1;
- Macenv = e;
- }
- /*
- ** MACPOPEV -- pop an environment
- **
- ** Makes sure all buffers and stuff are purged
- */
-
- macpopev()
- {
- register struct env *e;
-
- e = Macenv;
- bufpurge(&e->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(env)
- struct env *env;
- {
- register struct env *e;
- register struct param *p;
- register struct param *q;
-
- e = env;
-
- for (p = e->params; p != 0; p = q)
- {
- buffree(p->paramt);
- q = p->nextp;
- buffree(p);
- }
-
- e->params = 0;
- }
- /*
- ** MACSGET -- get from string
- **
- ** Works like a getchar from a string. Used by macprescan().
- ** The parameter is a pointer to the string.
- */
-
- macsget(pp)
- char **pp;
- {
- register char **p;
- register int c;
-
- p = pp;
-
- c = **p & BYTEMASK;
- if (c != 0)
- (*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(str, flag)
- char *str;
- int flag;
- {
- register struct env *e;
- register char *s;
- register char c;
- extern char *macplkup();
-
- e = Macenv;
- s = str;
-
- if (s == 0)
- return;
-
- while ((c = *s++) != 0)
- {
- if (c == PARAMN)
- macload(macplkup(*s++), 0);
- 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 char c;
-
- e = Macenv;
-
- while ((c = bufget(&e->mbuf) & CHARMASK) != 0)
- 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)
- int mask;
- {
- register struct env *e;
- register char c;
-
- e = Macenv;
-
- if (e->prevchar != 0)
- {
- 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)
- char name;
- {
- register struct param *p;
-
- for (p = Macenv->params; p != 0; p = p->nextp)
- {
- if (p->name == name)
- return (p->paramt);
- }
-
- return (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)
- char *trap;
- {
- register struct env *e;
- register char *p;
- char *macro();
-
- e = Macenv;
-
- bufflush(&e->mbuf);
-
- /* fetch the macro */
- p = macro(trap);
-
- /* if not defined, don't bother */
- if (p == 0)
- return;
-
- /* load the trap */
- macload(p);
-
- /* insert a newline after the trap */
- bufput('\n', &e->mbuf);
-
- macrescan();
- }
- /*
- ** MACPRIM -- do primitives
- **
- ** The parameter is the primitive to execute.
- */
-
- char *
- macprim(n)
- int n;
- {
- register struct env *e;
- extern char *macplkup();
- extern char *macsstr();
-
- e = Macenv;
-
- switch (n)
- {
- case 1: /* {define; $t; $s} */
- macdnl();
- macdefine(macplkup('t'), macplkup('s'), 0);
- break;
-
- case 2: /* {rawdefine; $t; $s} */
- macdnl();
- macdefine(macplkup('t'), macplkup('s'), 1);
- break;
-
- case 3: /* {remove $t} */
- macdnl();
- macremove(macplkup('t'));
- break;
-
- case 4: /* {dump} */
- /* {dump; $n} */
- macdnl();
- macdump(macplkup('n'));
- break;
-
- case 5: /* {type $m} */
- macdnl();
- printf("%s\n", macplkup('m'));
- break;
-
- case 6: /* {read $m} */
- printf("%s ", macplkup('m'));
- macread();
- break;
-
- case 7: /* {read; $n; $m} */
- printf("%s ", macplkup('m'));
- macread();
- macdefine(macplkup('n'), bufcrunch(&e->mbuf), 1);
- return("{readcount}");
-
- case 8: /* {ifsame; $a; $b; $t; $f} */
- if (sequal(macplkup('a'), macplkup('b')))
- return (macplkup('t'));
- else
- return (macplkup('f'));
-
- case 9: /* {ifeq; $a; $b; $t; $f} */
- if (macnumber(macplkup('a')) == macnumber(macplkup('b')))
- return (macplkup('t'));
- else
- return (macplkup('f'));
-
- case 10: /* {ifgt; $a; $b; $t; $f} */
- if (macnumber(macplkup('a')) > macnumber(macplkup('b')))
- return (macplkup('t'));
- else
- return (macplkup('f'));
-
- case 12: /* {substr; $f; $t; $s} */
- return (macsstr(macnumber(macplkup('f')), macnumber(macplkup('t')), macplkup('s')));
-
- case 13: /* {dnl} */
- macdnl();
- break;
-
- default:
- syserr("macro: bad primitive %d", 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 char c;
- register struct env *e;
-
- e = Macenv;
-
- while ((c = macgch()) != 0 && c != NEWLINE)
- ;
-
- bufflush(&e->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;
- char paramdefined[128];
- char *p;
- register char c;
- char d;
- struct buf *b;
- register struct macro *m;
- extern int macsget();
- extern char *bufalloc(),*bufcrunch();
- char *mactcvt();
- int escapech;
-
- /* remove any old macro definition */
- macremove(template);
-
- /* get a new environment */
- macnewev(macsget, &p);
- b = 0;
- e = Macenv;
-
- /* undefine all parameters */
- clrmem(paramdefined, 128);
-
- /* avoid an initial token change */
- e->tokenmode = NOCHANGE;
- escapech = 1;
-
- /* 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 == 0)
- {
- /* some sort of syntax error */
- buffree(m);
- macpopev();
- return;
- }
-
- bufflush(&e->mbuf);
- bufflush(&e->pbuf);
- e->eof = 0;
-
- /* scan substitute string */
- for (p = subs; c = macfetch(0); )
- {
- if (c != '$')
- {
- /* substitute non-parameters literally */
- bufput(c & CHARMASK, &b);
- continue;
- }
-
- /* it's a parameter */
- bufput(PARAMN, &b);
- c = macfetch(0);
-
- /* check to see if name is supplied */
- if (paramdefined[c] == 0)
- {
- /* nope, it's not */
- printf("define: parameter %c referenced but not defined\n", c);
- 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;
- struct buf *b;
- register char d;
- register int escapech;
- char *p;
-
- b = 0;
- escapech = 1;
-
- while (c = macfetch(0))
- {
- switch (c)
- {
- case '$': /* parameter */
- if (escapech < 0)
- {
- printf("define: every parameter needs a delimiter\n");
- bufpurge(&b);
- return (0);
- }
-
- /* skip delimiters before parameter in non-raw */
- if (Macenv->change && !escapech && !raw)
- bufput(ANYDELIM, &b);
-
- escapech = 0;
- c = macfetch(0);
- d = PARAMN;
- if (c == '$')
- {
- /* prescanned parameter */
- d = PARAMP;
- c = macfetch(0);
- }
-
- /* process parameter name */
- if (c == 0)
- {
- /* no parameter name */
- printf("define: null parameter name\n");
- bufpurge(&b);
- return (0);
- }
-
- bufput(d, &b);
- escapech = -1;
-
- /* check for legal parameter */
- if (paramdefined == 0)
- break;
-
- if (paramdefined[c])
- {
- printf("define: parameter %c redeclared\n", c);
- bufpurge(&b);
- return (0);
- }
- paramdefined[c]++;
-
- /* get parameter delimiter */
- break;
-
- case BACKSLASH: /* a backslash escape */
- escapech = 1;
- c = macfetch(0);
- switch (c)
- {
- case '|':
- c = ANYDELIM;
- break;
-
- case '^':
- c = ONEDELIM;
- break;
-
- case '&':
- c = CHANGE;
- break;
-
- default:
- escapech = 0;
- c = BACKSLASH;
- macunget(0);
- 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(0)) == DELIM)
- ;
- macunget(0);
- if (c == 0)
- c = ONEDELIM;
- else
- c = ANYDELIM;
- 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);
- TRACE("mactcvt: '%s'\n", p);
- return (p);
- }
- /*
- ** MACREMOVE -- remove macro
- **
- ** The named macro is looked up. If it is found it is removed
- ** from the macro list.
- */
-
- macremove(name)
- char *name;
- {
- register struct macro *m;
- register struct macro **mp;
- extern int macsget();
- char *p;
- register char *cname;
- struct macro *macmlkup();
-
- if (name != 0)
- {
- /* convert name to internal format */
- macnewev(macsget, &p);
- p = name;
- cname = mactcvt(0, 0);
- macpopev();
- if (cname == 0)
- {
- /* some sort of syntax error */
- return;
- }
- }
-
- /* find macro */
- while (name == 0 ? ((m = Machead)->substitute > (char *) NPRIMS) : ((m = macmlkup(cname)) != 0))
- {
- /* 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);
- }
- 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(name)
- char *name;
- {
- register struct macro *m;
- register char *n;
-
- n = name;
-
- /* scan the macro list for it */
- for (m = Machead; m != 0; m = m->nextm)
- {
- if (macmmatch(n, m->template, 0))
- return (m);
- }
- return (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 char ac;
- register char bc;
- char *ap, *bp;
-
- ap = name;
- bp = temp;
-
- /* scan character by character */
- for (;; ap++, bp++)
- {
- ac = *ap;
- bc = *bp;
- TRACE("macmmatch: ac=%c/%u, bc=%c/%u\n", ac, ap, bc, bp);
-
- 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 == 0 ||
- bc == ANYDELIM || bc == ONEDELIM ||
- bc == CHANGE || macmode(bc) == DELIM)
- {
- /* success */
- if (!flag)
- return (1);
- if (ac == 0 && bc == 0)
- return (1);
- }
- break;
-
- default:
- if (ac == bc)
- continue;
- break;
- }
- }
-
- /* failure */
- return (0);
- }
- }
- /*
- ** 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)
- char **pp;
- {
- register char *p;
- register char c;
- register int matchflag;
-
- p = *pp;
-
- for (matchflag = 0; ; matchflag++)
- {
- c = *p;
- if (c != ANYDELIM && c != ONEDELIM && c != CHANGE &&
- macmode(c) != DELIM)
- break;
- p++;
- }
-
- p--;
- if (matchflag == 0)
- return (0);
- *pp = p;
- return (1);
- }
- /*
- ** 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 char c;
-
- e = Macenv;
- count = -1;
-
- while ((c = getchar()) > 0)
- {
- count++;
- if (c == NEWLINE)
- break;
- bufput(c, &e->mbuf);
- }
-
- macdefine("{readcount}", iocv(count), 1);
- }
- /*
- ** MACNUMBER -- return converted number
- **
- ** This procedure is essentially identical to the system atoi
- ** routine, in that it does no syntax checking whatsoever.
- */
-
- macnumber(s)
- char *s;
- {
- register char *p;
- register char c;
- register int result;
- int minus;
-
- result = 0;
- p = s;
- minus = 0;
-
- while ((c = *p++) == SPACE)
- ;
-
- if (c == '-')
- {
- minus++;
- while ((c = *p++) == SPACE)
- ;
- }
-
- while (c >= '0' && c <= '9')
- {
- result = result * 10 + (c - '0');
- c = *p++;
- }
-
- if (minus)
- result = -result;
-
- return (result);
- }
- /*
- ** 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)
- int from;
- int to;
- char *string;
- {
- register int f;
- int l;
- register char *s;
- register int t;
-
- s = string;
- t = to;
- f = from;
-
- if (f < 1)
- f = 1;
-
- if (f >= t)
- return ("");
- l = length(s);
- if (t < l)
- s[t] = 0;
- return (&s[f - 1]);
- }
- /*
- ** 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(name)
- char *name;
- {
- register struct macro *m;
- register char *p;
- register char *n;
- extern int macsget();
- extern char *macmocv();
- char *ptr;
-
- n = name;
- if (n != 0)
- {
- macnewev(macsget, &ptr);
- ptr = n;
- n = mactcvt(0, 0);
- macpopev();
- if (n == 0)
- return;
- }
-
- for (m = Machead; m != 0; m = m->nextm)
- {
- if (n == 0 || macmmatch(n, m->template, 0))
- {
- if (m->substitute <= (char *) NPRIMS)
- continue;
- p = macmocv(m->template);
- macload("`{rawdefine; ", 0);
- macload(p, 0);
- macload("; ", 0);
- p = macmocv(m->substitute);
- macload(p, 0);
- macload("}'\n", 0);
- }
- }
- if (n != 0)
- 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;
- struct buf *b;
- register int c;
- register int pc;
- static char *lastbuf;
- extern char *bufcrunch();
-
- p = m;
-
- /* release last used buffer (as appropriate) */
- if (lastbuf != 0)
- {
- buffree(lastbuf);
- lastbuf = 0;
- }
-
- if (p <= (char *) NPRIMS)
- {
- /* we have a primitive */
- p = "Primitive xxx";
- itoa(m, &p[10]);
- return (p);
- }
-
- b = 0;
-
- for (; (c = *p++) != 0; pc = c)
- {
- switch (c)
- {
- case BACKSLASH:
- case '|':
- case '&':
- case '^':
- break;
-
- case ANYDELIM:
- c = '\\|';
- break;
-
- case ONEDELIM:
- c = '\\^';
- break;
-
- case CHANGE:
- c = '\\&';
- break;
-
- case PARAMN:
- c = '$';
- break;
-
- case PARAMP:
- c = '$$';
- break;
-
- case '$':
- c = '\\$';
- break;
-
- case NEWLINE:
- c = ('\\' | QUOTED) | ('\n' << 8);
- break;
-
- default:
- bufput(c, &b);
- continue;
- }
-
- if (pc == BACKSLASH)
- bufput(pc, &b);
- pc = c & CHARMASK;
- bufput(pc, &b);
- pc = (c >> 8) & CHARMASK;
- if (pc != 0)
- {
- c = pc;
- bufput(c, &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;
- extern int macsget();
- char *p;
-
- /* convert macro name to internal format */
- macnewev(macsget, &p);
- p = name;
- n = mactcvt(0, 0);
- macpopev();
- if (n == 0)
- {
- /* some sort of syntax error */
- return (0);
- }
-
- for (m = Machead; m != 0; m = m->nextm)
- {
- if (macmmatch(n, m->template, 1))
- {
- buffree(n);
- return (m->substitute);
- }
- }
-
- buffree(n);
- return (0);
- }
-