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