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: }

Defined functions

macallscan defined in line 548; used 1 times
macckch defined in line 791; used 2 times
macdefine defined in line 1226; used 6 times
macdelim defined in line 768; used 2 times
macdnl defined in line 1206; used 6 times
macdump defined in line 1772; used 1 times
macfetch defined in line 343; used 9 times
macgch defined in line 251; used 7 times
macload defined in line 989; used 9 times
macmatch defined in line 709; used 2 times
macmlkup defined in line 1529; used 2 times
macmmatch defined in line 1562; used 3 times
macmmchew defined in line 1641; used 2 times
macmocv defined in line 1822; used 3 times
macmode defined in line 523; used 7 times
macnewev defined in line 906; used 5 times
macnumber defined in line 1699; used 6 times
macparam defined in line 635; used 1 times
macpflush defined in line 943; used 3 times
macplkup defined in line 1065; used 28 times
macpopev defined in line 926; used 7 times
macprescan defined in line 870; used 1 times
macprim defined in line 1118; used 2 times
macread defined in line 1673; used 2 times
macremove defined in line 1479; used 2 times
macrescan defined in line 1020; used 3 times
macscan defined in line 577; used 1 times
macschng defined in line 438; used 1 times
macsget defined in line 968; used 12 times
macspring defined in line 1086; used 2 times
macsstr defined in line 1740; used 2 times
macsubs defined in line 824; used 1 times
mactcvt defined in line 1333; used 5 times
macunget defined in line 1041; used 8 times

Defined variables

Macenv defined in line 117; used 27 times
Machead defined in line 80; used 8 times
Macprims defined in line 61; used 17 times

Defined struct's

env defined in line 95; used 52 times
macro defined in line 53; used 32 times
param defined in line 84; used 16 times

Defined macros

ANYDELIM defined in line 14; used 7 times
BACKSLASH defined in line 24; used 5 times
BYTEMASK defined in line 33; used 1 times
CHANGE defined in line 16; used 4 times
CHARMASK defined in line 32; used 10 times
DELIM defined in line 42; used 8 times
ID defined in line 40; used 2 times
ITERTHRESH defined in line 35; used 1 times
LBRACE defined in line 22; used 1 times
LQUOTE defined in line 25; never used
NEWLINE defined in line 29; used 9 times
NOCHANGE defined in line 45; used 3 times
NONE defined in line 39; used 3 times
NPRIMS defined in line 36; used 4 times
NUMBER defined in line 41; used 4 times
ONEDELIM defined in line 15; used 6 times
OP defined in line 44; used 1 times
PARAMN defined in line 18; used 7 times
PARAMP defined in line 19; used 5 times
PRESCANENABLE defined in line 21; used 1 times
QUOTED defined in line 31; used 7 times
QUOTEMODE defined in line 43; used 1 times
RBRACE defined in line 23; used 1 times
RQUOTE defined in line 26; never used
SPACE defined in line 27; used 4 times
TAB defined in line 28; used 3 times
TRACE defined in line 7; used 17 times
Last modified: 1986-04-17
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2966
Valid CSS Valid XHTML 1.0 Strict