eval.c revision 7004
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1989, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * This code is derived from software contributed to Berkeley by
61590Srgrimes * Ozan Yigit at York University.
71590Srgrimes *
81590Srgrimes * Redistribution and use in source and binary forms, with or without
91590Srgrimes * modification, are permitted provided that the following conditions
101590Srgrimes * are met:
111590Srgrimes * 1. Redistributions of source code must retain the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer.
131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer in the
151590Srgrimes *    documentation and/or other materials provided with the distribution.
161590Srgrimes * 3. All advertising materials mentioning features or use of this software
171590Srgrimes *    must display the following acknowledgement:
181590Srgrimes *	This product includes software developed by the University of
191590Srgrimes *	California, Berkeley and its contributors.
201590Srgrimes * 4. Neither the name of the University nor the names of its contributors
211590Srgrimes *    may be used to endorse or promote products derived from this software
221590Srgrimes *    without specific prior written permission.
231590Srgrimes *
241590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341590Srgrimes * SUCH DAMAGE.
351590Srgrimes */
361590Srgrimes
371590Srgrimes#ifndef lint
381590Srgrimesstatic char sccsid[] = "@(#)eval.c	8.1 (Berkeley) 6/6/93";
391590Srgrimes#endif /* not lint */
401590Srgrimes
411590Srgrimes/*
421590Srgrimes * eval.c
431590Srgrimes * Facility: m4 macro processor
441590Srgrimes * by: oz
451590Srgrimes */
461590Srgrimes
471590Srgrimes#include <sys/types.h>
481590Srgrimes#include <errno.h>
491590Srgrimes#include <unistd.h>
501590Srgrimes#include <stdio.h>
511590Srgrimes#include <stdlib.h>
521590Srgrimes#include <string.h>
531590Srgrimes#include "mdef.h"
541590Srgrimes#include "stdd.h"
551590Srgrimes#include "extern.h"
561590Srgrimes#include "pathnames.h"
571590Srgrimes
581590Srgrimes/*
591590Srgrimes * eval - evaluate built-in macros.
601590Srgrimes *	  argc - number of elements in argv.
611590Srgrimes *	  argv - element vector :
621590Srgrimes *			argv[0] = definition of a user
631590Srgrimes *				  macro or nil if built-in.
641590Srgrimes *			argv[1] = name of the macro or
651590Srgrimes *				  built-in.
661590Srgrimes *			argv[2] = parameters to user-defined
671590Srgrimes *			   .	  macro or built-in.
681590Srgrimes *			   .
691590Srgrimes *
701590Srgrimes * Note that the minimum value for argc is 3. A call in the form
711590Srgrimes * of macro-or-builtin() will result in:
721590Srgrimes *			argv[0] = nullstr
731590Srgrimes *			argv[1] = macro-or-builtin
741590Srgrimes *			argv[2] = nullstr
751590Srgrimes */
761590Srgrimes
771590Srgrimesvoid
781590Srgrimeseval(argv, argc, td)
791590Srgrimesregister char *argv[];
801590Srgrimesregister int argc;
811590Srgrimesregister int td;
821590Srgrimes{
831590Srgrimes	register int c, n;
841590Srgrimes	static int sysval = 0;
851590Srgrimes
861590Srgrimes#ifdef DEBUG
871590Srgrimes	printf("argc = %d\n", argc);
881590Srgrimes	for (n = 0; n < argc; n++)
891590Srgrimes		printf("argv[%d] = %s\n", n, argv[n]);
901590Srgrimes#endif
911590Srgrimes /*
921590Srgrimes  * if argc == 3 and argv[2] is null, then we
931590Srgrimes  * have macro-or-builtin() type call. We adjust
941590Srgrimes  * argc to avoid further checking..
951590Srgrimes  */
961590Srgrimes	if (argc == 3 && !*(argv[2]))
971590Srgrimes		argc--;
981590Srgrimes
991590Srgrimes	switch (td & ~STATIC) {
1001590Srgrimes
1011590Srgrimes	case DEFITYPE:
1021590Srgrimes		if (argc > 2)
1031590Srgrimes			dodefine(argv[2], (argc > 3) ? argv[3] : null);
1041590Srgrimes		break;
1051590Srgrimes
1061590Srgrimes	case PUSDTYPE:
1071590Srgrimes		if (argc > 2)
1081590Srgrimes			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
1091590Srgrimes		break;
1101590Srgrimes
1111590Srgrimes	case DUMPTYPE:
1121590Srgrimes		dodump(argv, argc);
1131590Srgrimes		break;
1141590Srgrimes
1151590Srgrimes	case EXPRTYPE:
1161590Srgrimes	/*
1171590Srgrimes	 * doexpr - evaluate arithmetic
1181590Srgrimes	 * expression
1191590Srgrimes	 */
1201590Srgrimes		if (argc > 2)
1211590Srgrimes			pbnum(expr(argv[2]));
1221590Srgrimes		break;
1231590Srgrimes
1241590Srgrimes	case IFELTYPE:
1251590Srgrimes		if (argc > 4)
1261590Srgrimes			doifelse(argv, argc);
1271590Srgrimes		break;
1281590Srgrimes
1291590Srgrimes	case IFDFTYPE:
1301590Srgrimes	/*
1311590Srgrimes	 * doifdef - select one of two
1321590Srgrimes	 * alternatives based on the existence of
1331590Srgrimes	 * another definition
1341590Srgrimes	 */
1351590Srgrimes		if (argc > 3) {
1361590Srgrimes			if (lookup(argv[2]) != nil)
1371590Srgrimes				pbstr(argv[3]);
1381590Srgrimes			else if (argc > 4)
1391590Srgrimes				pbstr(argv[4]);
1401590Srgrimes		}
1411590Srgrimes		break;
1421590Srgrimes
1431590Srgrimes	case LENGTYPE:
1441590Srgrimes	/*
1451590Srgrimes	 * dolen - find the length of the
1461590Srgrimes	 * argument
1471590Srgrimes	 */
1481590Srgrimes		if (argc > 2)
1491590Srgrimes			pbnum((argc > 2) ? strlen(argv[2]) : 0);
1501590Srgrimes		break;
1511590Srgrimes
1521590Srgrimes	case INCRTYPE:
1531590Srgrimes	/*
1541590Srgrimes	 * doincr - increment the value of the
1551590Srgrimes	 * argument
1561590Srgrimes	 */
1571590Srgrimes		if (argc > 2)
1581590Srgrimes			pbnum(atoi(argv[2]) + 1);
1591590Srgrimes		break;
1601590Srgrimes
1611590Srgrimes	case DECRTYPE:
1621590Srgrimes	/*
1631590Srgrimes	 * dodecr - decrement the value of the
1641590Srgrimes	 * argument
1651590Srgrimes	 */
1661590Srgrimes		if (argc > 2)
1671590Srgrimes			pbnum(atoi(argv[2]) - 1);
1681590Srgrimes		break;
1691590Srgrimes
1701590Srgrimes	case SYSCTYPE:
1711590Srgrimes	/*
1721590Srgrimes	 * dosys - execute system command
1737004Sache	 */
1747004Sache		/* Make sure m4 output is NOT interrupted */
1757004Sache		fflush(stdout);
1767004Sache		fflush(stderr);
1771590Srgrimes		if (argc > 2)
1781590Srgrimes			sysval = system(argv[2]);
1791590Srgrimes		break;
1801590Srgrimes
1811590Srgrimes	case SYSVTYPE:
1821590Srgrimes	/*
1831590Srgrimes	 * dosysval - return value of the last
1841590Srgrimes	 * system call.
1851590Srgrimes	 *
1861590Srgrimes	 */
1871590Srgrimes		pbnum(sysval);
1881590Srgrimes		break;
1891590Srgrimes
1901590Srgrimes	case INCLTYPE:
1911590Srgrimes		if (argc > 2)
1921590Srgrimes			if (!doincl(argv[2]))
1931590Srgrimes				oops("%s: %s", argv[2], strerror(errno));
1941590Srgrimes		break;
1951590Srgrimes
1961590Srgrimes	case SINCTYPE:
1971590Srgrimes		if (argc > 2)
1981590Srgrimes			(void) doincl(argv[2]);
1991590Srgrimes		break;
2001590Srgrimes#ifdef EXTENDED
2011590Srgrimes	case PASTTYPE:
2021590Srgrimes		if (argc > 2)
2031590Srgrimes			if (!dopaste(argv[2]))
2041590Srgrimes				oops("%s: %s", argv[2], strerror(errno));
2051590Srgrimes		break;
2061590Srgrimes
2071590Srgrimes	case SPASTYPE:
2081590Srgrimes		if (argc > 2)
2091590Srgrimes			(void) dopaste(argv[2]);
2101590Srgrimes		break;
2111590Srgrimes#endif
2121590Srgrimes	case CHNQTYPE:
2131590Srgrimes		dochq(argv, argc);
2141590Srgrimes		break;
2151590Srgrimes
2161590Srgrimes	case CHNCTYPE:
2171590Srgrimes		dochc(argv, argc);
2181590Srgrimes		break;
2191590Srgrimes
2201590Srgrimes	case SUBSTYPE:
2211590Srgrimes	/*
2221590Srgrimes	 * dosub - select substring
2231590Srgrimes	 *
2241590Srgrimes	 */
2251590Srgrimes		if (argc > 3)
2261590Srgrimes			dosub(argv, argc);
2271590Srgrimes		break;
2281590Srgrimes
2291590Srgrimes	case SHIFTYPE:
2301590Srgrimes	/*
2311590Srgrimes	 * doshift - push back all arguments
2321590Srgrimes	 * except the first one (i.e. skip
2331590Srgrimes	 * argv[2])
2341590Srgrimes	 */
2351590Srgrimes		if (argc > 3) {
2361590Srgrimes			for (n = argc - 1; n > 3; n--) {
2371590Srgrimes				putback(rquote);
2381590Srgrimes				pbstr(argv[n]);
2391590Srgrimes				putback(lquote);
2401590Srgrimes				putback(',');
2411590Srgrimes			}
2421590Srgrimes			putback(rquote);
2431590Srgrimes			pbstr(argv[3]);
2441590Srgrimes			putback(lquote);
2451590Srgrimes		}
2461590Srgrimes		break;
2471590Srgrimes
2481590Srgrimes	case DIVRTYPE:
2491590Srgrimes		if (argc > 2 && (n = atoi(argv[2])) != 0)
2501590Srgrimes			dodiv(n);
2511590Srgrimes		else {
2521590Srgrimes			active = stdout;
2531590Srgrimes			oindex = 0;
2541590Srgrimes		}
2551590Srgrimes		break;
2561590Srgrimes
2571590Srgrimes	case UNDVTYPE:
2581590Srgrimes		doundiv(argv, argc);
2591590Srgrimes		break;
2601590Srgrimes
2611590Srgrimes	case DIVNTYPE:
2621590Srgrimes	/*
2631590Srgrimes	 * dodivnum - return the number of
2641590Srgrimes	 * current output diversion
2651590Srgrimes	 */
2661590Srgrimes		pbnum(oindex);
2671590Srgrimes		break;
2681590Srgrimes
2691590Srgrimes	case UNDFTYPE:
2701590Srgrimes	/*
2711590Srgrimes	 * doundefine - undefine a previously
2721590Srgrimes	 * defined macro(s) or m4 keyword(s).
2731590Srgrimes	 */
2741590Srgrimes		if (argc > 2)
2751590Srgrimes			for (n = 2; n < argc; n++)
2761590Srgrimes				remhash(argv[n], ALL);
2771590Srgrimes		break;
2781590Srgrimes
2791590Srgrimes	case POPDTYPE:
2801590Srgrimes	/*
2811590Srgrimes	 * dopopdef - remove the topmost
2821590Srgrimes	 * definitions of macro(s) or m4
2831590Srgrimes	 * keyword(s).
2841590Srgrimes	 */
2851590Srgrimes		if (argc > 2)
2861590Srgrimes			for (n = 2; n < argc; n++)
2871590Srgrimes				remhash(argv[n], TOP);
2881590Srgrimes		break;
2891590Srgrimes
2901590Srgrimes	case MKTMTYPE:
2911590Srgrimes	/*
2921590Srgrimes	 * dotemp - create a temporary file
2931590Srgrimes	 */
2941590Srgrimes		if (argc > 2)
2951590Srgrimes			pbstr(mktemp(argv[2]));
2961590Srgrimes		break;
2971590Srgrimes
2981590Srgrimes	case TRNLTYPE:
2991590Srgrimes	/*
3001590Srgrimes	 * dotranslit - replace all characters in
3011590Srgrimes	 * the source string that appears in the
3021590Srgrimes	 * "from" string with the corresponding
3031590Srgrimes	 * characters in the "to" string.
3041590Srgrimes	 */
3051590Srgrimes		if (argc > 3) {
3061590Srgrimes			char temp[MAXTOK];
3071590Srgrimes			if (argc > 4)
3081590Srgrimes				map(temp, argv[2], argv[3], argv[4]);
3091590Srgrimes			else
3101590Srgrimes				map(temp, argv[2], argv[3], null);
3111590Srgrimes			pbstr(temp);
3121590Srgrimes		}
3131590Srgrimes		else if (argc > 2)
3141590Srgrimes			pbstr(argv[2]);
3151590Srgrimes		break;
3161590Srgrimes
3171590Srgrimes	case INDXTYPE:
3181590Srgrimes	/*
3191590Srgrimes	 * doindex - find the index of the second
3201590Srgrimes	 * argument string in the first argument
3211590Srgrimes	 * string. -1 if not present.
3221590Srgrimes	 */
3231590Srgrimes		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
3241590Srgrimes		break;
3251590Srgrimes
3261590Srgrimes	case ERRPTYPE:
3271590Srgrimes	/*
3281590Srgrimes	 * doerrp - print the arguments to stderr
3291590Srgrimes	 * file
3301590Srgrimes	 */
3311590Srgrimes		if (argc > 2) {
3321590Srgrimes			for (n = 2; n < argc; n++)
3331590Srgrimes				fprintf(stderr, "%s ", argv[n]);
3341590Srgrimes			fprintf(stderr, "\n");
3351590Srgrimes		}
3361590Srgrimes		break;
3371590Srgrimes
3381590Srgrimes	case DNLNTYPE:
3391590Srgrimes	/*
3401590Srgrimes	 * dodnl - eat-up-to and including
3411590Srgrimes	 * newline
3421590Srgrimes	 */
3431590Srgrimes		while ((c = gpbc()) != '\n' && c != EOF)
3441590Srgrimes			;
3451590Srgrimes		break;
3461590Srgrimes
3471590Srgrimes	case M4WRTYPE:
3481590Srgrimes	/*
3491590Srgrimes	 * dom4wrap - set up for
3501590Srgrimes	 * wrap-up/wind-down activity
3511590Srgrimes	 */
3521590Srgrimes		m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
3531590Srgrimes		break;
3541590Srgrimes
3551590Srgrimes	case EXITTYPE:
3561590Srgrimes	/*
3571590Srgrimes	 * doexit - immediate exit from m4.
3581590Srgrimes	 */
3591590Srgrimes		exit((argc > 2) ? atoi(argv[2]) : 0);
3601590Srgrimes		break;
3611590Srgrimes
3621590Srgrimes	case DEFNTYPE:
3631590Srgrimes		if (argc > 2)
3641590Srgrimes			for (n = 2; n < argc; n++)
3651590Srgrimes				dodefn(argv[n]);
3661590Srgrimes		break;
3671590Srgrimes
3681590Srgrimes	default:
3691590Srgrimes		oops("%s: major botch.", "eval");
3701590Srgrimes		break;
3711590Srgrimes	}
3721590Srgrimes}
3731590Srgrimes
3741590Srgrimeschar *dumpfmt = "`%s'\t`%s'\n";	       /* format string for dumpdef   */
3751590Srgrimes
3761590Srgrimes/*
3771590Srgrimes * expand - user-defined macro expansion
3781590Srgrimes */
3791590Srgrimesvoid
3801590Srgrimesexpand(argv, argc)
3811590Srgrimesregister char *argv[];
3821590Srgrimesregister int argc;
3831590Srgrimes{
3845165Sache	register unsigned char *t;
3855165Sache	register unsigned char *p;
3861590Srgrimes	register int n;
3871590Srgrimes	register int argno;
3881590Srgrimes
3891590Srgrimes	t = argv[0];		       /* defn string as a whole */
3901590Srgrimes	p = t;
3911590Srgrimes	while (*p)
3921590Srgrimes		p++;
3931590Srgrimes	p--;			       /* last character of defn */
3941590Srgrimes	while (p > t) {
3951590Srgrimes		if (*(p - 1) != ARGFLAG)
3961590Srgrimes			putback(*p);
3971590Srgrimes		else {
3981590Srgrimes			switch (*p) {
3991590Srgrimes
4001590Srgrimes			case '#':
4011590Srgrimes				pbnum(argc - 2);
4021590Srgrimes				break;
4031590Srgrimes			case '0':
4041590Srgrimes			case '1':
4051590Srgrimes			case '2':
4061590Srgrimes			case '3':
4071590Srgrimes			case '4':
4081590Srgrimes			case '5':
4091590Srgrimes			case '6':
4101590Srgrimes			case '7':
4111590Srgrimes			case '8':
4121590Srgrimes			case '9':
4131590Srgrimes				if ((argno = *p - '0') < argc - 1)
4141590Srgrimes					pbstr(argv[argno + 1]);
4151590Srgrimes				break;
4161590Srgrimes			case '*':
4171590Srgrimes				for (n = argc - 1; n > 2; n--) {
4181590Srgrimes					pbstr(argv[n]);
4191590Srgrimes					putback(',');
4201590Srgrimes				}
4211590Srgrimes				pbstr(argv[2]);
4221590Srgrimes				break;
4231590Srgrimes			default:
4241590Srgrimes				putback(*p);
4251590Srgrimes				putback('$');
4261590Srgrimes				break;
4271590Srgrimes			}
4281590Srgrimes			p--;
4291590Srgrimes		}
4301590Srgrimes		p--;
4311590Srgrimes	}
4321590Srgrimes	if (p == t)		       /* do last character */
4331590Srgrimes		putback(*p);
4341590Srgrimes}
4351590Srgrimes
4361590Srgrimes/*
4371590Srgrimes * dodefine - install definition in the table
4381590Srgrimes */
4391590Srgrimesvoid
4401590Srgrimesdodefine(name, defn)
4411590Srgrimesregister char *name;
4421590Srgrimesregister char *defn;
4431590Srgrimes{
4441590Srgrimes	register ndptr p;
4451590Srgrimes
4461590Srgrimes	if (!*name)
4471590Srgrimes		oops("null definition.");
4481590Srgrimes	if (STREQ(name, defn))
4491590Srgrimes		oops("%s: recursive definition.", name);
4501590Srgrimes	if ((p = lookup(name)) == nil)
4511590Srgrimes		p = addent(name);
4521590Srgrimes	else if (p->defn != null)
4531590Srgrimes		free((char *) p->defn);
4541590Srgrimes	if (!*defn)
4551590Srgrimes		p->defn = null;
4561590Srgrimes	else
4571590Srgrimes		p->defn = xstrdup(defn);
4581590Srgrimes	p->type = MACRTYPE;
4591590Srgrimes}
4601590Srgrimes
4611590Srgrimes/*
4621590Srgrimes * dodefn - push back a quoted definition of
4631590Srgrimes *      the given name.
4641590Srgrimes */
4651590Srgrimesvoid
4661590Srgrimesdodefn(name)
4671590Srgrimeschar *name;
4681590Srgrimes{
4691590Srgrimes	register ndptr p;
4701590Srgrimes
4711590Srgrimes	if ((p = lookup(name)) != nil && p->defn != null) {
4721590Srgrimes		putback(rquote);
4731590Srgrimes		pbstr(p->defn);
4741590Srgrimes		putback(lquote);
4751590Srgrimes	}
4761590Srgrimes}
4771590Srgrimes
4781590Srgrimes/*
4791590Srgrimes * dopushdef - install a definition in the hash table
4801590Srgrimes *      without removing a previous definition. Since
4811590Srgrimes *      each new entry is entered in *front* of the
4821590Srgrimes *      hash bucket, it hides a previous definition from
4831590Srgrimes *      lookup.
4841590Srgrimes */
4851590Srgrimesvoid
4861590Srgrimesdopushdef(name, defn)
4871590Srgrimesregister char *name;
4881590Srgrimesregister char *defn;
4891590Srgrimes{
4901590Srgrimes	register ndptr p;
4911590Srgrimes
4921590Srgrimes	if (!*name)
4931590Srgrimes		oops("null definition");
4941590Srgrimes	if (STREQ(name, defn))
4951590Srgrimes		oops("%s: recursive definition.", name);
4961590Srgrimes	p = addent(name);
4971590Srgrimes	if (!*defn)
4981590Srgrimes		p->defn = null;
4991590Srgrimes	else
5001590Srgrimes		p->defn = xstrdup(defn);
5011590Srgrimes	p->type = MACRTYPE;
5021590Srgrimes}
5031590Srgrimes
5041590Srgrimes/*
5051590Srgrimes * dodumpdef - dump the specified definitions in the hash
5061590Srgrimes *      table to stderr. If nothing is specified, the entire
5071590Srgrimes *      hash table is dumped.
5081590Srgrimes */
5091590Srgrimesvoid
5101590Srgrimesdodump(argv, argc)
5111590Srgrimesregister char *argv[];
5121590Srgrimesregister int argc;
5131590Srgrimes{
5141590Srgrimes	register int n;
5151590Srgrimes	ndptr p;
5161590Srgrimes
5171590Srgrimes	if (argc > 2) {
5181590Srgrimes		for (n = 2; n < argc; n++)
5191590Srgrimes			if ((p = lookup(argv[n])) != nil)
5201590Srgrimes				fprintf(stderr, dumpfmt, p->name,
5211590Srgrimes					p->defn);
5221590Srgrimes	}
5231590Srgrimes	else {
5241590Srgrimes		for (n = 0; n < HASHSIZE; n++)
5251590Srgrimes			for (p = hashtab[n]; p != nil; p = p->nxtptr)
5261590Srgrimes				fprintf(stderr, dumpfmt, p->name,
5271590Srgrimes					p->defn);
5281590Srgrimes	}
5291590Srgrimes}
5301590Srgrimes
5311590Srgrimes/*
5321590Srgrimes * doifelse - select one of two alternatives - loop.
5331590Srgrimes */
5341590Srgrimesvoid
5351590Srgrimesdoifelse(argv, argc)
5361590Srgrimesregister char *argv[];
5371590Srgrimesregister int argc;
5381590Srgrimes{
5391590Srgrimes	cycle {
5401590Srgrimes		if (STREQ(argv[2], argv[3]))
5411590Srgrimes			pbstr(argv[4]);
5421590Srgrimes		else if (argc == 6)
5431590Srgrimes			pbstr(argv[5]);
5441590Srgrimes		else if (argc > 6) {
5451590Srgrimes			argv += 3;
5461590Srgrimes			argc -= 3;
5471590Srgrimes			continue;
5481590Srgrimes		}
5491590Srgrimes		break;
5501590Srgrimes	}
5511590Srgrimes}
5521590Srgrimes
5531590Srgrimes/*
5541590Srgrimes * doinclude - include a given file.
5551590Srgrimes */
5561590Srgrimesint
5571590Srgrimesdoincl(ifile)
5581590Srgrimeschar *ifile;
5591590Srgrimes{
5601590Srgrimes	if (ilevel + 1 == MAXINP)
5611590Srgrimes		oops("too many include files.");
5621590Srgrimes	if ((infile[ilevel + 1] = fopen(ifile, "r")) != NULL) {
5631590Srgrimes		ilevel++;
5641590Srgrimes		bbase[ilevel] = bufbase = bp;
5651590Srgrimes		return (1);
5661590Srgrimes	}
5671590Srgrimes	else
5681590Srgrimes		return (0);
5691590Srgrimes}
5701590Srgrimes
5711590Srgrimes#ifdef EXTENDED
5721590Srgrimes/*
5731590Srgrimes * dopaste - include a given file without any
5741590Srgrimes *           macro processing.
5751590Srgrimes */
5761590Srgrimesint
5771590Srgrimesdopaste(pfile)
5781590Srgrimeschar *pfile;
5791590Srgrimes{
5801590Srgrimes	FILE *pf;
5811590Srgrimes	register int c;
5821590Srgrimes
5831590Srgrimes	if ((pf = fopen(pfile, "r")) != NULL) {
5841590Srgrimes		while ((c = getc(pf)) != EOF)
5851590Srgrimes			putc(c, active);
5861590Srgrimes		(void) fclose(pf);
5871590Srgrimes		return (1);
5881590Srgrimes	}
5891590Srgrimes	else
5901590Srgrimes		return (0);
5911590Srgrimes}
5921590Srgrimes#endif
5931590Srgrimes
5941590Srgrimes/*
5951590Srgrimes * dochq - change quote characters
5961590Srgrimes */
5971590Srgrimesvoid
5981590Srgrimesdochq(argv, argc)
5991590Srgrimesregister char *argv[];
6001590Srgrimesregister int argc;
6011590Srgrimes{
6021590Srgrimes	if (argc > 2) {
6031590Srgrimes		if (*argv[2])
6041590Srgrimes			lquote = *argv[2];
6051590Srgrimes		if (argc > 3) {
6061590Srgrimes			if (*argv[3])
6071590Srgrimes				rquote = *argv[3];
6081590Srgrimes		}
6091590Srgrimes		else
6101590Srgrimes			rquote = lquote;
6111590Srgrimes	}
6121590Srgrimes	else {
6131590Srgrimes		lquote = LQUOTE;
6141590Srgrimes		rquote = RQUOTE;
6151590Srgrimes	}
6161590Srgrimes}
6171590Srgrimes
6181590Srgrimes/*
6191590Srgrimes * dochc - change comment characters
6201590Srgrimes */
6211590Srgrimesvoid
6221590Srgrimesdochc(argv, argc)
6231590Srgrimesregister char *argv[];
6241590Srgrimesregister int argc;
6251590Srgrimes{
6261590Srgrimes	if (argc > 2) {
6271590Srgrimes		if (*argv[2])
6281590Srgrimes			scommt = *argv[2];
6291590Srgrimes		if (argc > 3) {
6301590Srgrimes			if (*argv[3])
6311590Srgrimes				ecommt = *argv[3];
6321590Srgrimes		}
6331590Srgrimes		else
6341590Srgrimes			ecommt = ECOMMT;
6351590Srgrimes	}
6361590Srgrimes	else {
6371590Srgrimes		scommt = SCOMMT;
6381590Srgrimes		ecommt = ECOMMT;
6391590Srgrimes	}
6401590Srgrimes}
6411590Srgrimes
6421590Srgrimes/*
6431590Srgrimes * dodivert - divert the output to a temporary file
6441590Srgrimes */
6451590Srgrimesvoid
6461590Srgrimesdodiv(n)
6471590Srgrimesregister int n;
6481590Srgrimes{
6491590Srgrimes	if (n < 0 || n >= MAXOUT)
6501590Srgrimes		n = 0;		       /* bitbucket */
6511590Srgrimes	if (outfile[n] == NULL) {
6521590Srgrimes		m4temp[UNIQUE] = n + '0';
6531590Srgrimes		if ((outfile[n] = fopen(m4temp, "w")) == NULL)
6541590Srgrimes			oops("%s: cannot divert.", m4temp);
6551590Srgrimes	}
6561590Srgrimes	oindex = n;
6571590Srgrimes	active = outfile[n];
6581590Srgrimes}
6591590Srgrimes
6601590Srgrimes/*
6611590Srgrimes * doundivert - undivert a specified output, or all
6621590Srgrimes *              other outputs, in numerical order.
6631590Srgrimes */
6641590Srgrimesvoid
6651590Srgrimesdoundiv(argv, argc)
6661590Srgrimesregister char *argv[];
6671590Srgrimesregister int argc;
6681590Srgrimes{
6691590Srgrimes	register int ind;
6701590Srgrimes	register int n;
6711590Srgrimes
6721590Srgrimes	if (argc > 2) {
6731590Srgrimes		for (ind = 2; ind < argc; ind++) {
6741590Srgrimes			n = atoi(argv[ind]);
6751590Srgrimes			if (n > 0 && n < MAXOUT && outfile[n] != NULL)
6761590Srgrimes				getdiv(n);
6771590Srgrimes
6781590Srgrimes		}
6791590Srgrimes	}
6801590Srgrimes	else
6811590Srgrimes		for (n = 1; n < MAXOUT; n++)
6821590Srgrimes			if (outfile[n] != NULL)
6831590Srgrimes				getdiv(n);
6841590Srgrimes}
6851590Srgrimes
6861590Srgrimes/*
6871590Srgrimes * dosub - select substring
6881590Srgrimes */
6891590Srgrimesvoid
6901590Srgrimesdosub(argv, argc)
6911590Srgrimesregister char *argv[];
6921590Srgrimesregister int argc;
6931590Srgrimes{
6945165Sache	register unsigned char *ap, *fc, *k;
6951590Srgrimes	register int nc;
6961590Srgrimes
6971590Srgrimes	if (argc < 5)
6981590Srgrimes		nc = MAXTOK;
6991590Srgrimes	else
7001590Srgrimes#ifdef EXPR
7011590Srgrimes		nc = expr(argv[4]);
7021590Srgrimes#else
7031590Srgrimes		nc = atoi(argv[4]);
7041590Srgrimes#endif
7051590Srgrimes	ap = argv[2];		       /* target string */
7061590Srgrimes#ifdef EXPR
7071590Srgrimes	fc = ap + expr(argv[3]);       /* first char */
7081590Srgrimes#else
7091590Srgrimes	fc = ap + atoi(argv[3]);       /* first char */
7101590Srgrimes#endif
7111590Srgrimes	if (fc >= ap && fc < ap + strlen(ap))
7121590Srgrimes		for (k = fc + min(nc, strlen(fc)) - 1; k >= fc; k--)
7131590Srgrimes			putback(*k);
7141590Srgrimes}
7151590Srgrimes
7161590Srgrimes/*
7171590Srgrimes * map:
7181590Srgrimes * map every character of s1 that is specified in from
7191590Srgrimes * into s3 and replace in s. (source s1 remains untouched)
7201590Srgrimes *
7211590Srgrimes * This is a standard implementation of map(s,from,to) function of ICON
7221590Srgrimes * language. Within mapvec, we replace every character of "from" with
7231590Srgrimes * the corresponding character in "to". If "to" is shorter than "from",
7241590Srgrimes * than the corresponding entries are null, which means that those
7251590Srgrimes * characters dissapear altogether. Furthermore, imagine
7261590Srgrimes * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
7271590Srgrimes * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
7281590Srgrimes * ultimately maps to `*'. In order to achieve this effect in an efficient
7291590Srgrimes * manner (i.e. without multiple passes over the destination string), we
7301590Srgrimes * loop over mapvec, starting with the initial source character. if the
7311590Srgrimes * character value (dch) in this location is different than the source
7321590Srgrimes * character (sch), sch becomes dch, once again to index into mapvec, until
7331590Srgrimes * the character value stabilizes (i.e. sch = dch, in other words
7341590Srgrimes * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
7351590Srgrimes * character, it will stabilize, since mapvec[0] == 0 at all times. At the
7361590Srgrimes * end, we restore mapvec* back to normal where mapvec[n] == n for
7371590Srgrimes * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
7381590Srgrimes * about 5 times faster than any algorithm that makes multiple passes over
7391590Srgrimes * destination string.
7401590Srgrimes */
7411590Srgrimesvoid
7421590Srgrimesmap(dest, src, from, to)
7431590Srgrimesregister char *dest;
7441590Srgrimesregister char *src;
7451590Srgrimesregister char *from;
7461590Srgrimesregister char *to;
7471590Srgrimes{
7481590Srgrimes	register char *tmp;
7491590Srgrimes	register char sch, dch;
7501590Srgrimes	static char mapvec[128] = {
7511590Srgrimes		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
7521590Srgrimes		12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
7531590Srgrimes		24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
7541590Srgrimes		36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
7551590Srgrimes		48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
7561590Srgrimes		60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
7571590Srgrimes		72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
7581590Srgrimes		84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
7591590Srgrimes		96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
7601590Srgrimes		108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
7611590Srgrimes		120, 121, 122, 123, 124, 125, 126, 127
7621590Srgrimes	};
7631590Srgrimes
7641590Srgrimes	if (*src) {
7651590Srgrimes		tmp = from;
7661590Srgrimes	/*
7671590Srgrimes	 * create a mapping between "from" and
7681590Srgrimes	 * "to"
7691590Srgrimes	 */
7701590Srgrimes		while (*from)
7711590Srgrimes			mapvec[*from++] = (*to) ? *to++ : (char) 0;
7721590Srgrimes
7731590Srgrimes		while (*src) {
7741590Srgrimes			sch = *src++;
7751590Srgrimes			dch = mapvec[sch];
7761590Srgrimes			while (dch != sch) {
7771590Srgrimes				sch = dch;
7781590Srgrimes				dch = mapvec[sch];
7791590Srgrimes			}
7801590Srgrimes			if (*dest = dch)
7811590Srgrimes				dest++;
7821590Srgrimes		}
7831590Srgrimes	/*
7841590Srgrimes	 * restore all the changed characters
7851590Srgrimes	 */
7861590Srgrimes		while (*tmp) {
7871590Srgrimes			mapvec[*tmp] = *tmp;
7881590Srgrimes			tmp++;
7891590Srgrimes		}
7901590Srgrimes	}
7911590Srgrimes	*dest = (char) 0;
7921590Srgrimes}
793