eval.c revision 228701
1228063Sbapt/*	$OpenBSD: eval.c,v 1.69 2011/03/24 11:23:08 espie Exp $	*/
295060Sjmallett/*	$NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $	*/
395060Sjmallett
41590Srgrimes/*
51590Srgrimes * Copyright (c) 1989, 1993
61590Srgrimes *	The Regents of the University of California.  All rights reserved.
71590Srgrimes *
81590Srgrimes * This code is derived from software contributed to Berkeley by
91590Srgrimes * Ozan Yigit at York University.
101590Srgrimes *
111590Srgrimes * Redistribution and use in source and binary forms, with or without
121590Srgrimes * modification, are permitted provided that the following conditions
131590Srgrimes * are met:
141590Srgrimes * 1. Redistributions of source code must retain the above copyright
151590Srgrimes *    notice, this list of conditions and the following disclaimer.
161590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
171590Srgrimes *    notice, this list of conditions and the following disclaimer in the
181590Srgrimes *    documentation and/or other materials provided with the distribution.
19228063Sbapt * 3. Neither the name of the University nor the names of its contributors
201590Srgrimes *    may be used to endorse or promote products derived from this software
211590Srgrimes *    without specific prior written permission.
221590Srgrimes *
231590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
241590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
251590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
261590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
271590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
281590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
291590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
301590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
311590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
321590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
331590Srgrimes * SUCH DAMAGE.
341590Srgrimes */
351590Srgrimes
3695060Sjmallett#include <sys/cdefs.h>
3795060Sjmallett__FBSDID("$FreeBSD: head/usr.bin/m4/eval.c 228701 2011-12-19 08:50:17Z bz $");
381590Srgrimes
39228063Sbapt
401590Srgrimes/*
411590Srgrimes * eval.c
421590Srgrimes * Facility: m4 macro processor
431590Srgrimes * by: oz
441590Srgrimes */
451590Srgrimes
461590Srgrimes#include <sys/types.h>
47228063Sbapt#include <err.h>
4895060Sjmallett#include <errno.h>
49228063Sbapt#include <limits.h>
5095060Sjmallett#include <unistd.h>
51228063Sbapt#include <stdint.h>
521590Srgrimes#include <stdio.h>
531590Srgrimes#include <stdlib.h>
5495060Sjmallett#include <stddef.h>
551590Srgrimes#include <string.h>
5695060Sjmallett#include <fcntl.h>
571590Srgrimes#include "mdef.h"
581590Srgrimes#include "stdd.h"
591590Srgrimes#include "extern.h"
601590Srgrimes#include "pathnames.h"
611590Srgrimes
6295060Sjmallettstatic void	dodefn(const char *);
6395060Sjmallettstatic void	dopushdef(const char *, const char *);
6495060Sjmallettstatic void	dodump(const char *[], int);
6595060Sjmallettstatic void	dotrace(const char *[], int, int);
6695060Sjmallettstatic void	doifelse(const char *[], int);
6795060Sjmallettstatic int	doincl(const char *);
6895060Sjmallettstatic int	dopaste(const char *);
6995060Sjmallettstatic void	dochq(const char *[], int);
7095060Sjmallettstatic void	dochc(const char *[], int);
71228063Sbaptstatic void	dom4wrap(const char *);
7295060Sjmallettstatic void	dodiv(int);
7395060Sjmallettstatic void	doundiv(const char *[], int);
7495060Sjmallettstatic void	dosub(const char *[], int);
7595060Sjmallettstatic void	map(char *, const char *, const char *, const char *);
7695060Sjmallettstatic const char *handledash(char *, char *, const char *);
7795060Sjmallettstatic void	expand_builtin(const char *[], int, int);
7895060Sjmallettstatic void	expand_macro(const char *[], int);
79228063Sbaptstatic void	dump_one_def(const char *, struct macro_definition *);
8095060Sjmallett
8195060Sjmallettunsigned long	expansion_id;
8295060Sjmallett
831590Srgrimes/*
8495060Sjmallett * eval - eval all macros and builtins calls
851590Srgrimes *	  argc - number of elements in argv.
861590Srgrimes *	  argv - element vector :
871590Srgrimes *			argv[0] = definition of a user
88228063Sbapt *				  macro or NULL if built-in.
891590Srgrimes *			argv[1] = name of the macro or
901590Srgrimes *				  built-in.
911590Srgrimes *			argv[2] = parameters to user-defined
921590Srgrimes *			   .	  macro or built-in.
931590Srgrimes *			   .
941590Srgrimes *
9595060Sjmallett * A call in the form of macro-or-builtin() will result in:
961590Srgrimes *			argv[0] = nullstr
971590Srgrimes *			argv[1] = macro-or-builtin
981590Srgrimes *			argv[2] = nullstr
9995060Sjmallett *
10095060Sjmallett * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
1011590Srgrimes */
1021590Srgrimesvoid
103228063Sbapteval(const char *argv[], int argc, int td, int is_traced)
1041590Srgrimes{
105228063Sbapt	size_t mark = SIZE_MAX;
10695060Sjmallett
10795060Sjmallett	expansion_id++;
108100014Sjmallett	if (td & RECDEF)
109228063Sbapt		m4errx(1, "expanding recursive definition for %s.", argv[1]);
110228063Sbapt	if (is_traced)
11195060Sjmallett		mark = trace(argv, argc, infile+ilevel);
11295060Sjmallett	if (td == MACRTYPE)
11395060Sjmallett		expand_macro(argv, argc);
11495060Sjmallett	else
11595060Sjmallett		expand_builtin(argv, argc, td);
116228063Sbapt	if (mark != SIZE_MAX)
11795060Sjmallett		finish_trace(mark);
11895060Sjmallett}
11995060Sjmallett
12095060Sjmallett/*
12195060Sjmallett * expand_builtin - evaluate built-in macros.
12295060Sjmallett */
12395060Sjmallettvoid
12495887Sjmallettexpand_builtin(const char *argv[], int argc, int td)
12595060Sjmallett{
12695060Sjmallett	int c, n;
12795060Sjmallett	int ac;
1281590Srgrimes	static int sysval = 0;
1291590Srgrimes
1301590Srgrimes#ifdef DEBUG
1311590Srgrimes	printf("argc = %d\n", argc);
1321590Srgrimes	for (n = 0; n < argc; n++)
1331590Srgrimes		printf("argv[%d] = %s\n", n, argv[n]);
13495060Sjmallett	fflush(stdout);
1351590Srgrimes#endif
13695060Sjmallett
1371590Srgrimes /*
1381590Srgrimes  * if argc == 3 and argv[2] is null, then we
1391590Srgrimes  * have macro-or-builtin() type call. We adjust
1401590Srgrimes  * argc to avoid further checking..
1411590Srgrimes  */
142228063Sbapt /* we keep the initial value for those built-ins that differentiate
143228063Sbapt  * between builtin() and builtin.
144228063Sbapt  */
145228063Sbapt	ac = argc;
14695060Sjmallett
147228063Sbapt	if (argc == 3 && !*(argv[2]) && !mimic_gnu)
1481590Srgrimes		argc--;
1491590Srgrimes
15095060Sjmallett	switch (td & TYPEMASK) {
1511590Srgrimes
1521590Srgrimes	case DEFITYPE:
1531590Srgrimes		if (argc > 2)
1541590Srgrimes			dodefine(argv[2], (argc > 3) ? argv[3] : null);
1551590Srgrimes		break;
1561590Srgrimes
1571590Srgrimes	case PUSDTYPE:
1581590Srgrimes		if (argc > 2)
1591590Srgrimes			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
1601590Srgrimes		break;
1611590Srgrimes
1621590Srgrimes	case DUMPTYPE:
1631590Srgrimes		dodump(argv, argc);
1641590Srgrimes		break;
1651590Srgrimes
16695060Sjmallett	case TRACEONTYPE:
16795060Sjmallett		dotrace(argv, argc, 1);
16895060Sjmallett		break;
16995060Sjmallett
17095060Sjmallett	case TRACEOFFTYPE:
17195060Sjmallett		dotrace(argv, argc, 0);
17295060Sjmallett		break;
17395060Sjmallett
1741590Srgrimes	case EXPRTYPE:
1751590Srgrimes	/*
1761590Srgrimes	 * doexpr - evaluate arithmetic
1771590Srgrimes	 * expression
1781590Srgrimes	 */
179228063Sbapt	{
180228063Sbapt		int base = 10;
181228063Sbapt		int maxdigits = 0;
182228063Sbapt		const char *errstr;
183228063Sbapt
184228063Sbapt		if (argc > 3) {
185228063Sbapt			base = strtonum(argv[3], 2, 36, &errstr);
186228063Sbapt			if (errstr) {
187228063Sbapt				m4errx(1, "expr: base %s invalid.", argv[3]);
188228063Sbapt			}
189228063Sbapt		}
190228063Sbapt		if (argc > 4) {
191228063Sbapt			maxdigits = strtonum(argv[4], 0, INT_MAX, &errstr);
192228063Sbapt			if (errstr) {
193228063Sbapt				m4errx(1, "expr: maxdigits %s invalid.", argv[4]);
194228063Sbapt			}
195228063Sbapt		}
1961590Srgrimes		if (argc > 2)
197228063Sbapt			pbnumbase(expr(argv[2]), base, maxdigits);
1981590Srgrimes		break;
199228063Sbapt	}
2001590Srgrimes
2011590Srgrimes	case IFELTYPE:
2021590Srgrimes		if (argc > 4)
2031590Srgrimes			doifelse(argv, argc);
2041590Srgrimes		break;
2051590Srgrimes
2061590Srgrimes	case IFDFTYPE:
2071590Srgrimes	/*
2081590Srgrimes	 * doifdef - select one of two
2091590Srgrimes	 * alternatives based on the existence of
2101590Srgrimes	 * another definition
2111590Srgrimes	 */
2121590Srgrimes		if (argc > 3) {
213228063Sbapt			if (lookup_macro_definition(argv[2]) != NULL)
2141590Srgrimes				pbstr(argv[3]);
2151590Srgrimes			else if (argc > 4)
2161590Srgrimes				pbstr(argv[4]);
2171590Srgrimes		}
2181590Srgrimes		break;
2191590Srgrimes
2201590Srgrimes	case LENGTYPE:
2211590Srgrimes	/*
2221590Srgrimes	 * dolen - find the length of the
2231590Srgrimes	 * argument
2241590Srgrimes	 */
22577378Sgshapiro		pbnum((argc > 2) ? strlen(argv[2]) : 0);
2261590Srgrimes		break;
2271590Srgrimes
2281590Srgrimes	case INCRTYPE:
2291590Srgrimes	/*
2301590Srgrimes	 * doincr - increment the value of the
2311590Srgrimes	 * argument
2321590Srgrimes	 */
2331590Srgrimes		if (argc > 2)
2341590Srgrimes			pbnum(atoi(argv[2]) + 1);
2351590Srgrimes		break;
2361590Srgrimes
2371590Srgrimes	case DECRTYPE:
2381590Srgrimes	/*
2391590Srgrimes	 * dodecr - decrement the value of the
2401590Srgrimes	 * argument
2411590Srgrimes	 */
2421590Srgrimes		if (argc > 2)
2431590Srgrimes			pbnum(atoi(argv[2]) - 1);
2441590Srgrimes		break;
2451590Srgrimes
2461590Srgrimes	case SYSCTYPE:
2471590Srgrimes	/*
2481590Srgrimes	 * dosys - execute system command
2498874Srgrimes	 */
250114368Stjr		if (argc > 2) {
251228063Sbapt			fflush(stdout);
2521590Srgrimes			sysval = system(argv[2]);
253114368Stjr		}
2541590Srgrimes		break;
2551590Srgrimes
2561590Srgrimes	case SYSVTYPE:
2571590Srgrimes	/*
2581590Srgrimes	 * dosysval - return value of the last
2591590Srgrimes	 * system call.
260100014Sjmallett	 *
2611590Srgrimes	 */
2621590Srgrimes		pbnum(sysval);
2631590Srgrimes		break;
2641590Srgrimes
26595060Sjmallett	case ESYSCMDTYPE:
26695060Sjmallett		if (argc > 2)
26795060Sjmallett			doesyscmd(argv[2]);
268228063Sbapt		break;
2691590Srgrimes	case INCLTYPE:
2701590Srgrimes		if (argc > 2)
271228701Sbz			if (!doincl(argv[2])) {
272228697Sbapt				if (mimic_gnu)
273228697Sbapt					warn("%s at line %lu: include(%s)",
274228697Sbapt					    CURRENT_NAME, CURRENT_LINE, argv[2]);
275228697Sbapt				else
276228697Sbapt					err(1, "%s at line %lu: include(%s)",
277228697Sbapt					    CURRENT_NAME, CURRENT_LINE, argv[2]);
278228701Sbz			}
2791590Srgrimes		break;
2801590Srgrimes
2811590Srgrimes	case SINCTYPE:
2821590Srgrimes		if (argc > 2)
2831590Srgrimes			(void) doincl(argv[2]);
2841590Srgrimes		break;
2851590Srgrimes#ifdef EXTENDED
2861590Srgrimes	case PASTTYPE:
2871590Srgrimes		if (argc > 2)
2881590Srgrimes			if (!dopaste(argv[2]))
289228063Sbapt				err(1, "%s at line %lu: paste(%s)",
29095060Sjmallett				    CURRENT_NAME, CURRENT_LINE, argv[2]);
2911590Srgrimes		break;
2921590Srgrimes
2931590Srgrimes	case SPASTYPE:
2941590Srgrimes		if (argc > 2)
2951590Srgrimes			(void) dopaste(argv[2]);
2961590Srgrimes		break;
297228063Sbapt	case FORMATTYPE:
298228063Sbapt		doformat(argv, argc);
299228063Sbapt		break;
3001590Srgrimes#endif
3011590Srgrimes	case CHNQTYPE:
302228063Sbapt		dochq(argv, ac);
3031590Srgrimes		break;
3041590Srgrimes
3051590Srgrimes	case CHNCTYPE:
306228063Sbapt		dochc(argv, argc);
3071590Srgrimes		break;
3081590Srgrimes
3091590Srgrimes	case SUBSTYPE:
3101590Srgrimes	/*
3111590Srgrimes	 * dosub - select substring
312100014Sjmallett	 *
3131590Srgrimes	 */
3141590Srgrimes		if (argc > 3)
3151590Srgrimes			dosub(argv, argc);
3161590Srgrimes		break;
3171590Srgrimes
3181590Srgrimes	case SHIFTYPE:
3191590Srgrimes	/*
3201590Srgrimes	 * doshift - push back all arguments
3211590Srgrimes	 * except the first one (i.e. skip
3221590Srgrimes	 * argv[2])
3231590Srgrimes	 */
3241590Srgrimes		if (argc > 3) {
3251590Srgrimes			for (n = argc - 1; n > 3; n--) {
32695060Sjmallett				pbstr(rquote);
3271590Srgrimes				pbstr(argv[n]);
32895060Sjmallett				pbstr(lquote);
329228063Sbapt				pushback(COMMA);
3301590Srgrimes			}
33195060Sjmallett			pbstr(rquote);
3321590Srgrimes			pbstr(argv[3]);
33395060Sjmallett			pbstr(lquote);
3341590Srgrimes		}
3351590Srgrimes		break;
3361590Srgrimes
3371590Srgrimes	case DIVRTYPE:
3381590Srgrimes		if (argc > 2 && (n = atoi(argv[2])) != 0)
3391590Srgrimes			dodiv(n);
3401590Srgrimes		else {
3411590Srgrimes			active = stdout;
3421590Srgrimes			oindex = 0;
3431590Srgrimes		}
3441590Srgrimes		break;
3451590Srgrimes
3461590Srgrimes	case UNDVTYPE:
3471590Srgrimes		doundiv(argv, argc);
3481590Srgrimes		break;
3491590Srgrimes
3501590Srgrimes	case DIVNTYPE:
3511590Srgrimes	/*
3521590Srgrimes	 * dodivnum - return the number of
3531590Srgrimes	 * current output diversion
3541590Srgrimes	 */
3551590Srgrimes		pbnum(oindex);
3561590Srgrimes		break;
3571590Srgrimes
3581590Srgrimes	case UNDFTYPE:
3591590Srgrimes	/*
3601590Srgrimes	 * doundefine - undefine a previously
3611590Srgrimes	 * defined macro(s) or m4 keyword(s).
3621590Srgrimes	 */
3631590Srgrimes		if (argc > 2)
3641590Srgrimes			for (n = 2; n < argc; n++)
365228063Sbapt				macro_undefine(argv[n]);
3661590Srgrimes		break;
3671590Srgrimes
3681590Srgrimes	case POPDTYPE:
3691590Srgrimes	/*
3701590Srgrimes	 * dopopdef - remove the topmost
3711590Srgrimes	 * definitions of macro(s) or m4
3721590Srgrimes	 * keyword(s).
3731590Srgrimes	 */
3741590Srgrimes		if (argc > 2)
3751590Srgrimes			for (n = 2; n < argc; n++)
376228063Sbapt				macro_popdef(argv[n]);
3771590Srgrimes		break;
3781590Srgrimes
3791590Srgrimes	case MKTMTYPE:
3801590Srgrimes	/*
3811590Srgrimes	 * dotemp - create a temporary file
3821590Srgrimes	 */
38395060Sjmallett		if (argc > 2) {
38495060Sjmallett			int fd;
38595060Sjmallett			char *temp;
38695060Sjmallett
38795060Sjmallett			temp = xstrdup(argv[2]);
388100014Sjmallett
38995060Sjmallett			fd = mkstemp(temp);
39095060Sjmallett			if (fd == -1)
391100014Sjmallett				err(1,
392100014Sjmallett	    "%s at line %lu: couldn't make temp file %s",
39395060Sjmallett	    CURRENT_NAME, CURRENT_LINE, argv[2]);
39495060Sjmallett			close(fd);
39595060Sjmallett			pbstr(temp);
39695060Sjmallett			free(temp);
39795060Sjmallett		}
3981590Srgrimes		break;
3991590Srgrimes
4001590Srgrimes	case TRNLTYPE:
4011590Srgrimes	/*
4021590Srgrimes	 * dotranslit - replace all characters in
4031590Srgrimes	 * the source string that appears in the
4041590Srgrimes	 * "from" string with the corresponding
4051590Srgrimes	 * characters in the "to" string.
4061590Srgrimes	 */
4071590Srgrimes		if (argc > 3) {
40895060Sjmallett			char *temp;
40995060Sjmallett
410228063Sbapt			temp = xalloc(strlen(argv[2])+1, NULL);
4111590Srgrimes			if (argc > 4)
4121590Srgrimes				map(temp, argv[2], argv[3], argv[4]);
4131590Srgrimes			else
4141590Srgrimes				map(temp, argv[2], argv[3], null);
4151590Srgrimes			pbstr(temp);
41695060Sjmallett			free(temp);
41795060Sjmallett		} else if (argc > 2)
4181590Srgrimes			pbstr(argv[2]);
4191590Srgrimes		break;
4201590Srgrimes
4211590Srgrimes	case INDXTYPE:
4221590Srgrimes	/*
4231590Srgrimes	 * doindex - find the index of the second
4241590Srgrimes	 * argument string in the first argument
4251590Srgrimes	 * string. -1 if not present.
4261590Srgrimes	 */
4271590Srgrimes		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
4281590Srgrimes		break;
4291590Srgrimes
4301590Srgrimes	case ERRPTYPE:
4311590Srgrimes	/*
4321590Srgrimes	 * doerrp - print the arguments to stderr
4331590Srgrimes	 * file
4341590Srgrimes	 */
4351590Srgrimes		if (argc > 2) {
4361590Srgrimes			for (n = 2; n < argc; n++)
4371590Srgrimes				fprintf(stderr, "%s ", argv[n]);
4381590Srgrimes			fprintf(stderr, "\n");
4391590Srgrimes		}
4401590Srgrimes		break;
4411590Srgrimes
4421590Srgrimes	case DNLNTYPE:
4431590Srgrimes	/*
4441590Srgrimes	 * dodnl - eat-up-to and including
4451590Srgrimes	 * newline
4461590Srgrimes	 */
4471590Srgrimes		while ((c = gpbc()) != '\n' && c != EOF)
4481590Srgrimes			;
4491590Srgrimes		break;
4501590Srgrimes
4511590Srgrimes	case M4WRTYPE:
4521590Srgrimes	/*
4531590Srgrimes	 * dom4wrap - set up for
4541590Srgrimes	 * wrap-up/wind-down activity
4551590Srgrimes	 */
456228063Sbapt		if (argc > 2)
457228063Sbapt			dom4wrap(argv[2]);
4581590Srgrimes		break;
4591590Srgrimes
4601590Srgrimes	case EXITTYPE:
4611590Srgrimes	/*
4621590Srgrimes	 * doexit - immediate exit from m4.
4631590Srgrimes	 */
4647896Sache		killdiv();
4651590Srgrimes		exit((argc > 2) ? atoi(argv[2]) : 0);
4661590Srgrimes		break;
4671590Srgrimes
4681590Srgrimes	case DEFNTYPE:
4691590Srgrimes		if (argc > 2)
4701590Srgrimes			for (n = 2; n < argc; n++)
4711590Srgrimes				dodefn(argv[n]);
4721590Srgrimes		break;
4731590Srgrimes
47495060Sjmallett	case INDIRTYPE:	/* Indirect call */
47595060Sjmallett		if (argc > 2)
47695060Sjmallett			doindir(argv, argc);
47738926Ssteve		break;
478100014Sjmallett
47995060Sjmallett	case BUILTINTYPE: /* Builtins only */
48095060Sjmallett		if (argc > 2)
48195060Sjmallett			dobuiltin(argv, argc);
48295060Sjmallett		break;
48338926Ssteve
48495060Sjmallett	case PATSTYPE:
48595060Sjmallett		if (argc > 2)
48695060Sjmallett			dopatsubst(argv, argc);
48795060Sjmallett		break;
48895060Sjmallett	case REGEXPTYPE:
48995060Sjmallett		if (argc > 2)
49095060Sjmallett			doregexp(argv, argc);
49195060Sjmallett		break;
49295060Sjmallett	case LINETYPE:
49395060Sjmallett		doprintlineno(infile+ilevel);
49495060Sjmallett		break;
49595060Sjmallett	case FILENAMETYPE:
49695060Sjmallett		doprintfilename(infile+ilevel);
49795060Sjmallett		break;
49895060Sjmallett	case SELFTYPE:
49995060Sjmallett		pbstr(rquote);
50095060Sjmallett		pbstr(argv[1]);
50195060Sjmallett		pbstr(lquote);
50295060Sjmallett		break;
5031590Srgrimes	default:
504228063Sbapt		m4errx(1, "eval: major botch.");
5051590Srgrimes		break;
5061590Srgrimes	}
5071590Srgrimes}
5081590Srgrimes
5091590Srgrimes/*
51095060Sjmallett * expand_macro - user-defined macro expansion
5111590Srgrimes */
5121590Srgrimesvoid
51395887Sjmallettexpand_macro(const char *argv[], int argc)
5141590Srgrimes{
51595060Sjmallett	const char *t;
51695060Sjmallett	const char *p;
51795060Sjmallett	int n;
51895060Sjmallett	int argno;
5191590Srgrimes
5201590Srgrimes	t = argv[0];		       /* defn string as a whole */
5211590Srgrimes	p = t;
5221590Srgrimes	while (*p)
5231590Srgrimes		p++;
5241590Srgrimes	p--;			       /* last character of defn */
5251590Srgrimes	while (p > t) {
5261590Srgrimes		if (*(p - 1) != ARGFLAG)
527228063Sbapt			PUSHBACK(*p);
5281590Srgrimes		else {
5291590Srgrimes			switch (*p) {
5301590Srgrimes
5311590Srgrimes			case '#':
5321590Srgrimes				pbnum(argc - 2);
5331590Srgrimes				break;
5341590Srgrimes			case '0':
5351590Srgrimes			case '1':
5361590Srgrimes			case '2':
5371590Srgrimes			case '3':
5381590Srgrimes			case '4':
5391590Srgrimes			case '5':
5401590Srgrimes			case '6':
5411590Srgrimes			case '7':
5421590Srgrimes			case '8':
5431590Srgrimes			case '9':
5441590Srgrimes				if ((argno = *p - '0') < argc - 1)
5451590Srgrimes					pbstr(argv[argno + 1]);
5461590Srgrimes				break;
5471590Srgrimes			case '*':
54895060Sjmallett				if (argc > 2) {
54995060Sjmallett					for (n = argc - 1; n > 2; n--) {
55095060Sjmallett						pbstr(argv[n]);
551228063Sbapt						pushback(COMMA);
55295060Sjmallett					}
55395060Sjmallett					pbstr(argv[2]);
554228063Sbapt				}
5551590Srgrimes				break;
55695060Sjmallett                        case '@':
55795060Sjmallett				if (argc > 2) {
55895060Sjmallett					for (n = argc - 1; n > 2; n--) {
55995060Sjmallett						pbstr(rquote);
56095060Sjmallett						pbstr(argv[n]);
56195060Sjmallett						pbstr(lquote);
562228063Sbapt						pushback(COMMA);
56395060Sjmallett					}
56495060Sjmallett					pbstr(rquote);
56595060Sjmallett					pbstr(argv[2]);
56695060Sjmallett					pbstr(lquote);
56724901Sjoerg				}
56895060Sjmallett                                break;
5691590Srgrimes			default:
570228063Sbapt				PUSHBACK(*p);
571228063Sbapt				PUSHBACK('$');
5721590Srgrimes				break;
5731590Srgrimes			}
5741590Srgrimes			p--;
5751590Srgrimes		}
5761590Srgrimes		p--;
5771590Srgrimes	}
5781590Srgrimes	if (p == t)		       /* do last character */
579228063Sbapt		PUSHBACK(*p);
5801590Srgrimes}
5811590Srgrimes
582228063Sbapt
5831590Srgrimes/*
5841590Srgrimes * dodefine - install definition in the table
5851590Srgrimes */
5861590Srgrimesvoid
58795887Sjmallettdodefine(const char *name, const char *defn)
5881590Srgrimes{
589228063Sbapt	if (!*name && !mimic_gnu)
590228063Sbapt		m4errx(1, "null definition.");
5911590Srgrimes	else
592228063Sbapt		macro_define(name, defn);
5931590Srgrimes}
5941590Srgrimes
5951590Srgrimes/*
5961590Srgrimes * dodefn - push back a quoted definition of
5971590Srgrimes *      the given name.
5981590Srgrimes */
59995060Sjmallettstatic void
60095887Sjmallettdodefn(const char *name)
6011590Srgrimes{
602228063Sbapt	struct macro_definition *p;
6031590Srgrimes
604228063Sbapt	if ((p = lookup_macro_definition(name)) != NULL) {
605228063Sbapt		if ((p->type & TYPEMASK) == MACRTYPE) {
60695060Sjmallett			pbstr(rquote);
60795060Sjmallett			pbstr(p->defn);
60895060Sjmallett			pbstr(lquote);
609228063Sbapt		} else {
610228063Sbapt			pbstr(p->defn);
61195060Sjmallett			pbstr(BUILTIN_MARKER);
61295060Sjmallett		}
6131590Srgrimes	}
6141590Srgrimes}
6151590Srgrimes
6161590Srgrimes/*
6171590Srgrimes * dopushdef - install a definition in the hash table
6181590Srgrimes *      without removing a previous definition. Since
6191590Srgrimes *      each new entry is entered in *front* of the
6201590Srgrimes *      hash bucket, it hides a previous definition from
6211590Srgrimes *      lookup.
6221590Srgrimes */
62395060Sjmallettstatic void
62495887Sjmallettdopushdef(const char *name, const char *defn)
6251590Srgrimes{
626228063Sbapt	if (!*name && !mimic_gnu)
627228063Sbapt		m4errx(1, "null definition.");
6281590Srgrimes	else
629228063Sbapt		macro_pushdef(name, defn);
6301590Srgrimes}
6311590Srgrimes
6321590Srgrimes/*
63395060Sjmallett * dump_one_def - dump the specified definition.
63495060Sjmallett */
63595060Sjmallettstatic void
636228063Sbaptdump_one_def(const char *name, struct macro_definition *p)
63795060Sjmallett{
638228063Sbapt	if (!traceout)
639228063Sbapt		traceout = stderr;
64095060Sjmallett	if (mimic_gnu) {
64195060Sjmallett		if ((p->type & TYPEMASK) == MACRTYPE)
642228063Sbapt			fprintf(traceout, "%s:\t%s\n", name, p->defn);
64395060Sjmallett		else {
644228063Sbapt			fprintf(traceout, "%s:\t<%s>\n", name, p->defn);
645228063Sbapt		}
64695060Sjmallett	} else
647228063Sbapt		fprintf(traceout, "`%s'\t`%s'\n", name, p->defn);
64895060Sjmallett}
64995060Sjmallett
65095060Sjmallett/*
6511590Srgrimes * dodumpdef - dump the specified definitions in the hash
6521590Srgrimes *      table to stderr. If nothing is specified, the entire
6531590Srgrimes *      hash table is dumped.
6541590Srgrimes */
65595060Sjmallettstatic void
65695887Sjmallettdodump(const char *argv[], int argc)
6571590Srgrimes{
65895060Sjmallett	int n;
659228063Sbapt	struct macro_definition *p;
6601590Srgrimes
6611590Srgrimes	if (argc > 2) {
6621590Srgrimes		for (n = 2; n < argc; n++)
663228063Sbapt			if ((p = lookup_macro_definition(argv[n])) != NULL)
664228063Sbapt				dump_one_def(argv[n], p);
665228063Sbapt	} else
666228063Sbapt		macro_for_all(dump_one_def);
6671590Srgrimes}
6681590Srgrimes
6691590Srgrimes/*
67095060Sjmallett * dotrace - mark some macros as traced/untraced depending upon on.
67195060Sjmallett */
67295060Sjmallettstatic void
67395887Sjmallettdotrace(const char *argv[], int argc, int on)
67495060Sjmallett{
67595060Sjmallett	int n;
67695060Sjmallett
67795060Sjmallett	if (argc > 2) {
67895060Sjmallett		for (n = 2; n < argc; n++)
67995060Sjmallett			mark_traced(argv[n], on);
68095060Sjmallett	} else
68195060Sjmallett		mark_traced(NULL, on);
68295060Sjmallett}
68395060Sjmallett
68495060Sjmallett/*
6851590Srgrimes * doifelse - select one of two alternatives - loop.
6861590Srgrimes */
68795060Sjmallettstatic void
68895887Sjmallettdoifelse(const char *argv[], int argc)
6891590Srgrimes{
6901590Srgrimes	cycle {
6911590Srgrimes		if (STREQ(argv[2], argv[3]))
6921590Srgrimes			pbstr(argv[4]);
6931590Srgrimes		else if (argc == 6)
6941590Srgrimes			pbstr(argv[5]);
6951590Srgrimes		else if (argc > 6) {
6961590Srgrimes			argv += 3;
6971590Srgrimes			argc -= 3;
6981590Srgrimes			continue;
6991590Srgrimes		}
7001590Srgrimes		break;
7011590Srgrimes	}
7021590Srgrimes}
7031590Srgrimes
7041590Srgrimes/*
7051590Srgrimes * doinclude - include a given file.
7061590Srgrimes */
70795060Sjmallettstatic int
70895887Sjmallettdoincl(const char *ifile)
7091590Srgrimes{
7101590Srgrimes	if (ilevel + 1 == MAXINP)
711228063Sbapt		m4errx(1, "too many include files.");
71295060Sjmallett	if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
7131590Srgrimes		ilevel++;
7141590Srgrimes		bbase[ilevel] = bufbase = bp;
7151590Srgrimes		return (1);
71695060Sjmallett	} else
7171590Srgrimes		return (0);
7181590Srgrimes}
7191590Srgrimes
7201590Srgrimes#ifdef EXTENDED
7211590Srgrimes/*
7221590Srgrimes * dopaste - include a given file without any
7231590Srgrimes *           macro processing.
7241590Srgrimes */
72595060Sjmallettstatic int
72695887Sjmallettdopaste(const char *pfile)
7271590Srgrimes{
7281590Srgrimes	FILE *pf;
72995060Sjmallett	int c;
7301590Srgrimes
7311590Srgrimes	if ((pf = fopen(pfile, "r")) != NULL) {
732228063Sbapt		if (synch_lines)
733228063Sbapt		    fprintf(active, "#line 1 \"%s\"\n", pfile);
7341590Srgrimes		while ((c = getc(pf)) != EOF)
7351590Srgrimes			putc(c, active);
7361590Srgrimes		(void) fclose(pf);
737228063Sbapt		emit_synchline();
7381590Srgrimes		return (1);
73995060Sjmallett	} else
7401590Srgrimes		return (0);
7411590Srgrimes}
7421590Srgrimes#endif
7431590Srgrimes
744228063Sbapt/*
745228063Sbapt * dochq - change quote characters
746228063Sbapt */
74795060Sjmallettstatic void
748228063Sbaptdochq(const char *argv[], int ac)
74995060Sjmallett{
75095060Sjmallett	if (ac == 2) {
751228063Sbapt		lquote[0] = LQUOTE; lquote[1] = EOS;
752228063Sbapt		rquote[0] = RQUOTE; rquote[1] = EOS;
75395060Sjmallett	} else {
75495060Sjmallett		strlcpy(lquote, argv[2], sizeof(lquote));
755228063Sbapt		if (ac > 3) {
75695060Sjmallett			strlcpy(rquote, argv[3], sizeof(rquote));
757228063Sbapt		} else {
758228063Sbapt			rquote[0] = ECOMMT; rquote[1] = EOS;
759228063Sbapt		}
76095060Sjmallett	}
76195060Sjmallett}
76295060Sjmallett
7631590Srgrimes/*
764228063Sbapt * dochc - change comment characters
7651590Srgrimes */
76695060Sjmallettstatic void
767228063Sbaptdochc(const char *argv[], int argc)
7681590Srgrimes{
769228063Sbapt/* XXX Note that there is no difference between no argument and a single
770228063Sbapt * empty argument.
771228063Sbapt */
772228063Sbapt	if (argc == 2) {
77395060Sjmallett		scommt[0] = EOS;
77495060Sjmallett		ecommt[0] = EOS;
77595060Sjmallett	} else {
776228063Sbapt		strlcpy(scommt, argv[2], sizeof(scommt));
777228063Sbapt		if (argc == 3) {
778228063Sbapt			ecommt[0] = ECOMMT; ecommt[1] = EOS;
779228063Sbapt		} else {
78095060Sjmallett			strlcpy(ecommt, argv[3], sizeof(ecommt));
781228063Sbapt		}
7821590Srgrimes	}
7831590Srgrimes}
784228063Sbapt
7851590Srgrimes/*
786228063Sbapt * dom4wrap - expand text at EOF
7871590Srgrimes */
78895060Sjmallettstatic void
789228063Sbaptdom4wrap(const char *text)
7901590Srgrimes{
791228063Sbapt	if (wrapindex >= maxwraps) {
792228063Sbapt		if (maxwraps == 0)
793228063Sbapt			maxwraps = 16;
7941590Srgrimes		else
795228063Sbapt			maxwraps *= 2;
796228063Sbapt		m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps),
797228063Sbapt		   "too many m4wraps");
7981590Srgrimes	}
799228063Sbapt	m4wraps[wrapindex++] = xstrdup(text);
8001590Srgrimes}
8011590Srgrimes
8021590Srgrimes/*
8031590Srgrimes * dodivert - divert the output to a temporary file
8041590Srgrimes */
80595060Sjmallettstatic void
80695887Sjmallettdodiv(int n)
8071590Srgrimes{
80895060Sjmallett	int fd;
80995060Sjmallett
81028386Sjlemon	oindex = n;
81195060Sjmallett	if (n >= maxout) {
81295060Sjmallett		if (mimic_gnu)
81395060Sjmallett			resizedivs(n + 10);
81495060Sjmallett		else
81595060Sjmallett			n = 0;		/* bitbucket */
816228063Sbapt	}
81795060Sjmallett
81895060Sjmallett	if (n < 0)
8191590Srgrimes		n = 0;		       /* bitbucket */
8201590Srgrimes	if (outfile[n] == NULL) {
82195060Sjmallett		char fname[] = _PATH_DIVNAME;
82295060Sjmallett
823228063Sbapt		if ((fd = mkstemp(fname)) < 0 ||
82495060Sjmallett			(outfile[n] = fdopen(fd, "w+")) == NULL)
82595060Sjmallett				err(1, "%s: cannot divert", fname);
82695060Sjmallett		if (unlink(fname) == -1)
82795060Sjmallett			err(1, "%s: cannot unlink", fname);
8281590Srgrimes	}
8291590Srgrimes	active = outfile[n];
8301590Srgrimes}
8311590Srgrimes
8321590Srgrimes/*
8331590Srgrimes * doundivert - undivert a specified output, or all
8341590Srgrimes *              other outputs, in numerical order.
8351590Srgrimes */
83695060Sjmallettstatic void
83795887Sjmallettdoundiv(const char *argv[], int argc)
8381590Srgrimes{
83995060Sjmallett	int ind;
84095060Sjmallett	int n;
8411590Srgrimes
8421590Srgrimes	if (argc > 2) {
8431590Srgrimes		for (ind = 2; ind < argc; ind++) {
844228063Sbapt			const char *errstr;
845228063Sbapt			n = strtonum(argv[ind], 1, INT_MAX, &errstr);
846228063Sbapt			if (errstr) {
847228063Sbapt				if (errno == EINVAL && mimic_gnu)
848228063Sbapt					getdivfile(argv[ind]);
849228063Sbapt			} else {
850228063Sbapt				if (n < maxout && outfile[n] != NULL)
851228063Sbapt					getdiv(n);
852228063Sbapt			}
8531590Srgrimes		}
8541590Srgrimes	}
8551590Srgrimes	else
85695060Sjmallett		for (n = 1; n < maxout; n++)
8571590Srgrimes			if (outfile[n] != NULL)
8581590Srgrimes				getdiv(n);
8591590Srgrimes}
8601590Srgrimes
8611590Srgrimes/*
8621590Srgrimes * dosub - select substring
8631590Srgrimes */
86495060Sjmallettstatic void
86595887Sjmallettdosub(const char *argv[], int argc)
8661590Srgrimes{
86795060Sjmallett	const char *ap, *fc, *k;
86895060Sjmallett	int nc;
8691590Srgrimes
87076822Sgshapiro	ap = argv[2];		       /* target string */
87176822Sgshapiro#ifdef EXPR
87276822Sgshapiro	fc = ap + expr(argv[3]);       /* first char */
87376822Sgshapiro#else
87476822Sgshapiro	fc = ap + atoi(argv[3]);       /* first char */
87576822Sgshapiro#endif
87695060Sjmallett	nc = strlen(fc);
87795060Sjmallett	if (argc >= 5)
8781590Srgrimes#ifdef EXPR
87995060Sjmallett		nc = min(nc, expr(argv[4]));
8801590Srgrimes#else
88195060Sjmallett		nc = min(nc, atoi(argv[4]));
8821590Srgrimes#endif
8831590Srgrimes	if (fc >= ap && fc < ap + strlen(ap))
88476822Sgshapiro		for (k = fc + nc - 1; k >= fc; k--)
885228063Sbapt			pushback(*k);
8861590Srgrimes}
8871590Srgrimes
8881590Srgrimes/*
8891590Srgrimes * map:
8901590Srgrimes * map every character of s1 that is specified in from
8911590Srgrimes * into s3 and replace in s. (source s1 remains untouched)
8921590Srgrimes *
893228063Sbapt * This is derived from the a standard implementation of map(s,from,to)
894228063Sbapt * function of ICON language. Within mapvec, we replace every character
895228063Sbapt * of "from" with the corresponding character in "to".
896228063Sbapt * If "to" is shorter than "from", than the corresponding entries are null,
897228063Sbapt * which means that those characters dissapear altogether.
8981590Srgrimes */
89995060Sjmallettstatic void
90095887Sjmallettmap(char *dest, const char *src, const char *from, const char *to)
9011590Srgrimes{
90295060Sjmallett	const char *tmp;
90395060Sjmallett	unsigned char sch, dch;
90495060Sjmallett	static char frombis[257];
90595060Sjmallett	static char tobis[257];
906228063Sbapt	int i;
907228063Sbapt	char seen[256];
90895060Sjmallett	static unsigned char mapvec[256] = {
90995060Sjmallett	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
91095060Sjmallett	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
91195060Sjmallett	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
91295060Sjmallett	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
91395060Sjmallett	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
91495060Sjmallett	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
91595060Sjmallett	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
91695060Sjmallett	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
91795060Sjmallett	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
91895060Sjmallett	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
91995060Sjmallett	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
92095060Sjmallett	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
92195060Sjmallett	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
92295060Sjmallett	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
92395060Sjmallett	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
92495060Sjmallett	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
92595060Sjmallett	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
92695060Sjmallett	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
9271590Srgrimes	};
9281590Srgrimes
9291590Srgrimes	if (*src) {
93095060Sjmallett		if (mimic_gnu) {
93195060Sjmallett			/*
93295060Sjmallett			 * expand character ranges on the fly
93395060Sjmallett			 */
93495060Sjmallett			from = handledash(frombis, frombis + 256, from);
93595060Sjmallett			to = handledash(tobis, tobis + 256, to);
93695060Sjmallett		}
9371590Srgrimes		tmp = from;
9381590Srgrimes	/*
9391590Srgrimes	 * create a mapping between "from" and
9401590Srgrimes	 * "to"
9411590Srgrimes	 */
942228063Sbapt		for (i = 0; i < 256; i++)
943228063Sbapt			seen[i] = 0;
944228063Sbapt		while (*from) {
945228063Sbapt			if (!seen[(unsigned char)(*from)]) {
946228063Sbapt				mapvec[(unsigned char)(*from)] = (unsigned char)(*to);
947228063Sbapt				seen[(unsigned char)(*from)] = 1;
948228063Sbapt			}
949228063Sbapt			from++;
950228063Sbapt			if (*to)
951228063Sbapt				to++;
952228063Sbapt		}
9531590Srgrimes
9541590Srgrimes		while (*src) {
95595060Sjmallett			sch = (unsigned char)(*src++);
9561590Srgrimes			dch = mapvec[sch];
95795060Sjmallett			if ((*dest = (char)dch))
9581590Srgrimes				dest++;
9591590Srgrimes		}
9601590Srgrimes	/*
9611590Srgrimes	 * restore all the changed characters
9621590Srgrimes	 */
9631590Srgrimes		while (*tmp) {
96495060Sjmallett			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
9651590Srgrimes			tmp++;
9661590Srgrimes		}
9671590Srgrimes	}
96895060Sjmallett	*dest = '\0';
9691590Srgrimes}
97095060Sjmallett
97195060Sjmallett
97295060Sjmallett/*
97395060Sjmallett * handledash:
97495060Sjmallett *  use buffer to copy the src string, expanding character ranges
97595060Sjmallett * on the way.
97695060Sjmallett */
97795060Sjmallettstatic const char *
97895887Sjmalletthandledash(char *buffer, char *end, const char *src)
97995060Sjmallett{
98095060Sjmallett	char *p;
981100014Sjmallett
98295060Sjmallett	p = buffer;
98395060Sjmallett	while(*src) {
98495060Sjmallett		if (src[1] == '-' && src[2]) {
98595060Sjmallett			unsigned char i;
986228063Sbapt			if ((unsigned char)src[0] <= (unsigned char)src[2]) {
987228063Sbapt				for (i = (unsigned char)src[0];
988228063Sbapt				    i <= (unsigned char)src[2]; i++) {
989228063Sbapt					*p++ = i;
990228063Sbapt					if (p == end) {
991228063Sbapt						*p = '\0';
992228063Sbapt						return buffer;
993228063Sbapt					}
99495060Sjmallett				}
995228063Sbapt			} else {
996228063Sbapt				for (i = (unsigned char)src[0];
997228063Sbapt				    i >= (unsigned char)src[2]; i--) {
998228063Sbapt					*p++ = i;
999228063Sbapt					if (p == end) {
1000228063Sbapt						*p = '\0';
1001228063Sbapt						return buffer;
1002228063Sbapt					}
1003228063Sbapt				}
100495060Sjmallett			}
100595060Sjmallett			src += 3;
100695060Sjmallett		} else
100795060Sjmallett			*p++ = *src++;
100895060Sjmallett		if (p == end)
100995060Sjmallett			break;
101095060Sjmallett	}
101195060Sjmallett	*p = '\0';
101295060Sjmallett	return buffer;
101395060Sjmallett}
1014