eval.c revision 291128
1291128Sbapt/*	$OpenBSD: eval.c,v 1.74 2015/02/05 12:59:57 millert 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 291128 2015-11-21 11:05:38Z bapt $");
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>
51291128Sbapt#include <stdio.h>
52228063Sbapt#include <stdint.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:
270269162Sbapt		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			}
280269162Sbapt		}
2811590Srgrimes		break;
2821590Srgrimes
2831590Srgrimes	case SINCTYPE:
2841590Srgrimes		if (argc > 2)
2851590Srgrimes			(void) doincl(argv[2]);
2861590Srgrimes		break;
2871590Srgrimes#ifdef EXTENDED
2881590Srgrimes	case PASTTYPE:
2891590Srgrimes		if (argc > 2)
2901590Srgrimes			if (!dopaste(argv[2]))
291228063Sbapt				err(1, "%s at line %lu: paste(%s)",
29295060Sjmallett				    CURRENT_NAME, CURRENT_LINE, argv[2]);
2931590Srgrimes		break;
2941590Srgrimes
2951590Srgrimes	case SPASTYPE:
2961590Srgrimes		if (argc > 2)
2971590Srgrimes			(void) dopaste(argv[2]);
2981590Srgrimes		break;
299228063Sbapt	case FORMATTYPE:
300228063Sbapt		doformat(argv, argc);
301228063Sbapt		break;
3021590Srgrimes#endif
3031590Srgrimes	case CHNQTYPE:
304228063Sbapt		dochq(argv, ac);
3051590Srgrimes		break;
3061590Srgrimes
3071590Srgrimes	case CHNCTYPE:
308228063Sbapt		dochc(argv, argc);
3091590Srgrimes		break;
3101590Srgrimes
3111590Srgrimes	case SUBSTYPE:
3121590Srgrimes	/*
3131590Srgrimes	 * dosub - select substring
314100014Sjmallett	 *
3151590Srgrimes	 */
3161590Srgrimes		if (argc > 3)
3171590Srgrimes			dosub(argv, argc);
3181590Srgrimes		break;
3191590Srgrimes
3201590Srgrimes	case SHIFTYPE:
3211590Srgrimes	/*
3221590Srgrimes	 * doshift - push back all arguments
3231590Srgrimes	 * except the first one (i.e. skip
3241590Srgrimes	 * argv[2])
3251590Srgrimes	 */
3261590Srgrimes		if (argc > 3) {
3271590Srgrimes			for (n = argc - 1; n > 3; n--) {
32895060Sjmallett				pbstr(rquote);
3291590Srgrimes				pbstr(argv[n]);
33095060Sjmallett				pbstr(lquote);
331228063Sbapt				pushback(COMMA);
3321590Srgrimes			}
33395060Sjmallett			pbstr(rquote);
3341590Srgrimes			pbstr(argv[3]);
33595060Sjmallett			pbstr(lquote);
3361590Srgrimes		}
3371590Srgrimes		break;
3381590Srgrimes
3391590Srgrimes	case DIVRTYPE:
3401590Srgrimes		if (argc > 2 && (n = atoi(argv[2])) != 0)
3411590Srgrimes			dodiv(n);
3421590Srgrimes		else {
3431590Srgrimes			active = stdout;
3441590Srgrimes			oindex = 0;
3451590Srgrimes		}
3461590Srgrimes		break;
3471590Srgrimes
3481590Srgrimes	case UNDVTYPE:
3491590Srgrimes		doundiv(argv, argc);
3501590Srgrimes		break;
3511590Srgrimes
3521590Srgrimes	case DIVNTYPE:
3531590Srgrimes	/*
3541590Srgrimes	 * dodivnum - return the number of
3551590Srgrimes	 * current output diversion
3561590Srgrimes	 */
3571590Srgrimes		pbnum(oindex);
3581590Srgrimes		break;
3591590Srgrimes
3601590Srgrimes	case UNDFTYPE:
3611590Srgrimes	/*
3621590Srgrimes	 * doundefine - undefine a previously
3631590Srgrimes	 * defined macro(s) or m4 keyword(s).
3641590Srgrimes	 */
3651590Srgrimes		if (argc > 2)
3661590Srgrimes			for (n = 2; n < argc; n++)
367228063Sbapt				macro_undefine(argv[n]);
3681590Srgrimes		break;
3691590Srgrimes
3701590Srgrimes	case POPDTYPE:
3711590Srgrimes	/*
3721590Srgrimes	 * dopopdef - remove the topmost
3731590Srgrimes	 * definitions of macro(s) or m4
3741590Srgrimes	 * keyword(s).
3751590Srgrimes	 */
3761590Srgrimes		if (argc > 2)
3771590Srgrimes			for (n = 2; n < argc; n++)
378228063Sbapt				macro_popdef(argv[n]);
3791590Srgrimes		break;
3801590Srgrimes
3811590Srgrimes	case MKTMTYPE:
3821590Srgrimes	/*
3831590Srgrimes	 * dotemp - create a temporary file
3841590Srgrimes	 */
38595060Sjmallett		if (argc > 2) {
38695060Sjmallett			int fd;
38795060Sjmallett			char *temp;
38895060Sjmallett
38995060Sjmallett			temp = xstrdup(argv[2]);
390100014Sjmallett
39195060Sjmallett			fd = mkstemp(temp);
39295060Sjmallett			if (fd == -1)
393100014Sjmallett				err(1,
394100014Sjmallett	    "%s at line %lu: couldn't make temp file %s",
39595060Sjmallett	    CURRENT_NAME, CURRENT_LINE, argv[2]);
39695060Sjmallett			close(fd);
39795060Sjmallett			pbstr(temp);
39895060Sjmallett			free(temp);
39995060Sjmallett		}
4001590Srgrimes		break;
4011590Srgrimes
4021590Srgrimes	case TRNLTYPE:
4031590Srgrimes	/*
4041590Srgrimes	 * dotranslit - replace all characters in
4051590Srgrimes	 * the source string that appears in the
4061590Srgrimes	 * "from" string with the corresponding
4071590Srgrimes	 * characters in the "to" string.
4081590Srgrimes	 */
4091590Srgrimes		if (argc > 3) {
41095060Sjmallett			char *temp;
41195060Sjmallett
412228063Sbapt			temp = xalloc(strlen(argv[2])+1, NULL);
4131590Srgrimes			if (argc > 4)
4141590Srgrimes				map(temp, argv[2], argv[3], argv[4]);
4151590Srgrimes			else
4161590Srgrimes				map(temp, argv[2], argv[3], null);
4171590Srgrimes			pbstr(temp);
41895060Sjmallett			free(temp);
41995060Sjmallett		} else if (argc > 2)
4201590Srgrimes			pbstr(argv[2]);
4211590Srgrimes		break;
4221590Srgrimes
4231590Srgrimes	case INDXTYPE:
4241590Srgrimes	/*
4251590Srgrimes	 * doindex - find the index of the second
4261590Srgrimes	 * argument string in the first argument
4271590Srgrimes	 * string. -1 if not present.
4281590Srgrimes	 */
4291590Srgrimes		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
4301590Srgrimes		break;
4311590Srgrimes
4321590Srgrimes	case ERRPTYPE:
4331590Srgrimes	/*
4341590Srgrimes	 * doerrp - print the arguments to stderr
4351590Srgrimes	 * file
4361590Srgrimes	 */
4371590Srgrimes		if (argc > 2) {
4381590Srgrimes			for (n = 2; n < argc; n++)
4391590Srgrimes				fprintf(stderr, "%s ", argv[n]);
4401590Srgrimes			fprintf(stderr, "\n");
4411590Srgrimes		}
4421590Srgrimes		break;
4431590Srgrimes
4441590Srgrimes	case DNLNTYPE:
4451590Srgrimes	/*
4461590Srgrimes	 * dodnl - eat-up-to and including
4471590Srgrimes	 * newline
4481590Srgrimes	 */
4491590Srgrimes		while ((c = gpbc()) != '\n' && c != EOF)
4501590Srgrimes			;
4511590Srgrimes		break;
4521590Srgrimes
4531590Srgrimes	case M4WRTYPE:
4541590Srgrimes	/*
4551590Srgrimes	 * dom4wrap - set up for
4561590Srgrimes	 * wrap-up/wind-down activity
4571590Srgrimes	 */
458228063Sbapt		if (argc > 2)
459228063Sbapt			dom4wrap(argv[2]);
4601590Srgrimes		break;
4611590Srgrimes
4621590Srgrimes	case EXITTYPE:
4631590Srgrimes	/*
4641590Srgrimes	 * doexit - immediate exit from m4.
4651590Srgrimes	 */
4667896Sache		killdiv();
4671590Srgrimes		exit((argc > 2) ? atoi(argv[2]) : 0);
4681590Srgrimes		break;
4691590Srgrimes
4701590Srgrimes	case DEFNTYPE:
4711590Srgrimes		if (argc > 2)
4721590Srgrimes			for (n = 2; n < argc; n++)
4731590Srgrimes				dodefn(argv[n]);
4741590Srgrimes		break;
4751590Srgrimes
47695060Sjmallett	case INDIRTYPE:	/* Indirect call */
47795060Sjmallett		if (argc > 2)
47895060Sjmallett			doindir(argv, argc);
47938926Ssteve		break;
480100014Sjmallett
48195060Sjmallett	case BUILTINTYPE: /* Builtins only */
48295060Sjmallett		if (argc > 2)
48395060Sjmallett			dobuiltin(argv, argc);
48495060Sjmallett		break;
48538926Ssteve
48695060Sjmallett	case PATSTYPE:
48795060Sjmallett		if (argc > 2)
48895060Sjmallett			dopatsubst(argv, argc);
48995060Sjmallett		break;
49095060Sjmallett	case REGEXPTYPE:
49195060Sjmallett		if (argc > 2)
49295060Sjmallett			doregexp(argv, argc);
49395060Sjmallett		break;
49495060Sjmallett	case LINETYPE:
49595060Sjmallett		doprintlineno(infile+ilevel);
49695060Sjmallett		break;
49795060Sjmallett	case FILENAMETYPE:
49895060Sjmallett		doprintfilename(infile+ilevel);
49995060Sjmallett		break;
50095060Sjmallett	case SELFTYPE:
50195060Sjmallett		pbstr(rquote);
50295060Sjmallett		pbstr(argv[1]);
50395060Sjmallett		pbstr(lquote);
50495060Sjmallett		break;
5051590Srgrimes	default:
506228063Sbapt		m4errx(1, "eval: major botch.");
5071590Srgrimes		break;
5081590Srgrimes	}
5091590Srgrimes}
5101590Srgrimes
5111590Srgrimes/*
51295060Sjmallett * expand_macro - user-defined macro expansion
5131590Srgrimes */
5141590Srgrimesvoid
51595887Sjmallettexpand_macro(const char *argv[], int argc)
5161590Srgrimes{
51795060Sjmallett	const char *t;
51895060Sjmallett	const char *p;
51995060Sjmallett	int n;
52095060Sjmallett	int argno;
5211590Srgrimes
5221590Srgrimes	t = argv[0];		       /* defn string as a whole */
5231590Srgrimes	p = t;
5241590Srgrimes	while (*p)
5251590Srgrimes		p++;
5261590Srgrimes	p--;			       /* last character of defn */
5271590Srgrimes	while (p > t) {
5281590Srgrimes		if (*(p - 1) != ARGFLAG)
529228063Sbapt			PUSHBACK(*p);
5301590Srgrimes		else {
5311590Srgrimes			switch (*p) {
5321590Srgrimes
5331590Srgrimes			case '#':
5341590Srgrimes				pbnum(argc - 2);
5351590Srgrimes				break;
5361590Srgrimes			case '0':
5371590Srgrimes			case '1':
5381590Srgrimes			case '2':
5391590Srgrimes			case '3':
5401590Srgrimes			case '4':
5411590Srgrimes			case '5':
5421590Srgrimes			case '6':
5431590Srgrimes			case '7':
5441590Srgrimes			case '8':
5451590Srgrimes			case '9':
5461590Srgrimes				if ((argno = *p - '0') < argc - 1)
5471590Srgrimes					pbstr(argv[argno + 1]);
5481590Srgrimes				break;
5491590Srgrimes			case '*':
55095060Sjmallett				if (argc > 2) {
55195060Sjmallett					for (n = argc - 1; n > 2; n--) {
55295060Sjmallett						pbstr(argv[n]);
553228063Sbapt						pushback(COMMA);
55495060Sjmallett					}
55595060Sjmallett					pbstr(argv[2]);
556228063Sbapt				}
5571590Srgrimes				break;
55895060Sjmallett                        case '@':
55995060Sjmallett				if (argc > 2) {
56095060Sjmallett					for (n = argc - 1; n > 2; n--) {
56195060Sjmallett						pbstr(rquote);
56295060Sjmallett						pbstr(argv[n]);
56395060Sjmallett						pbstr(lquote);
564228063Sbapt						pushback(COMMA);
56595060Sjmallett					}
56695060Sjmallett					pbstr(rquote);
56795060Sjmallett					pbstr(argv[2]);
56895060Sjmallett					pbstr(lquote);
56924901Sjoerg				}
57095060Sjmallett                                break;
5711590Srgrimes			default:
572228063Sbapt				PUSHBACK(*p);
573228063Sbapt				PUSHBACK('$');
5741590Srgrimes				break;
5751590Srgrimes			}
5761590Srgrimes			p--;
5771590Srgrimes		}
5781590Srgrimes		p--;
5791590Srgrimes	}
5801590Srgrimes	if (p == t)		       /* do last character */
581228063Sbapt		PUSHBACK(*p);
5821590Srgrimes}
5831590Srgrimes
584228063Sbapt
5851590Srgrimes/*
5861590Srgrimes * dodefine - install definition in the table
5871590Srgrimes */
5881590Srgrimesvoid
58995887Sjmallettdodefine(const char *name, const char *defn)
5901590Srgrimes{
591228063Sbapt	if (!*name && !mimic_gnu)
592228063Sbapt		m4errx(1, "null definition.");
5931590Srgrimes	else
594228063Sbapt		macro_define(name, defn);
5951590Srgrimes}
5961590Srgrimes
5971590Srgrimes/*
5981590Srgrimes * dodefn - push back a quoted definition of
5991590Srgrimes *      the given name.
6001590Srgrimes */
60195060Sjmallettstatic void
60295887Sjmallettdodefn(const char *name)
6031590Srgrimes{
604228063Sbapt	struct macro_definition *p;
6051590Srgrimes
606228063Sbapt	if ((p = lookup_macro_definition(name)) != NULL) {
607228063Sbapt		if ((p->type & TYPEMASK) == MACRTYPE) {
60895060Sjmallett			pbstr(rquote);
60995060Sjmallett			pbstr(p->defn);
61095060Sjmallett			pbstr(lquote);
611228063Sbapt		} else {
612228063Sbapt			pbstr(p->defn);
61395060Sjmallett			pbstr(BUILTIN_MARKER);
61495060Sjmallett		}
6151590Srgrimes	}
6161590Srgrimes}
6171590Srgrimes
6181590Srgrimes/*
6191590Srgrimes * dopushdef - install a definition in the hash table
6201590Srgrimes *      without removing a previous definition. Since
6211590Srgrimes *      each new entry is entered in *front* of the
6221590Srgrimes *      hash bucket, it hides a previous definition from
6231590Srgrimes *      lookup.
6241590Srgrimes */
62595060Sjmallettstatic void
62695887Sjmallettdopushdef(const char *name, const char *defn)
6271590Srgrimes{
628228063Sbapt	if (!*name && !mimic_gnu)
629228063Sbapt		m4errx(1, "null definition.");
6301590Srgrimes	else
631228063Sbapt		macro_pushdef(name, defn);
6321590Srgrimes}
6331590Srgrimes
6341590Srgrimes/*
63595060Sjmallett * dump_one_def - dump the specified definition.
63695060Sjmallett */
63795060Sjmallettstatic void
638228063Sbaptdump_one_def(const char *name, struct macro_definition *p)
63995060Sjmallett{
640228063Sbapt	if (!traceout)
641228063Sbapt		traceout = stderr;
64295060Sjmallett	if (mimic_gnu) {
64395060Sjmallett		if ((p->type & TYPEMASK) == MACRTYPE)
644228063Sbapt			fprintf(traceout, "%s:\t%s\n", name, p->defn);
64595060Sjmallett		else {
646228063Sbapt			fprintf(traceout, "%s:\t<%s>\n", name, p->defn);
647228063Sbapt		}
64895060Sjmallett	} else
649228063Sbapt		fprintf(traceout, "`%s'\t`%s'\n", name, p->defn);
65095060Sjmallett}
65195060Sjmallett
65295060Sjmallett/*
6531590Srgrimes * dodumpdef - dump the specified definitions in the hash
6541590Srgrimes *      table to stderr. If nothing is specified, the entire
6551590Srgrimes *      hash table is dumped.
6561590Srgrimes */
65795060Sjmallettstatic void
65895887Sjmallettdodump(const char *argv[], int argc)
6591590Srgrimes{
66095060Sjmallett	int n;
661228063Sbapt	struct macro_definition *p;
6621590Srgrimes
6631590Srgrimes	if (argc > 2) {
6641590Srgrimes		for (n = 2; n < argc; n++)
665228063Sbapt			if ((p = lookup_macro_definition(argv[n])) != NULL)
666228063Sbapt				dump_one_def(argv[n], p);
667228063Sbapt	} else
668228063Sbapt		macro_for_all(dump_one_def);
6691590Srgrimes}
6701590Srgrimes
6711590Srgrimes/*
67295060Sjmallett * dotrace - mark some macros as traced/untraced depending upon on.
67395060Sjmallett */
67495060Sjmallettstatic void
67595887Sjmallettdotrace(const char *argv[], int argc, int on)
67695060Sjmallett{
67795060Sjmallett	int n;
67895060Sjmallett
67995060Sjmallett	if (argc > 2) {
68095060Sjmallett		for (n = 2; n < argc; n++)
68195060Sjmallett			mark_traced(argv[n], on);
68295060Sjmallett	} else
68395060Sjmallett		mark_traced(NULL, on);
68495060Sjmallett}
68595060Sjmallett
68695060Sjmallett/*
6871590Srgrimes * doifelse - select one of two alternatives - loop.
6881590Srgrimes */
68995060Sjmallettstatic void
69095887Sjmallettdoifelse(const char *argv[], int argc)
6911590Srgrimes{
6921590Srgrimes	cycle {
6931590Srgrimes		if (STREQ(argv[2], argv[3]))
6941590Srgrimes			pbstr(argv[4]);
6951590Srgrimes		else if (argc == 6)
6961590Srgrimes			pbstr(argv[5]);
6971590Srgrimes		else if (argc > 6) {
6981590Srgrimes			argv += 3;
6991590Srgrimes			argc -= 3;
7001590Srgrimes			continue;
7011590Srgrimes		}
7021590Srgrimes		break;
7031590Srgrimes	}
7041590Srgrimes}
7051590Srgrimes
7061590Srgrimes/*
7071590Srgrimes * doinclude - include a given file.
7081590Srgrimes */
70995060Sjmallettstatic int
71095887Sjmallettdoincl(const char *ifile)
7111590Srgrimes{
7121590Srgrimes	if (ilevel + 1 == MAXINP)
713228063Sbapt		m4errx(1, "too many include files.");
71495060Sjmallett	if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
7151590Srgrimes		ilevel++;
7161590Srgrimes		bbase[ilevel] = bufbase = bp;
7171590Srgrimes		return (1);
71895060Sjmallett	} else
7191590Srgrimes		return (0);
7201590Srgrimes}
7211590Srgrimes
7221590Srgrimes#ifdef EXTENDED
7231590Srgrimes/*
7241590Srgrimes * dopaste - include a given file without any
7251590Srgrimes *           macro processing.
7261590Srgrimes */
72795060Sjmallettstatic int
72895887Sjmallettdopaste(const char *pfile)
7291590Srgrimes{
7301590Srgrimes	FILE *pf;
73195060Sjmallett	int c;
7321590Srgrimes
7331590Srgrimes	if ((pf = fopen(pfile, "r")) != NULL) {
734228063Sbapt		if (synch_lines)
735228063Sbapt		    fprintf(active, "#line 1 \"%s\"\n", pfile);
7361590Srgrimes		while ((c = getc(pf)) != EOF)
7371590Srgrimes			putc(c, active);
7381590Srgrimes		(void) fclose(pf);
739228063Sbapt		emit_synchline();
7401590Srgrimes		return (1);
74195060Sjmallett	} else
7421590Srgrimes		return (0);
7431590Srgrimes}
7441590Srgrimes#endif
7451590Srgrimes
746228063Sbapt/*
747228063Sbapt * dochq - change quote characters
748228063Sbapt */
74995060Sjmallettstatic void
750228063Sbaptdochq(const char *argv[], int ac)
75195060Sjmallett{
75295060Sjmallett	if (ac == 2) {
753228063Sbapt		lquote[0] = LQUOTE; lquote[1] = EOS;
754228063Sbapt		rquote[0] = RQUOTE; rquote[1] = EOS;
75595060Sjmallett	} else {
75695060Sjmallett		strlcpy(lquote, argv[2], sizeof(lquote));
757228063Sbapt		if (ac > 3) {
75895060Sjmallett			strlcpy(rquote, argv[3], sizeof(rquote));
759228063Sbapt		} else {
760228063Sbapt			rquote[0] = ECOMMT; rquote[1] = EOS;
761228063Sbapt		}
76295060Sjmallett	}
76395060Sjmallett}
76495060Sjmallett
7651590Srgrimes/*
766228063Sbapt * dochc - change comment characters
7671590Srgrimes */
76895060Sjmallettstatic void
769228063Sbaptdochc(const char *argv[], int argc)
7701590Srgrimes{
771228063Sbapt/* XXX Note that there is no difference between no argument and a single
772228063Sbapt * empty argument.
773228063Sbapt */
774228063Sbapt	if (argc == 2) {
77595060Sjmallett		scommt[0] = EOS;
77695060Sjmallett		ecommt[0] = EOS;
77795060Sjmallett	} else {
778228063Sbapt		strlcpy(scommt, argv[2], sizeof(scommt));
779228063Sbapt		if (argc == 3) {
780228063Sbapt			ecommt[0] = ECOMMT; ecommt[1] = EOS;
781228063Sbapt		} else {
78295060Sjmallett			strlcpy(ecommt, argv[3], sizeof(ecommt));
783228063Sbapt		}
7841590Srgrimes	}
7851590Srgrimes}
786228063Sbapt
7871590Srgrimes/*
788228063Sbapt * dom4wrap - expand text at EOF
7891590Srgrimes */
79095060Sjmallettstatic void
791228063Sbaptdom4wrap(const char *text)
7921590Srgrimes{
793228063Sbapt	if (wrapindex >= maxwraps) {
794228063Sbapt		if (maxwraps == 0)
795228063Sbapt			maxwraps = 16;
7961590Srgrimes		else
797228063Sbapt			maxwraps *= 2;
798269162Sbapt		m4wraps = xreallocarray(m4wraps, maxwraps, sizeof(*m4wraps),
799228063Sbapt		   "too many m4wraps");
8001590Srgrimes	}
801228063Sbapt	m4wraps[wrapindex++] = xstrdup(text);
8021590Srgrimes}
8031590Srgrimes
8041590Srgrimes/*
8051590Srgrimes * dodivert - divert the output to a temporary file
8061590Srgrimes */
80795060Sjmallettstatic void
80895887Sjmallettdodiv(int n)
8091590Srgrimes{
81095060Sjmallett	int fd;
81195060Sjmallett
81228386Sjlemon	oindex = n;
81395060Sjmallett	if (n >= maxout) {
81495060Sjmallett		if (mimic_gnu)
81595060Sjmallett			resizedivs(n + 10);
81695060Sjmallett		else
81795060Sjmallett			n = 0;		/* bitbucket */
818228063Sbapt	}
81995060Sjmallett
82095060Sjmallett	if (n < 0)
8211590Srgrimes		n = 0;		       /* bitbucket */
8221590Srgrimes	if (outfile[n] == NULL) {
82395060Sjmallett		char fname[] = _PATH_DIVNAME;
82495060Sjmallett
825269162Sbapt		if ((fd = mkstemp(fname)) < 0 ||
826269162Sbapt		    unlink(fname) == -1 ||
827269162Sbapt		    (outfile[n] = fdopen(fd, "w+")) == NULL)
828269162Sbapt			err(1, "%s: cannot divert", 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