eval.c revision 1.8 1 /* $NetBSD: eval.c,v 1.8 1997/01/09 20:20:35 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.8 1997/01/09 20:20:35 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 int k;
240 for (n = argc - 1; n > 3; n--) {
241 k = strlen(rquote);
242 while (k--)
243 putback(rquote[k]);
244 pbstr(argv[n]);
245 k = strlen(lquote);
246 while (k--)
247 putback(lquote[k]);
248 putback(COMMA);
249 }
250 k = strlen(rquote);
251 while (k--)
252 putback(rquote[k]);
253 pbstr(argv[3]);
254 k = strlen(lquote);
255 while (k--)
256 putback(lquote[k]);
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(COMMA);
433 }
434 pbstr(argv[2]);
435 break;
436 case '@':
437 for (n = argc - 1; n > 2; n--) {
438 pbstr(rquote);
439 pbstr(argv[n]);
440 pbstr(lquote);
441 putback(COMMA);
442 }
443 pbstr(rquote);
444 pbstr(argv[2]);
445 pbstr(lquote);
446 break;
447 default:
448 putback(*p);
449 putback('$');
450 break;
451 }
452 p--;
453 }
454 p--;
455 }
456 if (p == t) /* do last character */
457 putback(*p);
458 }
459
460 /*
461 * dodefine - install definition in the table
462 */
463 void
464 dodefine(name, defn)
465 register char *name;
466 register char *defn;
467 {
468 register ndptr p;
469
470 if (!*name)
471 oops("null definition.");
472 if (STREQ(name, defn))
473 oops("%s: recursive definition.", name);
474 if ((p = lookup(name)) == nil)
475 p = addent(name);
476 else if (p->defn != null)
477 free((char *) p->defn);
478 if (!*defn)
479 p->defn = null;
480 else
481 p->defn = xstrdup(defn);
482 p->type = MACRTYPE;
483 }
484
485 /*
486 * dodefn - push back a quoted definition of
487 * the given name.
488 */
489 void
490 dodefn(name)
491 char *name;
492 {
493 register ndptr p;
494
495 if ((p = lookup(name)) != nil && p->defn != null) {
496 int n = strlen(rquote);
497 while (n--)
498 putback(rquote[n]);
499 pbstr(p->defn);
500 n = strlen(lquote);
501 while (n--)
502 putback(lquote[n]);
503 }
504 }
505
506 /*
507 * dopushdef - install a definition in the hash table
508 * without removing a previous definition. Since
509 * each new entry is entered in *front* of the
510 * hash bucket, it hides a previous definition from
511 * lookup.
512 */
513 void
514 dopushdef(name, defn)
515 register char *name;
516 register char *defn;
517 {
518 register ndptr p;
519
520 if (!*name)
521 oops("null definition");
522 if (STREQ(name, defn))
523 oops("%s: recursive definition.", name);
524 p = addent(name);
525 if (!*defn)
526 p->defn = null;
527 else
528 p->defn = xstrdup(defn);
529 p->type = MACRTYPE;
530 }
531
532 /*
533 * dodumpdef - dump the specified definitions in the hash
534 * table to stderr. If nothing is specified, the entire
535 * hash table is dumped.
536 */
537 void
538 dodump(argv, argc)
539 register char *argv[];
540 register int argc;
541 {
542 register int n;
543 ndptr p;
544
545 if (argc > 2) {
546 for (n = 2; n < argc; n++)
547 if ((p = lookup(argv[n])) != nil)
548 fprintf(stderr, dumpfmt, p->name,
549 p->defn);
550 }
551 else {
552 for (n = 0; n < HASHSIZE; n++)
553 for (p = hashtab[n]; p != nil; p = p->nxtptr)
554 fprintf(stderr, dumpfmt, p->name,
555 p->defn);
556 }
557 }
558
559 /*
560 * doifelse - select one of two alternatives - loop.
561 */
562 void
563 doifelse(argv, argc)
564 register char *argv[];
565 register int argc;
566 {
567 cycle {
568 if (STREQ(argv[2], argv[3]))
569 pbstr(argv[4]);
570 else if (argc == 6)
571 pbstr(argv[5]);
572 else if (argc > 6) {
573 argv += 3;
574 argc -= 3;
575 continue;
576 }
577 break;
578 }
579 }
580
581 /*
582 * doinclude - include a given file.
583 */
584 int
585 doincl(ifile)
586 char *ifile;
587 {
588 if (ilevel + 1 == MAXINP)
589 oops("too many include files.");
590 if ((infile[ilevel + 1] = fopen(ifile, "r")) != NULL) {
591 ilevel++;
592 bbase[ilevel] = bufbase = bp;
593 return (1);
594 }
595 else
596 return (0);
597 }
598
599 #ifdef EXTENDED
600 /*
601 * dopaste - include a given file without any
602 * macro processing.
603 */
604 int
605 dopaste(pfile)
606 char *pfile;
607 {
608 FILE *pf;
609 register int c;
610
611 if ((pf = fopen(pfile, "r")) != NULL) {
612 while ((c = getc(pf)) != EOF)
613 putc(c, active);
614 (void) fclose(pf);
615 return (1);
616 }
617 else
618 return (0);
619 }
620 #endif
621
622 /*
623 * dochq - change quote characters
624 */
625 void
626 dochq(argv, argc)
627 register char *argv[];
628 register int argc;
629 {
630 if (argc > 2) {
631 if (*argv[2])
632 strncpy(lquote, argv[2], MAXCCHARS);
633 if (argc > 3) {
634 if (*argv[3])
635 strncpy(rquote, argv[3], MAXCCHARS);
636 }
637 else
638 strcpy(rquote, lquote);
639 }
640 else {
641 lquote[0] = LQUOTE, lquote[1] = '\0';
642 rquote[0] = RQUOTE, rquote[1] = '\0';
643 }
644 }
645
646 /*
647 * dochc - change comment characters
648 */
649 void
650 dochc(argv, argc)
651 register char *argv[];
652 register int argc;
653 {
654 if (argc > 2) {
655 if (*argv[2])
656 strncpy(scommt, argv[2], MAXCCHARS);
657 if (argc > 3) {
658 if (*argv[3])
659 strncpy(ecommt, argv[3], MAXCCHARS);
660 }
661 else
662 ecommt[0] = ECOMMT, ecommt[1] = '\0';
663 }
664 else {
665 scommt[0] = SCOMMT, scommt[1] = '\0';
666 ecommt[0] = ECOMMT, ecommt[1] = '\0';
667 }
668 }
669
670 /*
671 * dodivert - divert the output to a temporary file
672 */
673 void
674 dodiv(n)
675 register int n;
676 {
677 if (n < 0 || n >= MAXOUT)
678 n = 0; /* bitbucket */
679 if (outfile[n] == NULL) {
680 m4temp[UNIQUE] = n + '0';
681 if ((outfile[n] = fopen(m4temp, "w")) == NULL)
682 oops("%s: cannot divert.", m4temp);
683 }
684 oindex = n;
685 active = outfile[n];
686 }
687
688 /*
689 * doundivert - undivert a specified output, or all
690 * other outputs, in numerical order.
691 */
692 void
693 doundiv(argv, argc)
694 register char *argv[];
695 register int argc;
696 {
697 register int ind;
698 register int n;
699
700 if (argc > 2) {
701 for (ind = 2; ind < argc; ind++) {
702 n = atoi(argv[ind]);
703 if (n > 0 && n < MAXOUT && outfile[n] != NULL)
704 getdiv(n);
705
706 }
707 }
708 else
709 for (n = 1; n < MAXOUT; n++)
710 if (outfile[n] != NULL)
711 getdiv(n);
712 }
713
714 /*
715 * dosub - select substring
716 */
717 void
718 dosub(argv, argc)
719 register char *argv[];
720 register int argc;
721 {
722 register char *ap, *fc, *k;
723 register int nc;
724
725 if (argc < 5)
726 nc = MAXTOK;
727 else
728 #ifdef EXPR
729 nc = expr(argv[4]);
730 #else
731 nc = atoi(argv[4]);
732 #endif
733 ap = argv[2]; /* target string */
734 #ifdef EXPR
735 fc = ap + expr(argv[3]); /* first char */
736 #else
737 fc = ap + atoi(argv[3]); /* first char */
738 #endif
739 if (fc >= ap && fc < ap + strlen(ap))
740 for (k = fc + min(nc, strlen(fc)) - 1; k >= fc; k--)
741 putback(*k);
742 }
743
744 /*
745 * map:
746 * map every character of s1 that is specified in from
747 * into s3 and replace in s. (source s1 remains untouched)
748 *
749 * This is a standard implementation of map(s,from,to) function of ICON
750 * language. Within mapvec, we replace every character of "from" with
751 * the corresponding character in "to". If "to" is shorter than "from",
752 * than the corresponding entries are null, which means that those
753 * characters dissapear altogether. Furthermore, imagine
754 * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
755 * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
756 * ultimately maps to `*'. In order to achieve this effect in an efficient
757 * manner (i.e. without multiple passes over the destination string), we
758 * loop over mapvec, starting with the initial source character. if the
759 * character value (dch) in this location is different than the source
760 * character (sch), sch becomes dch, once again to index into mapvec, until
761 * the character value stabilizes (i.e. sch = dch, in other words
762 * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
763 * character, it will stabilize, since mapvec[0] == 0 at all times. At the
764 * end, we restore mapvec* back to normal where mapvec[n] == n for
765 * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
766 * about 5 times faster than any algorithm that makes multiple passes over
767 * destination string.
768 */
769 void
770 map(dest, src, from, to)
771 register char *dest;
772 register char *src;
773 register char *from;
774 register char *to;
775 {
776 register char *tmp;
777 register char sch, dch;
778 static char mapvec[128] = {
779 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
780 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
781 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
782 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
783 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
784 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
785 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
786 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
787 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
788 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
789 120, 121, 122, 123, 124, 125, 126, 127
790 };
791
792 if (*src) {
793 tmp = from;
794 /*
795 * create a mapping between "from" and
796 * "to"
797 */
798 while (*from)
799 mapvec[*from++] = (*to) ? *to++ : (char) 0;
800
801 while (*src) {
802 sch = *src++;
803 dch = mapvec[sch];
804 while (dch != sch) {
805 sch = dch;
806 dch = mapvec[sch];
807 }
808 if (*dest = dch)
809 dest++;
810 }
811 /*
812 * restore all the changed characters
813 */
814 while (*tmp) {
815 mapvec[*tmp] = *tmp;
816 tmp++;
817 }
818 }
819 *dest = (char) 0;
820 }
821