1250226Sjkim/*	$OpenBSD: eval.c,v 1.70 2012/04/12 17:00:11 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$");
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])) {
272234310Sbapt				if (mimic_gnu) {
273228697Sbapt					warn("%s at line %lu: include(%s)",
274228697Sbapt					    CURRENT_NAME, CURRENT_LINE, argv[2]);
275234310Sbapt					exit_code = 1;
276234310Sbapt				} else
277228697Sbapt					err(1, "%s at line %lu: include(%s)",
278228697Sbapt					    CURRENT_NAME, CURRENT_LINE, argv[2]);
279228701Sbz			}
2801590Srgrimes		break;
2811590Srgrimes
2821590Srgrimes	case SINCTYPE:
2831590Srgrimes		if (argc > 2)
2841590Srgrimes			(void) doincl(argv[2]);
2851590Srgrimes		break;
2861590Srgrimes#ifdef EXTENDED
2871590Srgrimes	case PASTTYPE:
2881590Srgrimes		if (argc > 2)
2891590Srgrimes			if (!dopaste(argv[2]))
290228063Sbapt				err(1, "%s at line %lu: paste(%s)",
29195060Sjmallett				    CURRENT_NAME, CURRENT_LINE, argv[2]);
2921590Srgrimes		break;
2931590Srgrimes
2941590Srgrimes	case SPASTYPE:
2951590Srgrimes		if (argc > 2)
2961590Srgrimes			(void) dopaste(argv[2]);
2971590Srgrimes		break;
298228063Sbapt	case FORMATTYPE:
299228063Sbapt		doformat(argv, argc);
300228063Sbapt		break;
3011590Srgrimes#endif
3021590Srgrimes	case CHNQTYPE:
303228063Sbapt		dochq(argv, ac);
3041590Srgrimes		break;
3051590Srgrimes
3061590Srgrimes	case CHNCTYPE:
307228063Sbapt		dochc(argv, argc);
3081590Srgrimes		break;
3091590Srgrimes
3101590Srgrimes	case SUBSTYPE:
3111590Srgrimes	/*
3121590Srgrimes	 * dosub - select substring
313100014Sjmallett	 *
3141590Srgrimes	 */
3151590Srgrimes		if (argc > 3)
3161590Srgrimes			dosub(argv, argc);
3171590Srgrimes		break;
3181590Srgrimes
3191590Srgrimes	case SHIFTYPE:
3201590Srgrimes	/*
3211590Srgrimes	 * doshift - push back all arguments
3221590Srgrimes	 * except the first one (i.e. skip
3231590Srgrimes	 * argv[2])
3241590Srgrimes	 */
3251590Srgrimes		if (argc > 3) {
3261590Srgrimes			for (n = argc - 1; n > 3; n--) {
32795060Sjmallett				pbstr(rquote);
3281590Srgrimes				pbstr(argv[n]);
32995060Sjmallett				pbstr(lquote);
330228063Sbapt				pushback(COMMA);
3311590Srgrimes			}
33295060Sjmallett			pbstr(rquote);
3331590Srgrimes			pbstr(argv[3]);
33495060Sjmallett			pbstr(lquote);
3351590Srgrimes		}
3361590Srgrimes		break;
3371590Srgrimes
3381590Srgrimes	case DIVRTYPE:
3391590Srgrimes		if (argc > 2 && (n = atoi(argv[2])) != 0)
3401590Srgrimes			dodiv(n);
3411590Srgrimes		else {
3421590Srgrimes			active = stdout;
3431590Srgrimes			oindex = 0;
3441590Srgrimes		}
3451590Srgrimes		break;
3461590Srgrimes
3471590Srgrimes	case UNDVTYPE:
3481590Srgrimes		doundiv(argv, argc);
3491590Srgrimes		break;
3501590Srgrimes
3511590Srgrimes	case DIVNTYPE:
3521590Srgrimes	/*
3531590Srgrimes	 * dodivnum - return the number of
3541590Srgrimes	 * current output diversion
3551590Srgrimes	 */
3561590Srgrimes		pbnum(oindex);
3571590Srgrimes		break;
3581590Srgrimes
3591590Srgrimes	case UNDFTYPE:
3601590Srgrimes	/*
3611590Srgrimes	 * doundefine - undefine a previously
3621590Srgrimes	 * defined macro(s) or m4 keyword(s).
3631590Srgrimes	 */
3641590Srgrimes		if (argc > 2)
3651590Srgrimes			for (n = 2; n < argc; n++)
366228063Sbapt				macro_undefine(argv[n]);
3671590Srgrimes		break;
3681590Srgrimes
3691590Srgrimes	case POPDTYPE:
3701590Srgrimes	/*
3711590Srgrimes	 * dopopdef - remove the topmost
3721590Srgrimes	 * definitions of macro(s) or m4
3731590Srgrimes	 * keyword(s).
3741590Srgrimes	 */
3751590Srgrimes		if (argc > 2)
3761590Srgrimes			for (n = 2; n < argc; n++)
377228063Sbapt				macro_popdef(argv[n]);
3781590Srgrimes		break;
3791590Srgrimes
3801590Srgrimes	case MKTMTYPE:
3811590Srgrimes	/*
3821590Srgrimes	 * dotemp - create a temporary file
3831590Srgrimes	 */
38495060Sjmallett		if (argc > 2) {
38595060Sjmallett			int fd;
38695060Sjmallett			char *temp;
38795060Sjmallett
38895060Sjmallett			temp = xstrdup(argv[2]);
389100014Sjmallett
39095060Sjmallett			fd = mkstemp(temp);
39195060Sjmallett			if (fd == -1)
392100014Sjmallett				err(1,
393100014Sjmallett	    "%s at line %lu: couldn't make temp file %s",
39495060Sjmallett	    CURRENT_NAME, CURRENT_LINE, argv[2]);
39595060Sjmallett			close(fd);
39695060Sjmallett			pbstr(temp);
39795060Sjmallett			free(temp);
39895060Sjmallett		}
3991590Srgrimes		break;
4001590Srgrimes
4011590Srgrimes	case TRNLTYPE:
4021590Srgrimes	/*
4031590Srgrimes	 * dotranslit - replace all characters in
4041590Srgrimes	 * the source string that appears in the
4051590Srgrimes	 * "from" string with the corresponding
4061590Srgrimes	 * characters in the "to" string.
4071590Srgrimes	 */
4081590Srgrimes		if (argc > 3) {
40995060Sjmallett			char *temp;
41095060Sjmallett
411228063Sbapt			temp = xalloc(strlen(argv[2])+1, NULL);
4121590Srgrimes			if (argc > 4)
4131590Srgrimes				map(temp, argv[2], argv[3], argv[4]);
4141590Srgrimes			else
4151590Srgrimes				map(temp, argv[2], argv[3], null);
4161590Srgrimes			pbstr(temp);
41795060Sjmallett			free(temp);
41895060Sjmallett		} else if (argc > 2)
4191590Srgrimes			pbstr(argv[2]);
4201590Srgrimes		break;
4211590Srgrimes
4221590Srgrimes	case INDXTYPE:
4231590Srgrimes	/*
4241590Srgrimes	 * doindex - find the index of the second
4251590Srgrimes	 * argument string in the first argument
4261590Srgrimes	 * string. -1 if not present.
4271590Srgrimes	 */
4281590Srgrimes		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
4291590Srgrimes		break;
4301590Srgrimes
4311590Srgrimes	case ERRPTYPE:
4321590Srgrimes	/*
4331590Srgrimes	 * doerrp - print the arguments to stderr
4341590Srgrimes	 * file
4351590Srgrimes	 */
4361590Srgrimes		if (argc > 2) {
4371590Srgrimes			for (n = 2; n < argc; n++)
4381590Srgrimes				fprintf(stderr, "%s ", argv[n]);
4391590Srgrimes			fprintf(stderr, "\n");
4401590Srgrimes		}
4411590Srgrimes		break;
4421590Srgrimes
4431590Srgrimes	case DNLNTYPE:
4441590Srgrimes	/*
4451590Srgrimes	 * dodnl - eat-up-to and including
4461590Srgrimes	 * newline
4471590Srgrimes	 */
4481590Srgrimes		while ((c = gpbc()) != '\n' && c != EOF)
4491590Srgrimes			;
4501590Srgrimes		break;
4511590Srgrimes
4521590Srgrimes	case M4WRTYPE:
4531590Srgrimes	/*
4541590Srgrimes	 * dom4wrap - set up for
4551590Srgrimes	 * wrap-up/wind-down activity
4561590Srgrimes	 */
457228063Sbapt		if (argc > 2)
458228063Sbapt			dom4wrap(argv[2]);
4591590Srgrimes		break;
4601590Srgrimes
4611590Srgrimes	case EXITTYPE:
4621590Srgrimes	/*
4631590Srgrimes	 * doexit - immediate exit from m4.
4641590Srgrimes	 */
4657896Sache		killdiv();
4661590Srgrimes		exit((argc > 2) ? atoi(argv[2]) : 0);
4671590Srgrimes		break;
4681590Srgrimes
4691590Srgrimes	case DEFNTYPE:
4701590Srgrimes		if (argc > 2)
4711590Srgrimes			for (n = 2; n < argc; n++)
4721590Srgrimes				dodefn(argv[n]);
4731590Srgrimes		break;
4741590Srgrimes
47595060Sjmallett	case INDIRTYPE:	/* Indirect call */
47695060Sjmallett		if (argc > 2)
47795060Sjmallett			doindir(argv, argc);
47838926Ssteve		break;
479100014Sjmallett
48095060Sjmallett	case BUILTINTYPE: /* Builtins only */
48195060Sjmallett		if (argc > 2)
48295060Sjmallett			dobuiltin(argv, argc);
48395060Sjmallett		break;
48438926Ssteve
48595060Sjmallett	case PATSTYPE:
48695060Sjmallett		if (argc > 2)
48795060Sjmallett			dopatsubst(argv, argc);
48895060Sjmallett		break;
48995060Sjmallett	case REGEXPTYPE:
49095060Sjmallett		if (argc > 2)
49195060Sjmallett			doregexp(argv, argc);
49295060Sjmallett		break;
49395060Sjmallett	case LINETYPE:
49495060Sjmallett		doprintlineno(infile+ilevel);
49595060Sjmallett		break;
49695060Sjmallett	case FILENAMETYPE:
49795060Sjmallett		doprintfilename(infile+ilevel);
49895060Sjmallett		break;
49995060Sjmallett	case SELFTYPE:
50095060Sjmallett		pbstr(rquote);
50195060Sjmallett		pbstr(argv[1]);
50295060Sjmallett		pbstr(lquote);
50395060Sjmallett		break;
5041590Srgrimes	default:
505228063Sbapt		m4errx(1, "eval: major botch.");
5061590Srgrimes		break;
5071590Srgrimes	}
5081590Srgrimes}
5091590Srgrimes
5101590Srgrimes/*
51195060Sjmallett * expand_macro - user-defined macro expansion
5121590Srgrimes */
5131590Srgrimesvoid
51495887Sjmallettexpand_macro(const char *argv[], int argc)
5151590Srgrimes{
51695060Sjmallett	const char *t;
51795060Sjmallett	const char *p;
51895060Sjmallett	int n;
51995060Sjmallett	int argno;
5201590Srgrimes
5211590Srgrimes	t = argv[0];		       /* defn string as a whole */
5221590Srgrimes	p = t;
5231590Srgrimes	while (*p)
5241590Srgrimes		p++;
5251590Srgrimes	p--;			       /* last character of defn */
5261590Srgrimes	while (p > t) {
5271590Srgrimes		if (*(p - 1) != ARGFLAG)
528228063Sbapt			PUSHBACK(*p);
5291590Srgrimes		else {
5301590Srgrimes			switch (*p) {
5311590Srgrimes
5321590Srgrimes			case '#':
5331590Srgrimes				pbnum(argc - 2);
5341590Srgrimes				break;
5351590Srgrimes			case '0':
5361590Srgrimes			case '1':
5371590Srgrimes			case '2':
5381590Srgrimes			case '3':
5391590Srgrimes			case '4':
5401590Srgrimes			case '5':
5411590Srgrimes			case '6':
5421590Srgrimes			case '7':
5431590Srgrimes			case '8':
5441590Srgrimes			case '9':
5451590Srgrimes				if ((argno = *p - '0') < argc - 1)
5461590Srgrimes					pbstr(argv[argno + 1]);
5471590Srgrimes				break;
5481590Srgrimes			case '*':
54995060Sjmallett				if (argc > 2) {
55095060Sjmallett					for (n = argc - 1; n > 2; n--) {
55195060Sjmallett						pbstr(argv[n]);
552228063Sbapt						pushback(COMMA);
55395060Sjmallett					}
55495060Sjmallett					pbstr(argv[2]);
555228063Sbapt				}
5561590Srgrimes				break;
55795060Sjmallett                        case '@':
55895060Sjmallett				if (argc > 2) {
55995060Sjmallett					for (n = argc - 1; n > 2; n--) {
56095060Sjmallett						pbstr(rquote);
56195060Sjmallett						pbstr(argv[n]);
56295060Sjmallett						pbstr(lquote);
563228063Sbapt						pushback(COMMA);
56495060Sjmallett					}
56595060Sjmallett					pbstr(rquote);
56695060Sjmallett					pbstr(argv[2]);
56795060Sjmallett					pbstr(lquote);
56824901Sjoerg				}
56995060Sjmallett                                break;
5701590Srgrimes			default:
571228063Sbapt				PUSHBACK(*p);
572228063Sbapt				PUSHBACK('$');
5731590Srgrimes				break;
5741590Srgrimes			}
5751590Srgrimes			p--;
5761590Srgrimes		}
5771590Srgrimes		p--;
5781590Srgrimes	}
5791590Srgrimes	if (p == t)		       /* do last character */
580228063Sbapt		PUSHBACK(*p);
5811590Srgrimes}
5821590Srgrimes
583228063Sbapt
5841590Srgrimes/*
5851590Srgrimes * dodefine - install definition in the table
5861590Srgrimes */
5871590Srgrimesvoid
58895887Sjmallettdodefine(const char *name, const char *defn)
5891590Srgrimes{
590228063Sbapt	if (!*name && !mimic_gnu)
591228063Sbapt		m4errx(1, "null definition.");
5921590Srgrimes	else
593228063Sbapt		macro_define(name, defn);
5941590Srgrimes}
5951590Srgrimes
5961590Srgrimes/*
5971590Srgrimes * dodefn - push back a quoted definition of
5981590Srgrimes *      the given name.
5991590Srgrimes */
60095060Sjmallettstatic void
60195887Sjmallettdodefn(const char *name)
6021590Srgrimes{
603228063Sbapt	struct macro_definition *p;
6041590Srgrimes
605228063Sbapt	if ((p = lookup_macro_definition(name)) != NULL) {
606228063Sbapt		if ((p->type & TYPEMASK) == MACRTYPE) {
60795060Sjmallett			pbstr(rquote);
60895060Sjmallett			pbstr(p->defn);
60995060Sjmallett			pbstr(lquote);
610228063Sbapt		} else {
611228063Sbapt			pbstr(p->defn);
61295060Sjmallett			pbstr(BUILTIN_MARKER);
61395060Sjmallett		}
6141590Srgrimes	}
6151590Srgrimes}
6161590Srgrimes
6171590Srgrimes/*
6181590Srgrimes * dopushdef - install a definition in the hash table
6191590Srgrimes *      without removing a previous definition. Since
6201590Srgrimes *      each new entry is entered in *front* of the
6211590Srgrimes *      hash bucket, it hides a previous definition from
6221590Srgrimes *      lookup.
6231590Srgrimes */
62495060Sjmallettstatic void
62595887Sjmallettdopushdef(const char *name, const char *defn)
6261590Srgrimes{
627228063Sbapt	if (!*name && !mimic_gnu)
628228063Sbapt		m4errx(1, "null definition.");
6291590Srgrimes	else
630228063Sbapt		macro_pushdef(name, defn);
6311590Srgrimes}
6321590Srgrimes
6331590Srgrimes/*
63495060Sjmallett * dump_one_def - dump the specified definition.
63595060Sjmallett */
63695060Sjmallettstatic void
637228063Sbaptdump_one_def(const char *name, struct macro_definition *p)
63895060Sjmallett{
639228063Sbapt	if (!traceout)
640228063Sbapt		traceout = stderr;
64195060Sjmallett	if (mimic_gnu) {
64295060Sjmallett		if ((p->type & TYPEMASK) == MACRTYPE)
643228063Sbapt			fprintf(traceout, "%s:\t%s\n", name, p->defn);
64495060Sjmallett		else {
645228063Sbapt			fprintf(traceout, "%s:\t<%s>\n", name, p->defn);
646228063Sbapt		}
64795060Sjmallett	} else
648228063Sbapt		fprintf(traceout, "`%s'\t`%s'\n", name, p->defn);
64995060Sjmallett}
65095060Sjmallett
65195060Sjmallett/*
6521590Srgrimes * dodumpdef - dump the specified definitions in the hash
6531590Srgrimes *      table to stderr. If nothing is specified, the entire
6541590Srgrimes *      hash table is dumped.
6551590Srgrimes */
65695060Sjmallettstatic void
65795887Sjmallettdodump(const char *argv[], int argc)
6581590Srgrimes{
65995060Sjmallett	int n;
660228063Sbapt	struct macro_definition *p;
6611590Srgrimes
6621590Srgrimes	if (argc > 2) {
6631590Srgrimes		for (n = 2; n < argc; n++)
664228063Sbapt			if ((p = lookup_macro_definition(argv[n])) != NULL)
665228063Sbapt				dump_one_def(argv[n], p);
666228063Sbapt	} else
667228063Sbapt		macro_for_all(dump_one_def);
6681590Srgrimes}
6691590Srgrimes
6701590Srgrimes/*
67195060Sjmallett * dotrace - mark some macros as traced/untraced depending upon on.
67295060Sjmallett */
67395060Sjmallettstatic void
67495887Sjmallettdotrace(const char *argv[], int argc, int on)
67595060Sjmallett{
67695060Sjmallett	int n;
67795060Sjmallett
67895060Sjmallett	if (argc > 2) {
67995060Sjmallett		for (n = 2; n < argc; n++)
68095060Sjmallett			mark_traced(argv[n], on);
68195060Sjmallett	} else
68295060Sjmallett		mark_traced(NULL, on);
68395060Sjmallett}
68495060Sjmallett
68595060Sjmallett/*
6861590Srgrimes * doifelse - select one of two alternatives - loop.
6871590Srgrimes */
68895060Sjmallettstatic void
68995887Sjmallettdoifelse(const char *argv[], int argc)
6901590Srgrimes{
6911590Srgrimes	cycle {
6921590Srgrimes		if (STREQ(argv[2], argv[3]))
6931590Srgrimes			pbstr(argv[4]);
6941590Srgrimes		else if (argc == 6)
6951590Srgrimes			pbstr(argv[5]);
6961590Srgrimes		else if (argc > 6) {
6971590Srgrimes			argv += 3;
6981590Srgrimes			argc -= 3;
6991590Srgrimes			continue;
7001590Srgrimes		}
7011590Srgrimes		break;
7021590Srgrimes	}
7031590Srgrimes}
7041590Srgrimes
7051590Srgrimes/*
7061590Srgrimes * doinclude - include a given file.
7071590Srgrimes */
70895060Sjmallettstatic int
70995887Sjmallettdoincl(const char *ifile)
7101590Srgrimes{
7111590Srgrimes	if (ilevel + 1 == MAXINP)
712228063Sbapt		m4errx(1, "too many include files.");
71395060Sjmallett	if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
7141590Srgrimes		ilevel++;
7151590Srgrimes		bbase[ilevel] = bufbase = bp;
7161590Srgrimes		return (1);
71795060Sjmallett	} else
7181590Srgrimes		return (0);
7191590Srgrimes}
7201590Srgrimes
7211590Srgrimes#ifdef EXTENDED
7221590Srgrimes/*
7231590Srgrimes * dopaste - include a given file without any
7241590Srgrimes *           macro processing.
7251590Srgrimes */
72695060Sjmallettstatic int
72795887Sjmallettdopaste(const char *pfile)
7281590Srgrimes{
7291590Srgrimes	FILE *pf;
73095060Sjmallett	int c;
7311590Srgrimes
7321590Srgrimes	if ((pf = fopen(pfile, "r")) != NULL) {
733228063Sbapt		if (synch_lines)
734228063Sbapt		    fprintf(active, "#line 1 \"%s\"\n", pfile);
7351590Srgrimes		while ((c = getc(pf)) != EOF)
7361590Srgrimes			putc(c, active);
7371590Srgrimes		(void) fclose(pf);
738228063Sbapt		emit_synchline();
7391590Srgrimes		return (1);
74095060Sjmallett	} else
7411590Srgrimes		return (0);
7421590Srgrimes}
7431590Srgrimes#endif
7441590Srgrimes
745228063Sbapt/*
746228063Sbapt * dochq - change quote characters
747228063Sbapt */
74895060Sjmallettstatic void
749228063Sbaptdochq(const char *argv[], int ac)
75095060Sjmallett{
75195060Sjmallett	if (ac == 2) {
752228063Sbapt		lquote[0] = LQUOTE; lquote[1] = EOS;
753228063Sbapt		rquote[0] = RQUOTE; rquote[1] = EOS;
75495060Sjmallett	} else {
75595060Sjmallett		strlcpy(lquote, argv[2], sizeof(lquote));
756228063Sbapt		if (ac > 3) {
75795060Sjmallett			strlcpy(rquote, argv[3], sizeof(rquote));
758228063Sbapt		} else {
759228063Sbapt			rquote[0] = ECOMMT; rquote[1] = EOS;
760228063Sbapt		}
76195060Sjmallett	}
76295060Sjmallett}
76395060Sjmallett
7641590Srgrimes/*
765228063Sbapt * dochc - change comment characters
7661590Srgrimes */
76795060Sjmallettstatic void
768228063Sbaptdochc(const char *argv[], int argc)
7691590Srgrimes{
770228063Sbapt/* XXX Note that there is no difference between no argument and a single
771228063Sbapt * empty argument.
772228063Sbapt */
773228063Sbapt	if (argc == 2) {
77495060Sjmallett		scommt[0] = EOS;
77595060Sjmallett		ecommt[0] = EOS;
77695060Sjmallett	} else {
777228063Sbapt		strlcpy(scommt, argv[2], sizeof(scommt));
778228063Sbapt		if (argc == 3) {
779228063Sbapt			ecommt[0] = ECOMMT; ecommt[1] = EOS;
780228063Sbapt		} else {
78195060Sjmallett			strlcpy(ecommt, argv[3], sizeof(ecommt));
782228063Sbapt		}
7831590Srgrimes	}
7841590Srgrimes}
785228063Sbapt
7861590Srgrimes/*
787228063Sbapt * dom4wrap - expand text at EOF
7881590Srgrimes */
78995060Sjmallettstatic void
790228063Sbaptdom4wrap(const char *text)
7911590Srgrimes{
792228063Sbapt	if (wrapindex >= maxwraps) {
793228063Sbapt		if (maxwraps == 0)
794228063Sbapt			maxwraps = 16;
7951590Srgrimes		else
796228063Sbapt			maxwraps *= 2;
797228063Sbapt		m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps),
798228063Sbapt		   "too many m4wraps");
7991590Srgrimes	}
800228063Sbapt	m4wraps[wrapindex++] = xstrdup(text);
8011590Srgrimes}
8021590Srgrimes
8031590Srgrimes/*
8041590Srgrimes * dodivert - divert the output to a temporary file
8051590Srgrimes */
80695060Sjmallettstatic void
80795887Sjmallettdodiv(int n)
8081590Srgrimes{
80995060Sjmallett	int fd;
81095060Sjmallett
81128386Sjlemon	oindex = n;
81295060Sjmallett	if (n >= maxout) {
81395060Sjmallett		if (mimic_gnu)
81495060Sjmallett			resizedivs(n + 10);
81595060Sjmallett		else
81695060Sjmallett			n = 0;		/* bitbucket */
817228063Sbapt	}
81895060Sjmallett
81995060Sjmallett	if (n < 0)
8201590Srgrimes		n = 0;		       /* bitbucket */
8211590Srgrimes	if (outfile[n] == NULL) {
82295060Sjmallett		char fname[] = _PATH_DIVNAME;
82395060Sjmallett
824228063Sbapt		if ((fd = mkstemp(fname)) < 0 ||
82595060Sjmallett			(outfile[n] = fdopen(fd, "w+")) == NULL)
82695060Sjmallett				err(1, "%s: cannot divert", fname);
82795060Sjmallett		if (unlink(fname) == -1)
82895060Sjmallett			err(1, "%s: cannot unlink", fname);
8291590Srgrimes	}
8301590Srgrimes	active = outfile[n];
8311590Srgrimes}
8321590Srgrimes
8331590Srgrimes/*
8341590Srgrimes * doundivert - undivert a specified output, or all
8351590Srgrimes *              other outputs, in numerical order.
8361590Srgrimes */
83795060Sjmallettstatic void
83895887Sjmallettdoundiv(const char *argv[], int argc)
8391590Srgrimes{
84095060Sjmallett	int ind;
84195060Sjmallett	int n;
8421590Srgrimes
8431590Srgrimes	if (argc > 2) {
8441590Srgrimes		for (ind = 2; ind < argc; ind++) {
845228063Sbapt			const char *errstr;
846228063Sbapt			n = strtonum(argv[ind], 1, INT_MAX, &errstr);
847228063Sbapt			if (errstr) {
848228063Sbapt				if (errno == EINVAL && mimic_gnu)
849228063Sbapt					getdivfile(argv[ind]);
850228063Sbapt			} else {
851228063Sbapt				if (n < maxout && outfile[n] != NULL)
852228063Sbapt					getdiv(n);
853228063Sbapt			}
8541590Srgrimes		}
8551590Srgrimes	}
8561590Srgrimes	else
85795060Sjmallett		for (n = 1; n < maxout; n++)
8581590Srgrimes			if (outfile[n] != NULL)
8591590Srgrimes				getdiv(n);
8601590Srgrimes}
8611590Srgrimes
8621590Srgrimes/*
8631590Srgrimes * dosub - select substring
8641590Srgrimes */
86595060Sjmallettstatic void
86695887Sjmallettdosub(const char *argv[], int argc)
8671590Srgrimes{
86895060Sjmallett	const char *ap, *fc, *k;
86995060Sjmallett	int nc;
8701590Srgrimes
87176822Sgshapiro	ap = argv[2];		       /* target string */
87276822Sgshapiro#ifdef EXPR
87376822Sgshapiro	fc = ap + expr(argv[3]);       /* first char */
87476822Sgshapiro#else
87576822Sgshapiro	fc = ap + atoi(argv[3]);       /* first char */
87676822Sgshapiro#endif
87795060Sjmallett	nc = strlen(fc);
87895060Sjmallett	if (argc >= 5)
8791590Srgrimes#ifdef EXPR
88095060Sjmallett		nc = min(nc, expr(argv[4]));
8811590Srgrimes#else
88295060Sjmallett		nc = min(nc, atoi(argv[4]));
8831590Srgrimes#endif
8841590Srgrimes	if (fc >= ap && fc < ap + strlen(ap))
88576822Sgshapiro		for (k = fc + nc - 1; k >= fc; k--)
886228063Sbapt			pushback(*k);
8871590Srgrimes}
8881590Srgrimes
8891590Srgrimes/*
8901590Srgrimes * map:
8911590Srgrimes * map every character of s1 that is specified in from
8921590Srgrimes * into s3 and replace in s. (source s1 remains untouched)
8931590Srgrimes *
894228063Sbapt * This is derived from the a standard implementation of map(s,from,to)
895228063Sbapt * function of ICON language. Within mapvec, we replace every character
896228063Sbapt * of "from" with the corresponding character in "to".
897228063Sbapt * If "to" is shorter than "from", than the corresponding entries are null,
898228063Sbapt * which means that those characters dissapear altogether.
8991590Srgrimes */
90095060Sjmallettstatic void
90195887Sjmallettmap(char *dest, const char *src, const char *from, const char *to)
9021590Srgrimes{
90395060Sjmallett	const char *tmp;
90495060Sjmallett	unsigned char sch, dch;
90595060Sjmallett	static char frombis[257];
90695060Sjmallett	static char tobis[257];
907228063Sbapt	int i;
908228063Sbapt	char seen[256];
90995060Sjmallett	static unsigned char mapvec[256] = {
91095060Sjmallett	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
91195060Sjmallett	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
91295060Sjmallett	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
91395060Sjmallett	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
91495060Sjmallett	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
91595060Sjmallett	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
91695060Sjmallett	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
91795060Sjmallett	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
91895060Sjmallett	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
91995060Sjmallett	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
92095060Sjmallett	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
92195060Sjmallett	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
92295060Sjmallett	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
92395060Sjmallett	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
92495060Sjmallett	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
92595060Sjmallett	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
92695060Sjmallett	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
92795060Sjmallett	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
9281590Srgrimes	};
9291590Srgrimes
9301590Srgrimes	if (*src) {
93195060Sjmallett		if (mimic_gnu) {
93295060Sjmallett			/*
93395060Sjmallett			 * expand character ranges on the fly
93495060Sjmallett			 */
93595060Sjmallett			from = handledash(frombis, frombis + 256, from);
93695060Sjmallett			to = handledash(tobis, tobis + 256, to);
93795060Sjmallett		}
9381590Srgrimes		tmp = from;
9391590Srgrimes	/*
9401590Srgrimes	 * create a mapping between "from" and
9411590Srgrimes	 * "to"
9421590Srgrimes	 */
943228063Sbapt		for (i = 0; i < 256; i++)
944228063Sbapt			seen[i] = 0;
945228063Sbapt		while (*from) {
946228063Sbapt			if (!seen[(unsigned char)(*from)]) {
947228063Sbapt				mapvec[(unsigned char)(*from)] = (unsigned char)(*to);
948228063Sbapt				seen[(unsigned char)(*from)] = 1;
949228063Sbapt			}
950228063Sbapt			from++;
951228063Sbapt			if (*to)
952228063Sbapt				to++;
953228063Sbapt		}
9541590Srgrimes
9551590Srgrimes		while (*src) {
95695060Sjmallett			sch = (unsigned char)(*src++);
9571590Srgrimes			dch = mapvec[sch];
95895060Sjmallett			if ((*dest = (char)dch))
9591590Srgrimes				dest++;
9601590Srgrimes		}
9611590Srgrimes	/*
9621590Srgrimes	 * restore all the changed characters
9631590Srgrimes	 */
9641590Srgrimes		while (*tmp) {
96595060Sjmallett			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
9661590Srgrimes			tmp++;
9671590Srgrimes		}
9681590Srgrimes	}
96995060Sjmallett	*dest = '\0';
9701590Srgrimes}
97195060Sjmallett
97295060Sjmallett
97395060Sjmallett/*
97495060Sjmallett * handledash:
97595060Sjmallett *  use buffer to copy the src string, expanding character ranges
97695060Sjmallett * on the way.
97795060Sjmallett */
97895060Sjmallettstatic const char *
97995887Sjmalletthandledash(char *buffer, char *end, const char *src)
98095060Sjmallett{
98195060Sjmallett	char *p;
982100014Sjmallett
98395060Sjmallett	p = buffer;
98495060Sjmallett	while(*src) {
98595060Sjmallett		if (src[1] == '-' && src[2]) {
98695060Sjmallett			unsigned char i;
987228063Sbapt			if ((unsigned char)src[0] <= (unsigned char)src[2]) {
988228063Sbapt				for (i = (unsigned char)src[0];
989228063Sbapt				    i <= (unsigned char)src[2]; i++) {
990228063Sbapt					*p++ = i;
991228063Sbapt					if (p == end) {
992228063Sbapt						*p = '\0';
993228063Sbapt						return buffer;
994228063Sbapt					}
99595060Sjmallett				}
996228063Sbapt			} else {
997228063Sbapt				for (i = (unsigned char)src[0];
998228063Sbapt				    i >= (unsigned char)src[2]; i--) {
999228063Sbapt					*p++ = i;
1000228063Sbapt					if (p == end) {
1001228063Sbapt						*p = '\0';
1002228063Sbapt						return buffer;
1003228063Sbapt					}
1004228063Sbapt				}
100595060Sjmallett			}
100695060Sjmallett			src += 3;
100795060Sjmallett		} else
100895060Sjmallett			*p++ = *src++;
100995060Sjmallett		if (p == end)
101095060Sjmallett			break;
101195060Sjmallett	}
101295060Sjmallett	*p = '\0';
101395060Sjmallett	return buffer;
101495060Sjmallett}
1015