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