Annotation of src/usr.bin/m4/eval.c, Revision 1.2
1.2 ! deraadt 1: /* $NetBSD: eval.c,v 1.5 1996/01/13 23:25:23 pk Exp $ */
1.1 deraadt 2:
3: /*
4: * Copyright (c) 1989, 1993
5: * The Regents of the University of California. All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * Ozan Yigit at York University.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. All advertising materials mentioning features or use of this software
19: * must display the following acknowledgement:
20: * This product includes software developed by the University of
21: * California, Berkeley and its contributors.
22: * 4. Neither the name of the University nor the names of its contributors
23: * may be used to endorse or promote products derived from this software
24: * without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36: * SUCH DAMAGE.
37: */
38:
39: #ifndef lint
40: #if 0
41: static char sccsid[] = "@(#)eval.c 8.2 (Berkeley) 4/27/95";
42: #else
1.2 ! deraadt 43: static char rcsid[] = "$NetBSD: eval.c,v 1.5 1996/01/13 23:25:23 pk Exp $";
1.1 deraadt 44: #endif
45: #endif /* not lint */
46:
47: /*
48: * eval.c
49: * Facility: m4 macro processor
50: * by: oz
51: */
52:
53: #include <sys/types.h>
54: #include <errno.h>
55: #include <unistd.h>
56: #include <stdio.h>
57: #include <stdlib.h>
58: #include <string.h>
59: #include "mdef.h"
60: #include "stdd.h"
61: #include "extern.h"
62: #include "pathnames.h"
63:
64: /*
65: * eval - evaluate built-in macros.
66: * argc - number of elements in argv.
67: * argv - element vector :
68: * argv[0] = definition of a user
69: * macro or nil if built-in.
70: * argv[1] = name of the macro or
71: * built-in.
72: * argv[2] = parameters to user-defined
73: * . macro or built-in.
74: * .
75: *
76: * Note that the minimum value for argc is 3. A call in the form
77: * of macro-or-builtin() will result in:
78: * argv[0] = nullstr
79: * argv[1] = macro-or-builtin
80: * argv[2] = nullstr
81: */
82:
83: void
84: eval(argv, argc, td)
85: register char *argv[];
86: register int argc;
87: register int td;
88: {
89: register int c, n;
90: static int sysval = 0;
91:
92: #ifdef DEBUG
93: printf("argc = %d\n", argc);
94: for (n = 0; n < argc; n++)
95: printf("argv[%d] = %s\n", n, argv[n]);
96: #endif
97: /*
98: * if argc == 3 and argv[2] is null, then we
99: * have macro-or-builtin() type call. We adjust
100: * argc to avoid further checking..
101: */
102: if (argc == 3 && !*(argv[2]))
103: argc--;
104:
105: switch (td & ~STATIC) {
106:
107: case DEFITYPE:
108: if (argc > 2)
109: dodefine(argv[2], (argc > 3) ? argv[3] : null);
110: break;
111:
112: case PUSDTYPE:
113: if (argc > 2)
114: dopushdef(argv[2], (argc > 3) ? argv[3] : null);
115: break;
116:
117: case DUMPTYPE:
118: dodump(argv, argc);
119: break;
120:
121: case EXPRTYPE:
122: /*
123: * doexpr - evaluate arithmetic
124: * expression
125: */
126: if (argc > 2)
127: pbnum(expr(argv[2]));
128: break;
129:
130: case IFELTYPE:
131: if (argc > 4)
132: doifelse(argv, argc);
133: break;
134:
135: case IFDFTYPE:
136: /*
137: * doifdef - select one of two
138: * alternatives based on the existence of
139: * another definition
140: */
141: if (argc > 3) {
142: if (lookup(argv[2]) != nil)
143: pbstr(argv[3]);
144: else if (argc > 4)
145: pbstr(argv[4]);
146: }
147: break;
148:
149: case LENGTYPE:
150: /*
151: * dolen - find the length of the
152: * argument
153: */
154: if (argc > 2)
155: pbnum((argc > 2) ? strlen(argv[2]) : 0);
156: break;
157:
158: case INCRTYPE:
159: /*
160: * doincr - increment the value of the
161: * argument
162: */
163: if (argc > 2)
164: pbnum(atoi(argv[2]) + 1);
165: break;
166:
167: case DECRTYPE:
168: /*
169: * dodecr - decrement the value of the
170: * argument
171: */
172: if (argc > 2)
173: pbnum(atoi(argv[2]) - 1);
174: break;
175:
176: case SYSCTYPE:
177: /*
178: * dosys - execute system command
179: */
180: if (argc > 2)
181: sysval = system(argv[2]);
182: break;
183:
184: case SYSVTYPE:
185: /*
186: * dosysval - return value of the last
187: * system call.
188: *
189: */
190: pbnum(sysval);
191: break;
192:
193: case INCLTYPE:
194: if (argc > 2)
195: if (!doincl(argv[2]))
196: oops("%s: %s", argv[2], strerror(errno));
197: break;
198:
199: case SINCTYPE:
200: if (argc > 2)
201: (void) doincl(argv[2]);
202: break;
203: #ifdef EXTENDED
204: case PASTTYPE:
205: if (argc > 2)
206: if (!dopaste(argv[2]))
207: oops("%s: %s", argv[2], strerror(errno));
208: break;
209:
210: case SPASTYPE:
211: if (argc > 2)
212: (void) dopaste(argv[2]);
213: break;
214: #endif
215: case CHNQTYPE:
216: dochq(argv, argc);
217: break;
218:
219: case CHNCTYPE:
220: dochc(argv, argc);
221: break;
222:
223: case SUBSTYPE:
224: /*
225: * dosub - select substring
226: *
227: */
228: if (argc > 3)
229: dosub(argv, argc);
230: break;
231:
232: case SHIFTYPE:
233: /*
234: * doshift - push back all arguments
235: * except the first one (i.e. skip
236: * argv[2])
237: */
238: if (argc > 3) {
1.2 ! deraadt 239: int k;
1.1 deraadt 240: for (n = argc - 1; n > 3; n--) {
1.2 ! deraadt 241: k = strlen(rquote);
! 242: while (k--)
! 243: putback(rquote[k]);
1.1 deraadt 244: pbstr(argv[n]);
1.2 ! deraadt 245: k = strlen(lquote);
! 246: while (k--)
! 247: putback(lquote[k]);
1.1 deraadt 248: putback(',');
249: }
1.2 ! deraadt 250: k = strlen(rquote);
! 251: while (k--)
! 252: putback(rquote[k]);
1.1 deraadt 253: pbstr(argv[3]);
1.2 ! deraadt 254: k = strlen(lquote);
! 255: while (k--)
! 256: putback(lquote[k]);
1.1 deraadt 257: }
258: break;
259:
260: case DIVRTYPE:
261: if (argc > 2 && (n = atoi(argv[2])) != 0)
262: dodiv(n);
263: else {
264: active = stdout;
265: oindex = 0;
266: }
267: break;
268:
269: case UNDVTYPE:
270: doundiv(argv, argc);
271: break;
272:
273: case DIVNTYPE:
274: /*
275: * dodivnum - return the number of
276: * current output diversion
277: */
278: pbnum(oindex);
279: break;
280:
281: case UNDFTYPE:
282: /*
283: * doundefine - undefine a previously
284: * defined macro(s) or m4 keyword(s).
285: */
286: if (argc > 2)
287: for (n = 2; n < argc; n++)
288: remhash(argv[n], ALL);
289: break;
290:
291: case POPDTYPE:
292: /*
293: * dopopdef - remove the topmost
294: * definitions of macro(s) or m4
295: * keyword(s).
296: */
297: if (argc > 2)
298: for (n = 2; n < argc; n++)
299: remhash(argv[n], TOP);
300: break;
301:
302: case MKTMTYPE:
303: /*
304: * dotemp - create a temporary file
305: */
306: if (argc > 2)
307: pbstr(mktemp(argv[2]));
308: break;
309:
310: case TRNLTYPE:
311: /*
312: * dotranslit - replace all characters in
313: * the source string that appears in the
314: * "from" string with the corresponding
315: * characters in the "to" string.
316: */
317: if (argc > 3) {
318: char temp[MAXTOK];
319: if (argc > 4)
320: map(temp, argv[2], argv[3], argv[4]);
321: else
322: map(temp, argv[2], argv[3], null);
323: pbstr(temp);
324: }
325: else if (argc > 2)
326: pbstr(argv[2]);
327: break;
328:
329: case INDXTYPE:
330: /*
331: * doindex - find the index of the second
332: * argument string in the first argument
333: * string. -1 if not present.
334: */
335: pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
336: break;
337:
338: case ERRPTYPE:
339: /*
340: * doerrp - print the arguments to stderr
341: * file
342: */
343: if (argc > 2) {
344: for (n = 2; n < argc; n++)
345: fprintf(stderr, "%s ", argv[n]);
346: fprintf(stderr, "\n");
347: }
348: break;
349:
350: case DNLNTYPE:
351: /*
352: * dodnl - eat-up-to and including
353: * newline
354: */
355: while ((c = gpbc()) != '\n' && c != EOF)
356: ;
357: break;
358:
359: case M4WRTYPE:
360: /*
361: * dom4wrap - set up for
362: * wrap-up/wind-down activity
363: */
364: m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
365: break;
366:
367: case EXITTYPE:
368: /*
369: * doexit - immediate exit from m4.
370: */
371: killdiv();
372: exit((argc > 2) ? atoi(argv[2]) : 0);
373: break;
374:
375: case DEFNTYPE:
376: if (argc > 2)
377: for (n = 2; n < argc; n++)
378: dodefn(argv[n]);
379: break;
380:
381: default:
382: oops("%s: major botch.", "eval");
383: break;
384: }
385: }
386:
387: char *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */
388:
389: /*
390: * expand - user-defined macro expansion
391: */
392: void
393: expand(argv, argc)
394: register char *argv[];
395: register int argc;
396: {
397: register char *t;
398: register char *p;
399: register int n;
400: register int argno;
401:
402: t = argv[0]; /* defn string as a whole */
403: p = t;
404: while (*p)
405: p++;
406: p--; /* last character of defn */
407: while (p > t) {
408: if (*(p - 1) != ARGFLAG)
409: putback(*p);
410: else {
411: switch (*p) {
412:
413: case '#':
414: pbnum(argc - 2);
415: break;
416: case '0':
417: case '1':
418: case '2':
419: case '3':
420: case '4':
421: case '5':
422: case '6':
423: case '7':
424: case '8':
425: case '9':
426: if ((argno = *p - '0') < argc - 1)
427: pbstr(argv[argno + 1]);
428: break;
429: case '*':
430: for (n = argc - 1; n > 2; n--) {
431: pbstr(argv[n]);
432: putback(',');
433: }
434: pbstr(argv[2]);
435: break;
436: default:
437: putback(*p);
438: putback('$');
439: break;
440: }
441: p--;
442: }
443: p--;
444: }
445: if (p == t) /* do last character */
446: putback(*p);
447: }
448:
449: /*
450: * dodefine - install definition in the table
451: */
452: void
453: dodefine(name, defn)
454: register char *name;
455: register char *defn;
456: {
457: register ndptr p;
458:
459: if (!*name)
460: oops("null definition.");
461: if (STREQ(name, defn))
462: oops("%s: recursive definition.", name);
463: if ((p = lookup(name)) == nil)
464: p = addent(name);
465: else if (p->defn != null)
466: free((char *) p->defn);
467: if (!*defn)
468: p->defn = null;
469: else
470: p->defn = xstrdup(defn);
471: p->type = MACRTYPE;
472: }
473:
474: /*
475: * dodefn - push back a quoted definition of
476: * the given name.
477: */
478: void
479: dodefn(name)
480: char *name;
481: {
482: register ndptr p;
483:
484: if ((p = lookup(name)) != nil && p->defn != null) {
1.2 ! deraadt 485: int n = strlen(rquote);
! 486: while (n--)
! 487: putback(rquote[n]);
1.1 deraadt 488: pbstr(p->defn);
1.2 ! deraadt 489: n = strlen(lquote);
! 490: while (n--)
! 491: putback(lquote[n]);
1.1 deraadt 492: }
493: }
494:
495: /*
496: * dopushdef - install a definition in the hash table
497: * without removing a previous definition. Since
498: * each new entry is entered in *front* of the
499: * hash bucket, it hides a previous definition from
500: * lookup.
501: */
502: void
503: dopushdef(name, defn)
504: register char *name;
505: register char *defn;
506: {
507: register ndptr p;
508:
509: if (!*name)
510: oops("null definition");
511: if (STREQ(name, defn))
512: oops("%s: recursive definition.", name);
513: p = addent(name);
514: if (!*defn)
515: p->defn = null;
516: else
517: p->defn = xstrdup(defn);
518: p->type = MACRTYPE;
519: }
520:
521: /*
522: * dodumpdef - dump the specified definitions in the hash
523: * table to stderr. If nothing is specified, the entire
524: * hash table is dumped.
525: */
526: void
527: dodump(argv, argc)
528: register char *argv[];
529: register int argc;
530: {
531: register int n;
532: ndptr p;
533:
534: if (argc > 2) {
535: for (n = 2; n < argc; n++)
536: if ((p = lookup(argv[n])) != nil)
537: fprintf(stderr, dumpfmt, p->name,
538: p->defn);
539: }
540: else {
541: for (n = 0; n < HASHSIZE; n++)
542: for (p = hashtab[n]; p != nil; p = p->nxtptr)
543: fprintf(stderr, dumpfmt, p->name,
544: p->defn);
545: }
546: }
547:
548: /*
549: * doifelse - select one of two alternatives - loop.
550: */
551: void
552: doifelse(argv, argc)
553: register char *argv[];
554: register int argc;
555: {
556: cycle {
557: if (STREQ(argv[2], argv[3]))
558: pbstr(argv[4]);
559: else if (argc == 6)
560: pbstr(argv[5]);
561: else if (argc > 6) {
562: argv += 3;
563: argc -= 3;
564: continue;
565: }
566: break;
567: }
568: }
569:
570: /*
571: * doinclude - include a given file.
572: */
573: int
574: doincl(ifile)
575: char *ifile;
576: {
577: if (ilevel + 1 == MAXINP)
578: oops("too many include files.");
579: if ((infile[ilevel + 1] = fopen(ifile, "r")) != NULL) {
580: ilevel++;
581: bbase[ilevel] = bufbase = bp;
582: return (1);
583: }
584: else
585: return (0);
586: }
587:
588: #ifdef EXTENDED
589: /*
590: * dopaste - include a given file without any
591: * macro processing.
592: */
593: int
594: dopaste(pfile)
595: char *pfile;
596: {
597: FILE *pf;
598: register int c;
599:
600: if ((pf = fopen(pfile, "r")) != NULL) {
601: while ((c = getc(pf)) != EOF)
602: putc(c, active);
603: (void) fclose(pf);
604: return (1);
605: }
606: else
607: return (0);
608: }
609: #endif
610:
611: /*
612: * dochq - change quote characters
613: */
614: void
615: dochq(argv, argc)
616: register char *argv[];
617: register int argc;
618: {
619: if (argc > 2) {
620: if (*argv[2])
1.2 ! deraadt 621: strncpy(lquote, argv[2], MAXCCHARS);
1.1 deraadt 622: if (argc > 3) {
623: if (*argv[3])
1.2 ! deraadt 624: strncpy(rquote, argv[3], MAXCCHARS);
1.1 deraadt 625: }
626: else
1.2 ! deraadt 627: strcpy(rquote, lquote);
1.1 deraadt 628: }
629: else {
1.2 ! deraadt 630: lquote[0] = LQUOTE, lquote[1] = '\0';
! 631: rquote[0] = RQUOTE, rquote[1] = '\0';
1.1 deraadt 632: }
633: }
634:
635: /*
636: * dochc - change comment characters
637: */
638: void
639: dochc(argv, argc)
640: register char *argv[];
641: register int argc;
642: {
643: if (argc > 2) {
644: if (*argv[2])
1.2 ! deraadt 645: strncpy(scommt, argv[2], MAXCCHARS);
1.1 deraadt 646: if (argc > 3) {
647: if (*argv[3])
1.2 ! deraadt 648: strncpy(ecommt, argv[3], MAXCCHARS);
1.1 deraadt 649: }
650: else
1.2 ! deraadt 651: ecommt[0] = ECOMMT, ecommt[1] = '\0';
1.1 deraadt 652: }
653: else {
1.2 ! deraadt 654: scommt[0] = SCOMMT, scommt[1] = '\0';
! 655: ecommt[0] = ECOMMT, ecommt[1] = '\0';
1.1 deraadt 656: }
657: }
658:
659: /*
660: * dodivert - divert the output to a temporary file
661: */
662: void
663: dodiv(n)
664: register int n;
665: {
666: if (n < 0 || n >= MAXOUT)
667: n = 0; /* bitbucket */
668: if (outfile[n] == NULL) {
669: m4temp[UNIQUE] = n + '0';
670: if ((outfile[n] = fopen(m4temp, "w")) == NULL)
671: oops("%s: cannot divert.", m4temp);
672: }
673: oindex = n;
674: active = outfile[n];
675: }
676:
677: /*
678: * doundivert - undivert a specified output, or all
679: * other outputs, in numerical order.
680: */
681: void
682: doundiv(argv, argc)
683: register char *argv[];
684: register int argc;
685: {
686: register int ind;
687: register int n;
688:
689: if (argc > 2) {
690: for (ind = 2; ind < argc; ind++) {
691: n = atoi(argv[ind]);
692: if (n > 0 && n < MAXOUT && outfile[n] != NULL)
693: getdiv(n);
694:
695: }
696: }
697: else
698: for (n = 1; n < MAXOUT; n++)
699: if (outfile[n] != NULL)
700: getdiv(n);
701: }
702:
703: /*
704: * dosub - select substring
705: */
706: void
707: dosub(argv, argc)
708: register char *argv[];
709: register int argc;
710: {
711: register char *ap, *fc, *k;
712: register int nc;
713:
714: if (argc < 5)
715: nc = MAXTOK;
716: else
717: #ifdef EXPR
718: nc = expr(argv[4]);
719: #else
720: nc = atoi(argv[4]);
721: #endif
722: ap = argv[2]; /* target string */
723: #ifdef EXPR
724: fc = ap + expr(argv[3]); /* first char */
725: #else
726: fc = ap + atoi(argv[3]); /* first char */
727: #endif
728: if (fc >= ap && fc < ap + strlen(ap))
729: for (k = fc + min(nc, strlen(fc)) - 1; k >= fc; k--)
730: putback(*k);
731: }
732:
733: /*
734: * map:
735: * map every character of s1 that is specified in from
736: * into s3 and replace in s. (source s1 remains untouched)
737: *
738: * This is a standard implementation of map(s,from,to) function of ICON
739: * language. Within mapvec, we replace every character of "from" with
740: * the corresponding character in "to". If "to" is shorter than "from",
741: * than the corresponding entries are null, which means that those
742: * characters dissapear altogether. Furthermore, imagine
743: * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
744: * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
745: * ultimately maps to `*'. In order to achieve this effect in an efficient
746: * manner (i.e. without multiple passes over the destination string), we
747: * loop over mapvec, starting with the initial source character. if the
748: * character value (dch) in this location is different than the source
749: * character (sch), sch becomes dch, once again to index into mapvec, until
750: * the character value stabilizes (i.e. sch = dch, in other words
751: * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
752: * character, it will stabilize, since mapvec[0] == 0 at all times. At the
753: * end, we restore mapvec* back to normal where mapvec[n] == n for
754: * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
755: * about 5 times faster than any algorithm that makes multiple passes over
756: * destination string.
757: */
758: void
759: map(dest, src, from, to)
760: register char *dest;
761: register char *src;
762: register char *from;
763: register char *to;
764: {
765: register char *tmp;
766: register char sch, dch;
767: static char mapvec[128] = {
768: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
769: 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
770: 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
771: 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
772: 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
773: 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
774: 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
775: 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
776: 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
777: 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
778: 120, 121, 122, 123, 124, 125, 126, 127
779: };
780:
781: if (*src) {
782: tmp = from;
783: /*
784: * create a mapping between "from" and
785: * "to"
786: */
787: while (*from)
788: mapvec[*from++] = (*to) ? *to++ : (char) 0;
789:
790: while (*src) {
791: sch = *src++;
792: dch = mapvec[sch];
793: while (dch != sch) {
794: sch = dch;
795: dch = mapvec[sch];
796: }
797: if (*dest = dch)
798: dest++;
799: }
800: /*
801: * restore all the changed characters
802: */
803: while (*tmp) {
804: mapvec[*tmp] = *tmp;
805: tmp++;
806: }
807: }
808: *dest = (char) 0;
809: }