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