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