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

Defined functions

macallscan defined in line 567; used 1 times
macckch defined in line 824; used 2 times
macdefine defined in line 1300; used 8 times
macdelim defined in line 799; used 2 times
macdnl defined in line 1277; used 6 times
macdump defined in line 1878; used 1 times
macfetch defined in line 352; used 9 times
macgch defined in line 256; used 7 times
macload defined in line 1043; used 9 times
macmatch defined in line 737; used 2 times
macmlkup defined in line 1615; used 2 times
macmmatch defined in line 1651; used 3 times
macmmchew defined in line 1734; used 2 times
macmocv defined in line 1931; used 3 times
macmode defined in line 539; used 7 times
macnewev defined in line 947; used 5 times
macnumber defined in line 1799; used 6 times
macparam defined in line 659; used 1 times
macpflush defined in line 990; used 3 times
macplkup defined in line 1129; used 26 times
macpopev defined in line 970; used 7 times
macprescan defined in line 907; used 1 times
macprim defined in line 1187; used 1 times
macread defined in line 1770; used 2 times
macremove defined in line 1561; used 2 times
macrescan defined in line 1076; used 3 times
macscan defined in line 598; used 1 times
macschng defined in line 450; used 1 times
macsget defined in line 1018; used 12 times
macspring defined in line 1152; used 2 times
macsstr defined in line 1844; used 2 times
macsubs defined in line 859; used 1 times
mactcvt defined in line 1412; used 5 times
macunget defined in line 1101; used 8 times

Defined variables

Macenv defined in line 112; used 27 times
Machead defined in line 75; used 8 times
Macprims defined in line 56; used 17 times

Defined struct's

env defined in line 90; used 52 times
macro defined in line 48; used 34 times
param defined in line 79; used 16 times

Defined macros

ANYDELIM defined in line 9; used 7 times
BACKSLASH defined in line 19; used 5 times
BYTEMASK defined in line 28; used 1 times
CHANGE defined in line 11; used 4 times
CHARMASK defined in line 27; used 10 times
DELIM defined in line 37; used 8 times
ID defined in line 35; used 2 times
ITERTHRESH defined in line 30; used 1 times
LBRACE defined in line 17; used 1 times
LQUOTE defined in line 20; never used
NEWLINE defined in line 24; used 9 times
NOCHANGE defined in line 40; used 3 times
NONE defined in line 34; used 3 times
NPRIMS defined in line 31; used 4 times
NUMBER defined in line 36; used 4 times
ONEDELIM defined in line 10; used 6 times
OP defined in line 39; used 1 times
PARAMN defined in line 13; used 7 times
PARAMP defined in line 14; used 5 times
PRESCANENABLE defined in line 16; used 1 times
QUOTED defined in line 26; used 7 times
QUOTEMODE defined in line 38; used 1 times
RBRACE defined in line 18; used 1 times
RQUOTE defined in line 21; never used
SPACE defined in line 22; used 4 times
TAB defined in line 23; used 3 times
TRACE defined in line 2; used 17 times
Last modified: 1995-02-18
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4520
Valid CSS Valid XHTML 1.0 Strict