|
|
1.1 root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.