home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / ingres04.lzh / source / monitor / mac.c < prev    next >
Encoding:
C/C++ Source or Header  |  1985-01-23  |  35.2 KB  |  1,956 lines

  1. # include    <useful.h>
  2. # include    <sccs.h>
  3.  
  4. SCCSID(@(#)mac.c    8.1    12/31/84)
  5.  
  6.  
  7. # define TRACE if (FALSE) printf
  8.  
  9. /*
  10. **  MACRO PROCESSOR
  11. */
  12.  
  13.  
  14. # define    ANYDELIM    '\020'        /* \| -- zero or more delims */
  15. # define    ONEDELIM    '\021'        /* \^ -- exactly one delim */
  16. # define    CHANGE        '\022'        /* \& -- token change */
  17.  
  18. # define    PARAMN        '\023'        /* $ -- non-preprocessed param */
  19. # define    PARAMP        '\024'        /* $$ -- preprocessed param */
  20.  
  21. # define    PRESCANENABLE    '@'        /* character to enable prescan */
  22. # define    LBRACE        '{'        /* left brace */
  23. # define    RBRACE        '}'        /* right brace */
  24. # define    BACKSLASH    '\\'        /* backslash */
  25. # define    LQUOTE        '`'        /* left quote */
  26. # define    RQUOTE        '\''        /* right quote */
  27. # define    SPACE        ' '
  28. # define    TAB        '\t'
  29. # define    NEWLINE        '\n'
  30.  
  31. # define    QUOTED        0200        /* pass right through bit */
  32. # define    CHARMASK    0177        /* character part */
  33. # define    BYTEMASK    0377        /* one byte */
  34.  
  35. # define    ITERTHRESH    100        /* iteration limit */
  36. # define    NPRIMS        (sizeof Macprims / sizeof Macprims[0])
  37.  
  38. /* token modes, used to compute token changes */
  39. # define    NONE        0        /* guarantees a token change */
  40. # define    ID        1        /* identifier */
  41. # define    NUMBER        2        /* number (int or float) */
  42. # define    DELIM        3        /* delimiter, guarantees a token change */
  43. # define    QUOTEMODE    4        /* quoted construct */
  44. # define    OP        5        /* operator */
  45. # define    NOCHANGE    6        /* guarantees no token change */
  46.  
  47.  
  48.  
  49. # include    "buf.h"            /* headers for buffer manip */
  50.  
  51.  
  52. /* macro definitions */
  53. struct macro
  54. {
  55.     struct macro    *nextm;        /* pointer to next macro header */
  56.     char        *template;    /* pointer to macro template */
  57.     char        *substitute;    /* pointer to substitution text */
  58. };
  59.  
  60. /* primitive declarations */
  61. struct macro    Macprims[] =
  62. {
  63.     &Macprims[1],    "{define;\020\024t;\020\024s}",                (char *) 1,
  64.     &Macprims[2],    "{rawdefine;\020\024t;\020\024s}",            (char *) 2,
  65.     &Macprims[3],    "{remove;\020\024t}",                    (char *) 3,
  66.     &Macprims[4],    "{dump}",                        (char *) 4,
  67.     &Macprims[5],    "{type\020\024m}",                    (char *) 5,
  68.     &Macprims[6],    "{read\020\024m}",                    (char *) 6,
  69.     &Macprims[7],    "{readdefine;\020\024n;\020\024m}",            (char *) 7,
  70.     &Macprims[8],    "{ifsame;\020\024a;\020\024b;\020\023t;\020\023f}",    (char *) 8,
  71.     &Macprims[9],    "{ifeq;\020\024a;\020\024b;\020\023t;\020\023f}",    (char *) 9,
  72.     &Macprims[10],    "{ifgt;\020\024a;\020\024b;\020\023t;\020\023f}",    (char *) 10,
  73.     &Macprims[11],    "{eval\020\024e}",                    (char *) 11,
  74.     &Macprims[12],    "{substr;\020\024f;\020\024t;\024s}",            (char *) 12,
  75.     &Macprims[13],    "{dnl}",                        (char *) 13,
  76.     &Macprims[14],    "{remove}",                        (char *) 3,
  77.     0,        "{dump;\020\024n}",                    (char *) 4,
  78. };
  79.  
  80. struct macro    *Machead    = &Macprims[0];    /* head of macro list */
  81.  
  82.  
  83. /* parameters */
  84. struct param
  85. {
  86.     struct param    *nextp;
  87.     char        mode;
  88.     char        name;
  89.     char        *paramt;
  90. };
  91.  
  92.  
  93.  
  94. /* the environment */
  95. struct env
  96. {
  97.     struct env    *nexte;        /* next environment */
  98.     int        (*rawget)();    /* raw character get routine */
  99.     char        **rawpar;    /* a parameter to that routine */
  100.     char        prevchar;    /* previous character read */
  101.     char        tokenmode;    /* current token mode */
  102.     char        change;        /* token change flag */
  103.     char        eof;        /* eof flag */
  104.     char        newline;    /* set if bol */
  105.     char        rawnewline;    /* same for raw input */
  106.     struct buf    *pbuf;        /* peek buffer */
  107.     struct buf    *mbuf;        /* macro buffer */
  108.     char        endtrap;    /* endtrap flag */
  109.     char        pass;        /* pass flag */
  110.     char        pdelim;        /* current parameter delimiter */
  111.     struct param    *params;    /* parameter list */
  112.     int        itercount;    /* iteration count */
  113.     int        quotelevel;    /* quote nesting level */
  114. };
  115.  
  116. /* current environment pointer */
  117. struct env    *Macenv;
  118. /*
  119. **  MACINIT -- initialize for macro processing
  120. **
  121. **    *** EXTERNAL INTERFACE ***
  122. **
  123. **    The macro processor is initialized.  Any crap left over from
  124. **    previous processing (which will never occur normally, but may
  125. **    happen on an interrupt, for instance) will be cleaned up.  The
  126. **    raw input is defined, and the 'endtrap' parameter tells whether
  127. **    this is "primary" processing or not; in other words, it tells
  128. **    whether to spring {begintrap} and {endtrap}.
  129. **
  130. **    This routine must always be called prior to any processing.
  131. */
  132.  
  133. macinit(rawget, rawpar, endtrap)
  134. int    (*rawget)();
  135. char    **rawpar;
  136. int    endtrap;
  137. {
  138.     static struct env    env;
  139.     register struct env    *e;
  140.     register struct env    *f;
  141.  
  142.     /* clear out old crap */
  143.     for (e = Macenv; e != 0; e = f)
  144.     {
  145.         bufpurge(&e->mbuf);
  146.         bufpurge(&e->pbuf);
  147.         macpflush(e);
  148.         f = e->nexte;
  149.         if (f != 0)
  150.             buffree(e);
  151.     }
  152.  
  153.     /* set up the primary environment */
  154.     Macenv = e = &env;
  155.     clrmem(e, sizeof *e);
  156.  
  157.     e->rawget = rawget;
  158.     e->rawpar = rawpar;
  159.     e->endtrap = endtrap;
  160.     e->newline = 1;
  161.  
  162.     if (endtrap)
  163.         macspring("{begintrap}");
  164. }
  165. /*
  166. **  MACGETCH -- get character after macro processing
  167. **
  168. **    *** EXTERNAL INTERFACE ROUTINE ***
  169. **
  170. **    The macro processor must have been previously initialized by a
  171. **    call to macinit().
  172. */
  173.  
  174. macgetch()
  175. {
  176.     register struct env    *e;
  177.     register int        c;
  178.  
  179.     e = Macenv;
  180.     for (;;)
  181.     {
  182.         /* get an input character */
  183.         c = macgch();
  184.  
  185.         /* check for end-of-file processing */
  186.         if (c == 0)
  187.         {
  188.             /* check to see if we should spring {endtrap} */
  189.             if (e->endtrap)
  190.             {
  191.                 e->endtrap = 0;
  192.                 macspring("{endtrap}");
  193.                 continue;
  194.             }
  195.  
  196.             /* don't spring endtrap -- real end of file */
  197.             return (0);
  198.         }
  199.  
  200.         /* not an end of file -- check for pass character through */
  201.         if (e->pass)
  202.         {
  203.             e->pass = 0;
  204.             e->change = 0;
  205.         }
  206.         if ((c & QUOTED) != 0 || !e->change || e->tokenmode == DELIM)
  207.         {
  208.             /* the character is to be passed through */
  209.             /* reset iteration count and purge macro buffer */
  210.             e->itercount = 0;
  211.             bufflush(&e->mbuf);
  212.             e->newline = (c == NEWLINE);
  213.             return (c & CHARMASK);
  214.         }
  215.  
  216.         /* this character is a candidate for macro processing */
  217.         macunget(0);
  218.         bufflush(&e->mbuf);
  219.  
  220.         /* check for infinite loop */
  221.         if (e->itercount > ITERTHRESH)
  222.         {
  223.             printf("Infinite loop in macro\n");
  224.             e->pass++;
  225.             continue;
  226.         }
  227.  
  228.         /* see if we have a macro match */
  229.         if (macallscan())
  230.         {
  231.             /* yep -- count iterations and rescan it */
  232.             e->itercount++;
  233.         }
  234.         else
  235.         {
  236.             /* nope -- pass the next token through raw */
  237.             e->pass++;
  238.         }
  239.     }
  240. }
  241. /*
  242. **  MACGCH -- get input character, knowing about tokens
  243. **
  244. **    The next input character is returned.  In addition, the quote
  245. **    level info is maintained and the QUOTED bit is set if the
  246. **    returned character is (a) quoted or (b) backslash escaped.
  247. **    As a side effect the change flag is maintained.  Also, the
  248. **    character is saved in mbuf.
  249. */
  250.  
  251. macgch()
  252. {
  253.     register int        c;
  254.     register struct env    *e;
  255.     register int        i;
  256.  
  257.     e = Macenv;
  258.  
  259.     for (;;)
  260.     {
  261.         /* get virtual raw character, save in mbuf, and set change */
  262.         c = macfetch(e->quotelevel > 0);
  263.  
  264.         /* test for magic frotz */
  265.         switch (c)
  266.         {
  267.           case 0:    /* end of file */
  268.             return (0);
  269.  
  270.           case LQUOTE:
  271.             if (e->quotelevel++ == 0)
  272.                 continue;
  273.             break;
  274.  
  275.           case RQUOTE:
  276.             if (e->quotelevel == 0)
  277.                 return (c);
  278.             if (--e->quotelevel == 0)
  279.             {
  280.                 continue;
  281.             }
  282.             break;
  283.  
  284.           case BACKSLASH:
  285.             if (e->quotelevel > 0)
  286.                 break;
  287.             c = macfetch(1);
  288.  
  289.             /* handle special cases */
  290.             if (c == e->pdelim)
  291.                 break;
  292.  
  293.             /* do translations */
  294.             switch (c)
  295.             {
  296.               case SPACE:    /* space */
  297.               case TAB:    /* tab */
  298.               case NEWLINE:    /* newline */
  299.               case RQUOTE:
  300.               case LQUOTE:
  301.               case '$':
  302.               case LBRACE:
  303.               case RBRACE:
  304.               case BACKSLASH:
  305.                 break;
  306.  
  307.               default:
  308.                 /* take character as is (unquoted) */
  309.                 c = 0;
  310.                 break;
  311.             }
  312.  
  313.             if (c != 0)
  314.                 break;
  315.  
  316.             /* not an escapable character -- treat it normally */
  317.             macunget(1);
  318.             c = BACKSLASH;
  319.             /* do default character processing on backslash */
  320.  
  321.           default:
  322.             if (e->quotelevel > 0)
  323.                 break;
  324.             return (c);
  325.         }
  326.  
  327.         /* the character is quoted */
  328.         return (c | QUOTED);
  329.     }
  330. }
  331. /*
  332. **  MACFETCH -- fetch virtual raw character
  333. **
  334. **    A character is fetched from the peek buffer.  If that buffer is
  335. **    empty, it is fetched from the raw input.  The character is then
  336. **    saved away, and the change flag is set accordingly.
  337. **    The QUOTED bit on the character is set if the 'quote' flag
  338. **    parameter is set; used for backslash escapes.
  339. **    Note that the QUOTED bit appears only on the character which
  340. **    goes into the macro buffer; the character returned is normal.
  341. */
  342.  
  343. macfetch(quote)
  344. int    quote;
  345. {
  346.     register struct env    *e;
  347.     register int        c;
  348.     register int        escapech;
  349.  
  350.     e = Macenv;
  351.     escapech = 0;
  352.  
  353.     for (;;)
  354.     {
  355.         /* get character from peek buffer */
  356.         c = bufget(&e->pbuf);
  357.  
  358.         if (c == 0)
  359.         {
  360.             /* peek buffer is empty */
  361.             /* check for already raw eof */
  362.             if (!e->eof)
  363.             {
  364.                 /* note that c must be int so that the QUOTED bit is not negative */
  365.                 c = (*e->rawget)(e->rawpar);
  366.                 if (c <= 0)
  367.                 {
  368.                     c = 0;
  369.                     e->eof++;
  370.                 }
  371.                 else
  372.                 {
  373.                     if (e->rawnewline)
  374.                         e->prevchar = NEWLINE;
  375.                     e->rawnewline = (c == NEWLINE);
  376.                 }
  377.             }
  378.         }
  379.  
  380.         /* test for escapable character */
  381.         if (escapech)
  382.         {
  383.             switch (c)
  384.             {
  385.               case 't':    /* become quoted tab */
  386.                 c = TAB | QUOTED;
  387.                 break;
  388.  
  389.               case 'n':    /* become quoted newline */
  390.                 c = NEWLINE | QUOTED;
  391.                 break;
  392.  
  393.               default:
  394.                 bufput(c, &e->pbuf);
  395.                 c = BACKSLASH;
  396.             }
  397.             escapech = 0;
  398.         }
  399.         else
  400.         {
  401.             if (c == BACKSLASH)
  402.             {
  403.                 escapech++;
  404.                 continue;
  405.             }
  406.         }
  407.         break;
  408.     }
  409.  
  410.     /* quote the character if appropriate to mask change flag */
  411.     /* ('escapech' now becomes the maybe quoted character) */
  412.     escapech = c;
  413.     if (quote && c != 0)
  414.         escapech |= QUOTED;
  415.  
  416.     /* set change flag */
  417.     macschng(escapech);
  418.  
  419.     if (c != 0)
  420.     {
  421.         /* save the character in the macro buffer */
  422.         bufput(escapech, &e->mbuf);
  423.     }
  424.  
  425.     return (c);
  426. }
  427. /*
  428. **  MACSCHNG -- set change flag and compute token type
  429. **
  430. **    The change flag and token type is set.  This does some tricky
  431. **    stuff to determine just when a new token begins.  Most notably,
  432. **    notice that quoted stuff IS scanned, but the change flag is
  433. **    reset in a higher level routine so that quoted stuff looks
  434. **    like a single token, but any begin/end quote causes a token
  435. **    change.
  436. */
  437.  
  438. macschng(ch)
  439. char    ch;
  440. {
  441.     register struct env    *e;
  442.     register char        c;
  443.     register int        thismode;
  444.     int            changeflag;
  445.  
  446.     e = Macenv;
  447.     c = ch;
  448.     changeflag = 0;
  449.     thismode = macmode(c);
  450.  
  451.     switch (e->tokenmode)
  452.     {
  453.       case NONE:
  454.         /* always cause token change */
  455.         break;
  456.  
  457.       case QUOTEMODE:
  458.         /* change only on initial entry to quotes */
  459.         break;
  460.  
  461.       case DELIM:
  462.         changeflag++;
  463.         break;
  464.  
  465.       case ID:
  466.         /* take any sequence of letters and numerals */
  467.         if (thismode == NUMBER)
  468.             thismode = ID;
  469.         break;
  470.  
  471.       case NUMBER:
  472.         /* take string of digits and decimal points */
  473.         if (c == '.')
  474.             thismode = NUMBER;
  475.         break;
  476.  
  477.       case OP:
  478.         switch (e->prevchar)
  479.         {
  480.           case '<':
  481.           case '>':
  482.           case '!':
  483.             if (c != '=')
  484.                 changeflag++;
  485.             break;
  486.  
  487.           case '*':
  488.             if (c != '*' && c != '/')
  489.                 changeflag++;
  490.             break;
  491.  
  492.           case '/':
  493.             if (c != '*')
  494.                 changeflag++;
  495.             break;
  496.  
  497.           case '.':
  498.             if (thismode == NUMBER)
  499.                 e->tokenmode = thismode;
  500.             break;
  501.  
  502.           default:
  503.             changeflag++;
  504.             break;
  505.         }
  506.         break;
  507.  
  508.       case NOCHANGE:    /* never cause token change */
  509.         e->tokenmode = thismode;
  510.         break;
  511.     }
  512.  
  513.     e->prevchar = c;
  514.     if (thismode != e->tokenmode)
  515.         changeflag++;
  516.     e->tokenmode = thismode;
  517.     e->change = changeflag;
  518. }
  519. /*
  520. **  MACMODE -- return mode of a character
  521. */
  522.  
  523. macmode(ch)
  524. char    ch;
  525. {
  526.     register char    c;
  527.  
  528.     c = ch;
  529.  
  530.     if ((c & QUOTED) != 0)
  531.         return (QUOTEMODE);
  532.     if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_'))
  533.         return (ID);
  534.     if (c >= '0' && c <= '9')
  535.         return (NUMBER);
  536.     if (c == SPACE || c == TAB || c == NEWLINE)
  537.         return (DELIM);
  538.     return (OP);
  539. }
  540. /*
  541. **  MACALLSCAN -- scan to see if input matches a macro
  542. **
  543. **    Returns true if there was a match, false if not.  In any case,
  544. **    the virtual raw input (i.e., the peek buffer) will contain
  545. **    either the old raw input, or the substituted macro.
  546. */
  547.  
  548. macallscan()
  549. {
  550.     register struct macro    *m;
  551.  
  552.     for (m = Machead; m != 0; m = m->nextm)
  553.     {
  554.         /* check to see if it matches this macro */
  555.         if (macscan(m))
  556.         {
  557.             /* it does -- substituted value is in mbuf */
  558.             macrescan();
  559.             return (1);
  560.         }
  561.  
  562.         /* it doesn't match this macro -- try the next one */
  563.         macrescan();
  564.     }
  565.  
  566.     /* it doesn't match any of them -- tough luck */
  567.     return (0);
  568. }
  569. /*
  570. **  MACSCAN -- scan a single macro for a match
  571. **
  572. **    As is scans it also collects parameters for possible future
  573. **    substitution.  If it finds a match, it takes responsibility
  574. **    for doing the substitution.
  575. */
  576.  
  577. macscan(mac)
  578. struct macro    *mac;
  579. {
  580.     register struct macro    *m;
  581.     register char        c;
  582.     register char        *temp;
  583.     char            pname, pdelim;
  584.  
  585.     m = mac;
  586.  
  587.     /* check for anchored mode */
  588.     temp = m->template;
  589.     if (*temp == ONEDELIM)
  590.     {
  591.         if (!Macenv->newline)
  592.             return (0);
  593.         temp++;
  594.     }
  595.  
  596.     /* scan the template */
  597.     for ( ; c = *temp; temp++)
  598.     {
  599.         if (c == PARAMN || c == PARAMP)
  600.         {
  601.             /* we have a parameter */
  602.             pname = *++temp;
  603.             pdelim = *++temp;
  604.             if (macparam(c, pname, pdelim))
  605.             {
  606.                 /* parameter ok */
  607.                 continue;
  608.             }
  609.  
  610.             /* failure on parameter scan */
  611.             return (0);
  612.         }
  613.  
  614.         if (!macmatch(c))
  615.         {
  616.             /* failure on literal match */
  617.             return (0);
  618.         }
  619.     }
  620.  
  621.     /* it matches!!  substitute the macro */
  622.     macsubs(m);
  623.     return (1);
  624. }
  625. /*
  626. **  MACPARAM -- collect a parameter
  627. **
  628. **    The parameter is collected and stored away "somewhere" with
  629. **    name 'name'.  The delimiter is taken to be 'delim'.  'Mode'
  630. **    tells whether to prescan the parameter (done immediately before
  631. **    substitute time to avoid side effects if the macro actually
  632. **    turns out to not match).
  633. */
  634.  
  635. macparam(mode, name, delim)
  636. char    mode;
  637. char    name;
  638. char    delim;
  639. {
  640.     register char        c;
  641.     register struct env    *e;
  642.     struct buf        *b;
  643.     register struct param    *p;
  644.     int            bracecount;
  645.     extern char        *bufalloc(),*bufcrunch();
  646.     e = Macenv;
  647.     b = 0;
  648.  
  649.     e->pdelim = delim;
  650.     TRACE("\nmacparam(%d, %c, %c):\n", mode, name, delim);
  651.     if (mode == PARAMP)
  652.     {
  653.         /* check for REALLY prescan */
  654.         c = macgch();
  655.         if (c != PRESCANENABLE)
  656.         {
  657.             mode = PARAMN;
  658.             macunget(0);
  659.         }
  660.     }
  661.  
  662.     bracecount = 0;
  663.     e->tokenmode = NOCHANGE;
  664.     while (!macmatch(delim))
  665.     {
  666.         do
  667.         {
  668.             c = macgch();
  669.             if (c == 0 || c == NEWLINE)
  670.             {
  671.                 e->pdelim = 0;
  672.                 bufpurge(&b);
  673.                 TRACE("macparam fails\n");
  674.                 return (0);
  675.             }
  676.             bufput(c, &b);
  677.             if (c == LBRACE)
  678.                 bracecount++;
  679.             else if (c == RBRACE && bracecount > 0)
  680.                 bracecount--;
  681.         } while (bracecount > 0);
  682.     }
  683.  
  684.     e->pdelim = 0;
  685.  
  686.     /* allocate and store the parameter */
  687.     p = (struct param *) bufalloc(sizeof *p);
  688.     p->mode = mode;
  689.     p->name = name;
  690.     p->nextp = e->params;
  691.     e->params = p;
  692.     p->paramt = bufcrunch(&b);
  693.     bufpurge(&b);
  694.     TRACE("macparam: |%s|\n", p->paramt);
  695.  
  696.     return (1);
  697. }
  698. /*
  699. **  MACMATCH -- test for a match between template character and input.
  700. **
  701. **    The parameter is the character from the template to match on.
  702. **    The input is read.  The template character may be a meta-
  703. **    character.  In all cases if the match occurs the input is
  704. **    thrown away; if no match occurs the input is left unchanged.
  705. **
  706. **    Return value is true for a match, false for no match.
  707. */
  708.  
  709. macmatch(template)
  710. char    template;
  711. {
  712.     register char    t;
  713.     register char    c;
  714.     register int    res;
  715.  
  716.     t = template;
  717.     TRACE("\tmacmatch(%c)", t);
  718.  
  719.     switch (t)
  720.     {
  721.       case ANYDELIM:    /* match zero or more delimiters */
  722.         /* chew and chuck delimiters */
  723.         while (macdelim())
  724.             ;
  725.  
  726.         /* as a side effect, must match a token change */
  727.         if (!macckch())
  728.         {
  729.             TRACE(" fail\n");
  730.             return (0);
  731.         }
  732.         TRACE(" succeed\n");
  733.         return (1);
  734.  
  735.       case ONEDELIM:    /* match exactly one delimiter */
  736.         TRACE(":\n");
  737.         res = macdelim();
  738.         return (res);
  739.  
  740.       case CHANGE:        /* match a token change */
  741.       case 0:        /* end of template */
  742.         TRACE(":\n");
  743.         res = macckch();
  744.         return (res);
  745.  
  746.       default:        /* must have exact character match */
  747.         c = macgch();
  748.         TRACE(" against %c ", c);
  749.         if (c == t)
  750.         {
  751.             TRACE("succeed\n");
  752.             return (1);
  753.         }
  754.  
  755.         /* failure */
  756.         macunget(0);
  757.         TRACE("fail\n");
  758.         return (0);
  759.     }
  760. }
  761. /*
  762. **  MACDELIM -- test for next input character a delimiter
  763. **
  764. **    Returns true if the next input character is a delimiter, false
  765. **    otherwise.  Delimiters are chewed.
  766. */
  767.  
  768. macdelim()
  769. {
  770.     register char    c;
  771.  
  772.     c = macgch();
  773.     TRACE("\t\tmacdelim against %c: ", c);
  774.     if (macmode(c) == DELIM)
  775.     {
  776.         TRACE("succeed\n");
  777.         return (1);
  778.     }
  779.     macunget(0);
  780.     TRACE("fail\n");
  781.     return (0);
  782. }
  783. /*
  784. **  MACCKCH -- check for token change
  785. **
  786. **    Returns true if a token change occurs between this and the next
  787. **    character.  No characters are ever chewed, however, the token
  788. **    change (if it exists) is always chewed.
  789. */
  790.  
  791. macckch()
  792. {
  793.     register int        change;
  794.     register char        c;
  795.     register struct env    *e;
  796.  
  797.     e = Macenv;
  798.  
  799.     if (e->tokenmode == NONE)
  800.     {
  801.         /* then last character has been ungotten: take old change */
  802.         change = e->change;
  803.     }
  804.     else
  805.     {
  806.         c = macgch();
  807.         change = Macenv->change;
  808.         macunget(0);
  809.     }
  810.     TRACE("macckch got %c ret %d\n", c, change);
  811.  
  812.     /* chew the change and return */
  813.     e->tokenmode = NOCHANGE;
  814.     return (change);
  815. }
  816. /*
  817. **  MACSUBS -- substitute in macro substitution
  818. **
  819. **    This routine prescans appropriate parameters and then either
  820. **    loads the substitution into the macro buffer or calls the
  821. **    correct primitive routine.
  822. */
  823.  
  824. macsubs(mac)
  825. struct macro    *mac;
  826. {
  827.     register struct param    *p;
  828.     register struct env    *e;
  829.     register char        *s;
  830.     char            *macprim();
  831.  
  832.     e = Macenv;
  833.  
  834.     for (p = e->params; p != 0; p = p->nextp)
  835.     {
  836.         /* check to see if we should prescan */
  837.         if (p->mode != PARAMP)
  838.         {
  839.             continue;
  840.         }
  841.  
  842.         /* prescan parameter */
  843.         macprescan(&p->paramt);
  844.         p->mode = PARAMN;
  845.     }
  846.  
  847.     s = mac->substitute;
  848.  
  849.     /* clear out the macro call */
  850.     bufflush(&e->mbuf);
  851.  
  852.     if (s <= (char *) NPRIMS)
  853.     {
  854.         /* it is a primitive */
  855.         macload(macprim(s), 0);
  856.     }
  857.     else
  858.     {
  859.         /* it is a user-defined macro */
  860.         macload(s, 1);
  861.     }
  862. }
  863. /*
  864. **  MACPRESCAN -- prescan a parameter
  865. **
  866. **    The parameter pointed to by 'pp' is fed once through the macro
  867. **    processor and replaced with the new version.
  868. */
  869.  
  870. macprescan(pp)
  871. char    **pp;
  872. {
  873.     struct buf        *b;
  874.     char            *p;
  875.     register struct env    *e;
  876.     register char        c;
  877.     extern int        macsget();
  878.  
  879.     b = 0;
  880.     p = *pp;
  881.  
  882.     /* set up a new environment */
  883.     macnewev(macsget, &p);
  884.     e = Macenv;
  885.  
  886.     /* scan the parameter */
  887.     while ((c = macgetch()) != 0)
  888.         bufput(c, &b);
  889.  
  890.     /* free the old parameter */
  891.     buffree(*pp);
  892.  
  893.     /* move in the new one */
  894.     *pp = bufcrunch(&b);
  895.     bufpurge(&b);
  896.  
  897.     /* restore the old environment */
  898.     macpopev();
  899. }
  900. /*
  901. **  MACNEWEV -- set up new environment
  902. **
  903. **    Parameters are raw get routine and parameter
  904. */
  905.  
  906. macnewev(rawget, rawpar)
  907. int    (*rawget)();
  908. char    **rawpar;
  909. {
  910.     register struct env    *e;
  911.     extern char        *bufalloc();
  912.  
  913.     e = (struct env *) bufalloc(sizeof *e);
  914.     e->rawget = rawget;
  915.     e->rawpar = rawpar;
  916.     e->nexte = Macenv;
  917.     e->newline = 1;
  918.     Macenv = e;
  919. }
  920. /*
  921. **  MACPOPEV -- pop an environment
  922. **
  923. **    Makes sure all buffers and stuff are purged
  924. */
  925.  
  926. macpopev()
  927. {
  928.     register struct env    *e;
  929.  
  930.     e = Macenv;
  931.     bufpurge(&e->mbuf);
  932.     bufpurge(&e->pbuf);
  933.     macpflush(e);
  934.     Macenv = e->nexte;
  935.     buffree(e);
  936. }
  937. /*
  938. **  MACPFLUSH -- flush all parameters
  939. **
  940. **    Used to deallocate all parameters in a given environment.
  941. */
  942.  
  943. macpflush(env)
  944. struct env    *env;
  945. {
  946.     register struct env    *e;
  947.     register struct param    *p;
  948.     register struct param    *q;
  949.  
  950.     e = env;
  951.  
  952.     for (p = e->params; p != 0; p = q)
  953.     {
  954.         buffree(p->paramt);
  955.         q = p->nextp;
  956.         buffree(p);
  957.     }
  958.  
  959.     e->params = 0;
  960. }
  961. /*
  962. **  MACSGET -- get from string
  963. **
  964. **    Works like a getchar from a string.  Used by macprescan().
  965. **    The parameter is a pointer to the string.
  966. */
  967.  
  968. macsget(pp)
  969. char    **pp;
  970. {
  971.     register char    **p;
  972.     register int    c;
  973.  
  974.     p = pp;
  975.  
  976.     c = **p & BYTEMASK;
  977.     if (c != 0)
  978.         (*p)++;
  979.     return (c);
  980. }
  981. /*
  982. **  MACLOAD -- load a string into the macro buffer
  983. **
  984. **    The parameters are a pointer to a string to be appended to
  985. **    the macro buffer and a flag telling whether parameter substi-
  986. **    tution can occur.
  987. */
  988.  
  989. macload(str, flag)
  990. char    *str;
  991. int    flag;
  992. {
  993.     register struct env    *e;
  994.     register char        *s;
  995.     register char        c;
  996.     extern char        *macplkup();
  997.  
  998.     e = Macenv;
  999.     s = str;
  1000.  
  1001.     if (s == 0)
  1002.         return;
  1003.  
  1004.     while ((c = *s++) != 0)
  1005.     {
  1006.         if (c == PARAMN)
  1007.             macload(macplkup(*s++), 0);
  1008.         else
  1009.             bufput(c & CHARMASK, &e->mbuf);
  1010.     }
  1011. }
  1012. /*
  1013. **  MACRESCAN -- rescan the macro buffer
  1014. **
  1015. **    Copies the macro buffer into the peek buffer so that it will be
  1016. **    reread.  Also deallocates any parameters which may happen to be
  1017. **    stored away.
  1018. */
  1019.  
  1020. macrescan()
  1021. {
  1022.     register struct env    *e;
  1023.     register char        c;
  1024.  
  1025.     e = Macenv;
  1026.  
  1027.     while ((c = bufget(&e->mbuf) & CHARMASK) != 0)
  1028.         bufput(c, &e->pbuf);
  1029.  
  1030.     e->quotelevel = 0;
  1031.     e->tokenmode = NONE;
  1032.     macpflush(e);
  1033. }
  1034. /*
  1035. **  MACUNGET -- unget a character
  1036. **
  1037. **    Moves one character from the macro buffer to the peek buffer.
  1038. **    If 'mask' is set, the character has the quote bit stripped off.
  1039. */
  1040.  
  1041. macunget(mask)
  1042. int    mask;
  1043. {
  1044.     register struct env    *e;
  1045.     register char        c;
  1046.  
  1047.     e = Macenv;
  1048.  
  1049.     if (e->prevchar != 0)
  1050.     {
  1051.         c = bufget(&e->mbuf);
  1052.         if (mask)
  1053.              c &= CHARMASK;
  1054.         bufput(c, &e->pbuf);
  1055.         e->tokenmode = NONE;
  1056.     }
  1057. }
  1058. /*
  1059. **  MACPLKUP -- look up parameter
  1060. **
  1061. **    Returns a pointer to the named parameter.  Returns null
  1062. **    if the parameter is not found ("cannot happen").
  1063. */
  1064.  
  1065. char *
  1066. macplkup(name)
  1067. char    name;
  1068. {
  1069.     register struct param    *p;
  1070.  
  1071.     for (p = Macenv->params; p != 0; p = p->nextp)
  1072.     {
  1073.         if (p->name == name)
  1074.             return (p->paramt);
  1075.     }
  1076.  
  1077.     return (0);
  1078. }
  1079. /*
  1080. **  MACSPRING -- spring a trap
  1081. **
  1082. **    The named trap is sprung, in other words, if the named macro is
  1083. **    defined it is called, otherwise there is no replacement text.
  1084. */
  1085.  
  1086. macspring(trap)
  1087. char    *trap;
  1088. {
  1089.     register struct env    *e;
  1090.     register char        *p;
  1091.     char            *macro();
  1092.  
  1093.     e = Macenv;
  1094.  
  1095.     bufflush(&e->mbuf);
  1096.  
  1097.     /* fetch the macro */
  1098.     p = macro(trap);
  1099.  
  1100.     /* if not defined, don't bother */
  1101.     if (p == 0)
  1102.         return;
  1103.  
  1104.     /* load the trap */
  1105.     macload(p);
  1106.  
  1107.     /* insert a newline after the trap */
  1108.     bufput('\n', &e->mbuf);
  1109.  
  1110.     macrescan();
  1111. }
  1112. /*
  1113. **  MACPRIM -- do primitives
  1114. **
  1115. **    The parameter is the primitive to execute.
  1116. */
  1117.  
  1118. char *
  1119. macprim(n)
  1120. int    n;
  1121. {
  1122.     register struct env    *e;
  1123.     extern char        *macplkup();
  1124.     extern char        *macsstr();
  1125.  
  1126.     e = Macenv;
  1127.  
  1128.     switch (n)
  1129.     {
  1130.       case 1:    /* {define; $t; $s} */
  1131.         macdnl();
  1132.         macdefine(macplkup('t'), macplkup('s'), 0);
  1133.         break;
  1134.  
  1135.       case 2:    /* {rawdefine; $t; $s} */
  1136.         macdnl();
  1137.         macdefine(macplkup('t'), macplkup('s'), 1);
  1138.         break;
  1139.  
  1140.       case 3:    /* {remove $t} */
  1141.         macdnl();
  1142.         macremove(macplkup('t'));
  1143.         break;
  1144.  
  1145.       case 4:    /* {dump} */
  1146.             /* {dump; $n} */
  1147.         macdnl();
  1148.         macdump(macplkup('n'));
  1149.         break;
  1150.  
  1151.       case 5:    /* {type $m} */
  1152.         macdnl();
  1153.         printf("%s\n", macplkup('m'));
  1154.         break;
  1155.  
  1156.       case 6:    /* {read $m} */
  1157.         printf("%s ", macplkup('m'));
  1158.         macread();
  1159.         break;
  1160.       
  1161.       case 7:    /* {read; $n; $m} */
  1162.         printf("%s ", macplkup('m'));
  1163.         macread();
  1164.         macdefine(macplkup('n'), bufcrunch(&e->mbuf), 1);
  1165.         return("{readcount}");
  1166.  
  1167.       case 8:    /* {ifsame; $a; $b; $t; $f} */
  1168.         if (sequal(macplkup('a'), macplkup('b')))
  1169.             return (macplkup('t'));
  1170.         else
  1171.             return (macplkup('f'));
  1172.  
  1173.       case 9:    /* {ifeq; $a; $b; $t; $f} */
  1174.         if (macnumber(macplkup('a')) == macnumber(macplkup('b')))
  1175.             return (macplkup('t'));
  1176.         else
  1177.             return (macplkup('f'));
  1178.  
  1179.       case 10:    /* {ifgt; $a; $b; $t; $f} */
  1180.         if (macnumber(macplkup('a')) > macnumber(macplkup('b')))
  1181.             return (macplkup('t'));
  1182.         else
  1183.             return (macplkup('f'));
  1184.       
  1185.       case 12:    /* {substr; $f; $t; $s} */
  1186.         return (macsstr(macnumber(macplkup('f')), macnumber(macplkup('t')), macplkup('s')));
  1187.  
  1188.       case 13:    /* {dnl} */
  1189.         macdnl();
  1190.         break;
  1191.  
  1192.       default:
  1193.         syserr("macro: bad primitive %d", n);
  1194.     }
  1195.  
  1196.     return ("");
  1197. }
  1198. /*
  1199. **  MACDNL -- delete to newline
  1200. **
  1201. **    Used in general after macro definitions to avoid embarrassing
  1202. **    newlines.  Just reads input until a newline character, and
  1203. **    then throws it away.
  1204. */
  1205.  
  1206. macdnl()
  1207. {
  1208.     register char        c;
  1209.     register struct env    *e;
  1210.  
  1211.     e = Macenv;
  1212.  
  1213.     while ((c = macgch()) != 0 && c != NEWLINE)
  1214.         ;
  1215.  
  1216.     bufflush(&e->mbuf);
  1217. }
  1218. /*
  1219. **  MACDEFINE -- define primitive
  1220. **
  1221. **    This function defines a macro.  The parameters are the
  1222. **    template, the substitution string, and a flag telling whether
  1223. **    this is a raw define or not.  Syntax checking is done.
  1224. */
  1225.  
  1226. macdefine(template, subs, raw)
  1227. char    *template;
  1228. char    *subs;
  1229. int    raw;
  1230. {
  1231.     register struct env    *e;
  1232.     char            paramdefined[128];
  1233.     char            *p;
  1234.     register char        c;
  1235.     char            d;
  1236.     struct buf        *b;
  1237.     register struct macro    *m;
  1238.     extern int        macsget();
  1239.     extern char        *bufalloc(),*bufcrunch();
  1240.     char            *mactcvt();
  1241.     int            escapech;
  1242.  
  1243.     /* remove any old macro definition */
  1244.     macremove(template);
  1245.  
  1246.     /* get a new environment */
  1247.     macnewev(macsget, &p);
  1248.     b = 0;
  1249.     e = Macenv;
  1250.  
  1251.     /* undefine all parameters */
  1252.     clrmem(paramdefined, 128);
  1253.  
  1254.     /* avoid an initial token change */
  1255.     e->tokenmode = NOCHANGE;
  1256.     escapech = 1;
  1257.  
  1258.     /* allocate macro header and template */
  1259.     m = (struct macro *) bufalloc(sizeof *m);
  1260.  
  1261.     /* scan and convert template, collect available parameters */
  1262.     p = template;
  1263.     m->template = mactcvt(raw, paramdefined);
  1264.     if (m->template == 0)
  1265.     {
  1266.         /* some sort of syntax error */
  1267.         buffree(m);
  1268.         macpopev();
  1269.         return;
  1270.     }
  1271.  
  1272.     bufflush(&e->mbuf);
  1273.     bufflush(&e->pbuf);
  1274.     e->eof = 0;
  1275.  
  1276.     /* scan substitute string */
  1277.     for (p = subs; c = macfetch(0); )
  1278.     {
  1279.         if (c != '$')
  1280.         {
  1281.             /* substitute non-parameters literally */
  1282.             bufput(c & CHARMASK, &b);
  1283.             continue;
  1284.         }
  1285.  
  1286.         /* it's a parameter */
  1287.         bufput(PARAMN, &b);
  1288.         c = macfetch(0);
  1289.  
  1290.         /* check to see if name is supplied */
  1291.         if (paramdefined[c] == 0)
  1292.         {
  1293.             /* nope, it's not */
  1294.             printf("define: parameter %c referenced but not defined\n", c);
  1295.             buffree(m->template);
  1296.             buffree(m);
  1297.             macpopev();
  1298.             bufpurge(&b);
  1299.             return;
  1300.         }
  1301.         bufput(c & CHARMASK, &b);
  1302.     }
  1303.  
  1304.     /* allocate substitution string */
  1305.     m->substitute = bufcrunch(&b);
  1306.  
  1307.     /* allocate it as a macro */
  1308.     m->nextm = Machead;
  1309.     Machead = m;
  1310.  
  1311.     /* finished... */
  1312.     macpopev();
  1313.     bufpurge(&b);
  1314. }
  1315. /*
  1316. **  MACTCVT -- convert template to internal form
  1317. **
  1318. **    Converts the template from external form to internal form.
  1319. **
  1320. **    Parameters:
  1321. **    raw -- set if only raw type conversion should take place.
  1322. **    paramdefined -- a map of flags to determine declaration of
  1323. **        parameters, etc.  If zero, no parameters are allowed.
  1324. **
  1325. **    Return value:
  1326. **    A character pointer off into mystic space.
  1327. **
  1328. **    The characters of the template are read using macfetch, so
  1329. **    a new environment should be created which will arrange to
  1330. **    get this.
  1331. */
  1332.  
  1333. char *
  1334. mactcvt(raw, paramdefined)
  1335. int    raw;
  1336. char    paramdefined[128];
  1337. {
  1338.     register int        c;
  1339.     struct buf        *b;
  1340.     register char        d;
  1341.     register int        escapech;
  1342.     char            *p;
  1343.  
  1344.     b = 0;
  1345.     escapech = 1;
  1346.  
  1347.     while (c = macfetch(0))
  1348.     {
  1349.         switch (c)
  1350.         {
  1351.           case '$':        /* parameter */
  1352.             if (escapech < 0)
  1353.             {
  1354.                 printf("define: every parameter needs a delimiter\n");
  1355.                 bufpurge(&b);
  1356.                 return (0);
  1357.             }
  1358.  
  1359.             /* skip delimiters before parameter in non-raw */
  1360.             if (Macenv->change && !escapech && !raw)
  1361.                 bufput(ANYDELIM, &b);
  1362.  
  1363.             escapech = 0;
  1364.             c = macfetch(0);
  1365.             d = PARAMN;
  1366.             if (c == '$')
  1367.             {
  1368.                 /* prescanned parameter */
  1369.                 d = PARAMP;
  1370.                 c = macfetch(0);
  1371.             }
  1372.  
  1373.             /* process parameter name */
  1374.             if (c == 0)
  1375.             {
  1376.                 /* no parameter name */
  1377.                 printf("define: null parameter name\n");
  1378.                 bufpurge(&b);
  1379.                 return (0);
  1380.             }
  1381.  
  1382.             bufput(d, &b);
  1383.             escapech = -1;
  1384.  
  1385.             /* check for legal parameter */
  1386.             if (paramdefined == 0)
  1387.                 break;
  1388.  
  1389.             if (paramdefined[c])
  1390.             {
  1391.                 printf("define: parameter %c redeclared\n", c);
  1392.                 bufpurge(&b);
  1393.                 return (0);
  1394.             }
  1395.             paramdefined[c]++;
  1396.  
  1397.             /* get parameter delimiter */
  1398.             break;
  1399.  
  1400.           case BACKSLASH:        /* a backslash escape */
  1401.             escapech = 1;
  1402.             c = macfetch(0);
  1403.             switch (c)
  1404.             {
  1405.               case '|':
  1406.                 c = ANYDELIM;
  1407.                 break;
  1408.  
  1409.               case '^':
  1410.                 c = ONEDELIM;
  1411.                 break;
  1412.  
  1413.               case '&':
  1414.                 c = CHANGE;
  1415.                 break;
  1416.  
  1417.               default:
  1418.                 escapech = 0;
  1419.                 c = BACKSLASH;
  1420.                 macunget(0);
  1421.                 break;
  1422.             }
  1423.             break;
  1424.  
  1425.           case NEWLINE | QUOTED:
  1426.           case TAB | QUOTED:
  1427.           case SPACE | QUOTED:
  1428.             if (escapech < 0)
  1429.                 c &= CHARMASK;
  1430.             escapech = 1;
  1431.             break;
  1432.  
  1433.           default:
  1434.             /* change delimiters to ANYDELIM */
  1435.             if (macmode(c) == DELIM && !raw)
  1436.             {
  1437.                 while (macmode(c = macfetch(0)) == DELIM)
  1438.                     ;
  1439.                 macunget(0);
  1440.                 if (c == 0)
  1441.                     c = ONEDELIM;
  1442.                 else
  1443.                     c = ANYDELIM;
  1444.                 escapech = 1;
  1445.             }
  1446.             else
  1447.             {
  1448.                 if (Macenv->change && !escapech)
  1449.                 {
  1450.                     bufput(ANYDELIM, &b);
  1451.                 }
  1452.  
  1453.                 if (escapech < 0)
  1454.                 {
  1455.                     /* parameter: don't allow quoted delimiters */
  1456.                     c &= CHARMASK;
  1457.                 }
  1458.                 escapech = 0;
  1459.             }
  1460.             break;
  1461.         }
  1462.         bufput(c, &b);
  1463.     }
  1464.     if (escapech <= 0)
  1465.         bufput(CHANGE, &b);
  1466.  
  1467.     p = bufcrunch(&b);
  1468.     bufpurge(&b);
  1469.     TRACE("mactcvt: '%s'\n", p);
  1470.     return (p);
  1471. }
  1472. /*
  1473. **  MACREMOVE -- remove macro
  1474. **
  1475. **    The named macro is looked up.  If it is found it is removed
  1476. **    from the macro list.
  1477. */
  1478.  
  1479. macremove(name)
  1480. char    *name;
  1481. {
  1482.     register struct macro    *m;
  1483.     register struct macro    **mp;
  1484.     extern int        macsget();
  1485.     char            *p;
  1486.     register char        *cname;
  1487.     struct macro        *macmlkup();
  1488.  
  1489.     if (name != 0)
  1490.     {
  1491.         /* convert name to internal format */
  1492.         macnewev(macsget, &p);
  1493.         p = name;
  1494.         cname = mactcvt(0, 0);
  1495.         macpopev();
  1496.         if (cname == 0)
  1497.         {
  1498.             /* some sort of syntax error */
  1499.             return;
  1500.         }
  1501.     }
  1502.  
  1503.     /* find macro */
  1504.     while (name == 0 ? ((m = Machead)->substitute > (char *) NPRIMS) : ((m = macmlkup(cname)) != 0))
  1505.     {
  1506.         /* remove macro from list */
  1507.         mp = &Machead;
  1508.  
  1509.         /* find it's parent */
  1510.         while (*mp != m)
  1511.             mp = &(*mp)->nextm;
  1512.  
  1513.         /* remove macro from list */
  1514.         *mp = m->nextm;
  1515.         buffree(m->template);
  1516.         buffree(m->substitute);
  1517.         buffree(m);
  1518.     }
  1519.     buffree(cname);
  1520. }
  1521. /*
  1522. **  MACMLKUP -- look up macro
  1523. **
  1524. **    The named macro is looked up and a pointer to the macro header
  1525. **    is returned.  Zero is returned if the macro is not found.
  1526. **    The name must be in internal form.
  1527. */
  1528.  
  1529. struct macro *
  1530. macmlkup(name)
  1531. char    *name;
  1532. {
  1533.     register struct macro    *m;
  1534.     register char        *n;
  1535.  
  1536.     n = name;
  1537.  
  1538.     /* scan the macro list for it */
  1539.     for (m = Machead; m != 0; m = m->nextm)
  1540.     {
  1541.         if (macmmatch(n, m->template, 0))
  1542.             return (m);
  1543.     }
  1544.     return (0);
  1545. }
  1546. /*
  1547. **  MACMMATCH -- check for macro name match
  1548. **
  1549. **    The given 'name' and 'temp' are compared for equality.  If they
  1550. **    match true is returned, else false.
  1551. **    Both must be converted to internal format before the call is
  1552. **    given.
  1553. **
  1554. **    "Match" is defined as two macros which might scan as equal.
  1555. **
  1556. **    'Flag' is set to indicate that the macros must match exactly,
  1557. **    that is, neither may have any parameters and must end with both
  1558. **    at end-of-template.  This mode is used for getting traps and
  1559. **    such.
  1560. */
  1561.  
  1562. macmmatch(name, temp, flag)
  1563. char    *name;
  1564. char    *temp;
  1565. int    flag;
  1566. {
  1567.     register char    ac;
  1568.     register char    bc;
  1569.     char        *ap, *bp;
  1570.  
  1571.     ap = name;
  1572.     bp = temp;
  1573.  
  1574.     /* scan character by character */
  1575.     for (;; ap++, bp++)
  1576.     {
  1577.         ac = *ap;
  1578.         bc = *bp;
  1579.         TRACE("macmmatch: ac=%c/%u, bc=%c/%u\n", ac, ap, bc, bp);
  1580.  
  1581.         if (bc == ANYDELIM)
  1582.         {
  1583.             if (macmmchew(&ap))
  1584.                 continue;
  1585.         }
  1586.         else
  1587.         {
  1588.             switch (ac)
  1589.             {
  1590.               case SPACE:
  1591.               case NEWLINE:
  1592.               case TAB:
  1593.                 if (ac == bc || bc == ONEDELIM)
  1594.                     continue;
  1595.                 break;
  1596.  
  1597.               case ONEDELIM:
  1598.                 if (ac == bc || macmode(bc) == DELIM)
  1599.                     continue;
  1600.                 break;
  1601.  
  1602.               case ANYDELIM:
  1603.                 if (macmmchew(&bp))
  1604.                     continue;
  1605.                 break;
  1606.  
  1607.               case PARAMP:
  1608.               case PARAMN:
  1609.               case 0:
  1610.                 if (bc == PARAMN || bc == PARAMP || bc == 0 ||
  1611.                     bc == ANYDELIM || bc == ONEDELIM ||
  1612.                     bc == CHANGE || macmode(bc) == DELIM)
  1613.                 {
  1614.                     /* success */
  1615.                     if (!flag)
  1616.                         return (1);
  1617.                     if (ac == 0 && bc == 0)
  1618.                         return (1);
  1619.                 }
  1620.                 break;
  1621.  
  1622.               default:
  1623.                 if (ac == bc)
  1624.                     continue;
  1625.                 break;
  1626.             }
  1627.         }
  1628.  
  1629.         /* failure */
  1630.         return (0);
  1631.     }
  1632. }
  1633. /*
  1634. **  MACMMCHEW -- chew nonspecific match characters
  1635. **
  1636. **    The pointer passed as parameter is scanned so as to skip over
  1637. **    delimiters and pseudocharacters.
  1638. **    At least one character must match.
  1639. */
  1640.  
  1641. macmmchew(pp)
  1642. char    **pp;
  1643. {
  1644.     register char    *p;
  1645.     register char    c;
  1646.     register int    matchflag;
  1647.  
  1648.     p = *pp;
  1649.  
  1650.     for (matchflag = 0; ; matchflag++)
  1651.     {
  1652.         c = *p;
  1653.         if (c != ANYDELIM && c != ONEDELIM && c != CHANGE &&
  1654.             macmode(c) != DELIM)
  1655.             break;
  1656.         p++;
  1657.     }
  1658.  
  1659.     p--;
  1660.     if (matchflag == 0)
  1661.         return (0);
  1662.     *pp = p;
  1663.     return (1);
  1664. }
  1665. /*
  1666. **  MACREAD -- read a terminal input line
  1667. **
  1668. **    Reads one line from the user.  Returns the line into mbuf,
  1669. **    and a count of the number of characters read into the macro
  1670. **    "{readcount}" (-1 for end of file).
  1671. */
  1672.  
  1673. macread()
  1674. {
  1675.     register struct env    *e;
  1676.     register int        count;
  1677.     register char        c;
  1678.  
  1679.     e = Macenv;
  1680.     count = -1;
  1681.  
  1682.     while ((c = getchar()) > 0)
  1683.     {
  1684.         count++;
  1685.         if (c == NEWLINE)
  1686.             break;
  1687.         bufput(c, &e->mbuf);
  1688.     }
  1689.  
  1690.     macdefine("{readcount}", iocv(count), 1);
  1691. }
  1692. /*
  1693. **  MACNUMBER -- return converted number
  1694. **
  1695. **    This procedure is essentially identical to the system atoi
  1696. **    routine, in that it does no syntax checking whatsoever.
  1697. */
  1698.  
  1699. macnumber(s)
  1700. char    *s;
  1701. {
  1702.     register char        *p;
  1703.     register char        c;
  1704.     register int        result;
  1705.     int            minus;
  1706.  
  1707.     result = 0;
  1708.     p = s;
  1709.     minus = 0;
  1710.  
  1711.     while ((c = *p++) == SPACE)
  1712.         ;
  1713.  
  1714.     if (c == '-')
  1715.     {
  1716.         minus++;
  1717.         while ((c = *p++) == SPACE)
  1718.             ;
  1719.     }
  1720.  
  1721.     while (c >= '0' && c <= '9')
  1722.     {
  1723.         result = result * 10 + (c - '0');
  1724.         c = *p++;
  1725.     }
  1726.  
  1727.     if (minus)
  1728.         result = -result;
  1729.  
  1730.     return (result);
  1731. }
  1732. /*
  1733. **  MACSUBSTR -- substring primitive
  1734. **
  1735. **    The substring of 'string' from 'from' to 'to' is extracted.
  1736. **    A pointer to the result is returned.  Note that macsstr
  1737. **    in the general case modifies 'string' in place.
  1738. */
  1739.  
  1740. char *
  1741. macsstr(from, to, string)
  1742. int    from;
  1743. int    to;
  1744. char    *string;
  1745. {
  1746.     register int    f;
  1747.     int        l;
  1748.     register char    *s;
  1749.     register int    t;
  1750.  
  1751.     s = string;
  1752.     t = to;
  1753.     f = from;
  1754.  
  1755.     if (f < 1)
  1756.         f = 1;
  1757.  
  1758.     if (f >= t)
  1759.         return ("");
  1760.     l = length(s);
  1761.     if (t < l)
  1762.         s[t] = 0;
  1763.     return (&s[f - 1]);
  1764. }
  1765. /*
  1766. **  MACDUMP -- dump a macro definition to the terminal
  1767. **
  1768. **    All macros matching 'name' are output to the buffer.  If
  1769. **    'name' is the null pointer, all macros are printed.
  1770. */
  1771.  
  1772. macdump(name)
  1773. char    *name;
  1774. {
  1775.     register struct macro    *m;
  1776.     register char        *p;
  1777.     register char        *n;
  1778.     extern int        macsget();
  1779.     extern char        *macmocv();
  1780.     char            *ptr;
  1781.  
  1782.     n = name;
  1783.     if (n != 0)
  1784.     {
  1785.         macnewev(macsget, &ptr);
  1786.         ptr = n;
  1787.         n = mactcvt(0, 0);
  1788.         macpopev();
  1789.         if (n == 0)
  1790.             return;
  1791.     }
  1792.  
  1793.     for (m = Machead; m != 0; m = m->nextm)
  1794.     {
  1795.         if (n == 0 || macmmatch(n, m->template, 0))
  1796.         {
  1797.             if (m->substitute <= (char *) NPRIMS)
  1798.                 continue;
  1799.             p = macmocv(m->template);
  1800.             macload("`{rawdefine; ", 0);
  1801.             macload(p, 0);
  1802.             macload("; ", 0);
  1803.             p = macmocv(m->substitute);
  1804.             macload(p, 0);
  1805.             macload("}'\n", 0);
  1806.         }
  1807.     }
  1808.     if (n != 0)
  1809.         buffree(n);
  1810. }
  1811. /*
  1812. **  MACMOCV -- macro output conversion
  1813. **
  1814. **    This routine converts the internal format of the named macro
  1815. **    to an unambigous external representation.
  1816. **
  1817. **    Note that much work can be done to this routine to make it
  1818. **    produce cleaner output, for example, translate "\|" to " "
  1819. **    in most cases.
  1820. */
  1821.  
  1822. char *
  1823. macmocv(m)
  1824. char    *m;
  1825. {
  1826.     register char    *p;
  1827.     struct buf    *b;
  1828.     register int    c;
  1829.     register int    pc;
  1830.     static char    *lastbuf;
  1831.     extern char    *bufcrunch();
  1832.  
  1833.     p = m;
  1834.  
  1835.     /* release last used buffer (as appropriate) */
  1836.     if (lastbuf != 0)
  1837.     {
  1838.         buffree(lastbuf);
  1839.         lastbuf = 0;
  1840.     }
  1841.  
  1842.     if (p <= (char *) NPRIMS)
  1843.     {
  1844.         /* we have a primitive */
  1845.         p = "Primitive xxx";
  1846.         itoa(m, &p[10]);
  1847.         return (p);
  1848.     }
  1849.  
  1850.     b = 0;
  1851.  
  1852.     for (; (c = *p++) != 0; pc = c)
  1853.     {
  1854.         switch (c)
  1855.         {
  1856.           case BACKSLASH:
  1857.           case '|':
  1858.           case '&':
  1859.           case '^':
  1860.             break;
  1861.  
  1862.           case ANYDELIM:
  1863.             c = '\\|';
  1864.             break;
  1865.  
  1866.           case ONEDELIM:
  1867.             c = '\\^';
  1868.             break;
  1869.  
  1870.           case CHANGE:
  1871.             c = '\\&';
  1872.             break;
  1873.  
  1874.           case PARAMN:
  1875.             c = '$';
  1876.             break;
  1877.  
  1878.           case PARAMP:
  1879.             c = '$$';
  1880.             break;
  1881.  
  1882.           case '$':
  1883.             c = '\\$';
  1884.             break;
  1885.  
  1886.           case NEWLINE:
  1887.             c = ('\\' | QUOTED) | ('\n' << 8);
  1888.             break;
  1889.  
  1890.           default:
  1891.             bufput(c, &b);
  1892.             continue;
  1893.         }
  1894.  
  1895.         if (pc == BACKSLASH)
  1896.             bufput(pc, &b);
  1897.         pc = c & CHARMASK;
  1898.         bufput(pc, &b);
  1899.         pc = (c >> 8) & CHARMASK;
  1900.         if (pc != 0)
  1901.         {
  1902.             c = pc;
  1903.             bufput(c, &b);
  1904.         }
  1905.     }
  1906.  
  1907.     p = bufcrunch(&b);
  1908.     bufpurge(&b);
  1909.     lastbuf = p;
  1910.     return (p);
  1911. }
  1912. /*
  1913. **  MACRO -- get macro substitution value
  1914. **
  1915. **    ***  EXTERNAL INTERFACE  ***
  1916. **
  1917. **    This routine handles the rather specialized case of looking
  1918. **    up a macro and returning the substitution value.  The name
  1919. **    must match EXACTLY, character for character.
  1920. **
  1921. **    The null pointer is returned if the macro is not defined.
  1922. */
  1923.  
  1924. char *
  1925. macro(name)
  1926. char    *name;
  1927. {
  1928.     register struct macro    *m;
  1929.     register char        *n;
  1930.     extern int        macsget();
  1931.     char            *p;
  1932.  
  1933.     /* convert macro name to internal format */
  1934.     macnewev(macsget, &p);
  1935.     p = name;
  1936.     n = mactcvt(0, 0);
  1937.     macpopev();
  1938.     if (n == 0)
  1939.     {
  1940.         /* some sort of syntax error */
  1941.         return (0);
  1942.     }
  1943.  
  1944.     for (m = Machead; m != 0; m = m->nextm)
  1945.     {
  1946.         if (macmmatch(n, m->template, 1))
  1947.         {
  1948.             buffree(n);
  1949.             return (m->substitute);
  1950.         }
  1951.     }
  1952.  
  1953.     buffree(n);
  1954.     return (0);
  1955. }
  1956.