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