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