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