xref: /minix3/usr.bin/m4/eval.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
12e8d1edaSArun Thomas /*	$OpenBSD: eval.c,v 1.66 2008/08/21 21:01:47 espie Exp $	*/
2*0a6a1f1dSLionel Sambuc /*	$NetBSD: eval.c,v 1.23 2015/01/29 19:26:20 christos Exp $	*/
32e8d1edaSArun Thomas 
42e8d1edaSArun Thomas /*
52e8d1edaSArun Thomas  * Copyright (c) 1989, 1993
62e8d1edaSArun Thomas  *	The Regents of the University of California.  All rights reserved.
72e8d1edaSArun Thomas  *
82e8d1edaSArun Thomas  * This code is derived from software contributed to Berkeley by
92e8d1edaSArun Thomas  * Ozan Yigit at York University.
102e8d1edaSArun Thomas  *
112e8d1edaSArun Thomas  * Redistribution and use in source and binary forms, with or without
122e8d1edaSArun Thomas  * modification, are permitted provided that the following conditions
132e8d1edaSArun Thomas  * are met:
142e8d1edaSArun Thomas  * 1. Redistributions of source code must retain the above copyright
152e8d1edaSArun Thomas  *    notice, this list of conditions and the following disclaimer.
162e8d1edaSArun Thomas  * 2. Redistributions in binary form must reproduce the above copyright
172e8d1edaSArun Thomas  *    notice, this list of conditions and the following disclaimer in the
182e8d1edaSArun Thomas  *    documentation and/or other materials provided with the distribution.
192e8d1edaSArun Thomas  * 3. Neither the name of the University nor the names of its contributors
202e8d1edaSArun Thomas  *    may be used to endorse or promote products derived from this software
212e8d1edaSArun Thomas  *    without specific prior written permission.
222e8d1edaSArun Thomas  *
232e8d1edaSArun Thomas  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
242e8d1edaSArun Thomas  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
252e8d1edaSArun Thomas  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
262e8d1edaSArun Thomas  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
272e8d1edaSArun Thomas  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
282e8d1edaSArun Thomas  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
292e8d1edaSArun Thomas  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
302e8d1edaSArun Thomas  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
312e8d1edaSArun Thomas  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
322e8d1edaSArun Thomas  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
332e8d1edaSArun Thomas  * SUCH DAMAGE.
342e8d1edaSArun Thomas  */
352e8d1edaSArun Thomas 
362e8d1edaSArun Thomas /*
372e8d1edaSArun Thomas  * eval.c
382e8d1edaSArun Thomas  * Facility: m4 macro processor
392e8d1edaSArun Thomas  * by: oz
402e8d1edaSArun Thomas  */
412e8d1edaSArun Thomas #if HAVE_NBTOOL_CONFIG_H
422e8d1edaSArun Thomas #include "nbtool_config.h"
432e8d1edaSArun Thomas #endif
442e8d1edaSArun Thomas #include <sys/cdefs.h>
45*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: eval.c,v 1.23 2015/01/29 19:26:20 christos Exp $");
462e8d1edaSArun Thomas 
472e8d1edaSArun Thomas #include <sys/types.h>
482e8d1edaSArun Thomas #include <err.h>
492e8d1edaSArun Thomas #include <errno.h>
502e8d1edaSArun Thomas #include <limits.h>
512e8d1edaSArun Thomas #include <unistd.h>
522e8d1edaSArun Thomas #include <stdio.h>
532e8d1edaSArun Thomas #include <stdlib.h>
542e8d1edaSArun Thomas #include <stddef.h>
5571c7dcb9SLionel Sambuc #include <stdint.h>
562e8d1edaSArun Thomas #include <string.h>
57*0a6a1f1dSLionel Sambuc #include <inttypes.h>
582e8d1edaSArun Thomas #include <fcntl.h>
592e8d1edaSArun Thomas #include "mdef.h"
602e8d1edaSArun Thomas #include "stdd.h"
612e8d1edaSArun Thomas #include "extern.h"
622e8d1edaSArun Thomas #include "pathnames.h"
632e8d1edaSArun Thomas 
642e8d1edaSArun Thomas static void	dodefn(const char *);
652e8d1edaSArun Thomas static void	dopushdef(const char *, const char *);
662e8d1edaSArun Thomas static void	dodump(const char *[], int);
672e8d1edaSArun Thomas static void	dotrace(const char *[], int, int);
682e8d1edaSArun Thomas static void	doifelse(const char *[], int);
692e8d1edaSArun Thomas static int	doincl(const char *);
702e8d1edaSArun Thomas static int	dopaste(const char *);
712e8d1edaSArun Thomas static void	dochq(const char *[], int);
722e8d1edaSArun Thomas static void	dochc(const char *[], int);
732e8d1edaSArun Thomas static void	dom4wrap(const char *);
742e8d1edaSArun Thomas static void	dodiv(int);
752e8d1edaSArun Thomas static void	doundiv(const char *[], int);
762e8d1edaSArun Thomas static void	dosub(const char *[], int);
772e8d1edaSArun Thomas static void	map(char *, const char *, const char *, const char *);
782e8d1edaSArun Thomas static const char *handledash(char *, char *, const char *);
792e8d1edaSArun Thomas static void	expand_builtin(const char *[], int, int);
802e8d1edaSArun Thomas static void	expand_macro(const char *[], int);
812e8d1edaSArun Thomas static void	dump_one_def(const char *, struct macro_definition *);
822e8d1edaSArun Thomas 
832e8d1edaSArun Thomas unsigned long	expansion_id;
842e8d1edaSArun Thomas 
852e8d1edaSArun Thomas /*
862e8d1edaSArun Thomas  * eval - eval all macros and builtins calls
872e8d1edaSArun Thomas  *	  argc - number of elements in argv.
882e8d1edaSArun Thomas  *	  argv - element vector :
892e8d1edaSArun Thomas  *			argv[0] = definition of a user
902e8d1edaSArun Thomas  *				  macro or NULL if built-in.
912e8d1edaSArun Thomas  *			argv[1] = name of the macro or
922e8d1edaSArun Thomas  *				  built-in.
932e8d1edaSArun Thomas  *			argv[2] = parameters to user-defined
942e8d1edaSArun Thomas  *			   .	  macro or built-in.
952e8d1edaSArun Thomas  *			   .
962e8d1edaSArun Thomas  *
972e8d1edaSArun Thomas  * A call in the form of macro-or-builtin() will result in:
982e8d1edaSArun Thomas  *			argv[0] = nullstr
992e8d1edaSArun Thomas  *			argv[1] = macro-or-builtin
1002e8d1edaSArun Thomas  *			argv[2] = nullstr
1012e8d1edaSArun Thomas  *
1022e8d1edaSArun Thomas  * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
1032e8d1edaSArun Thomas  */
1042e8d1edaSArun Thomas void
eval(const char * argv[],int argc,int td,int is_traced)1052e8d1edaSArun Thomas eval(const char *argv[], int argc, int td, int is_traced)
1062e8d1edaSArun Thomas {
1072e8d1edaSArun Thomas 	size_t mark = SIZE_MAX;
1082e8d1edaSArun Thomas 
1092e8d1edaSArun Thomas 	expansion_id++;
1102e8d1edaSArun Thomas 	if (td & RECDEF)
1112e8d1edaSArun Thomas 		m4errx(1, "expanding recursive definition for %s.", argv[1]);
1122e8d1edaSArun Thomas 	if (is_traced)
1132e8d1edaSArun Thomas 		mark = trace(argv, argc, infile+ilevel);
1142e8d1edaSArun Thomas 	if (td == MACRTYPE)
1152e8d1edaSArun Thomas 		expand_macro(argv, argc);
1162e8d1edaSArun Thomas 	else
1172e8d1edaSArun Thomas 		expand_builtin(argv, argc, td);
1182e8d1edaSArun Thomas     	if (mark != SIZE_MAX)
1192e8d1edaSArun Thomas 		finish_trace(mark);
1202e8d1edaSArun Thomas }
1212e8d1edaSArun Thomas 
1222e8d1edaSArun Thomas /*
1232e8d1edaSArun Thomas  * expand_builtin - evaluate built-in macros.
1242e8d1edaSArun Thomas  */
1252e8d1edaSArun Thomas void
expand_builtin(const char * argv[],int argc,int td)1262e8d1edaSArun Thomas expand_builtin(const char *argv[], int argc, int td)
1272e8d1edaSArun Thomas {
1282e8d1edaSArun Thomas 	int c, n;
1292e8d1edaSArun Thomas 	int ac;
1302e8d1edaSArun Thomas 	static int sysval = 0;
1312e8d1edaSArun Thomas 
1322e8d1edaSArun Thomas #ifdef DEBUG
1332e8d1edaSArun Thomas 	printf("argc = %d\n", argc);
1342e8d1edaSArun Thomas 	for (n = 0; n < argc; n++)
1352e8d1edaSArun Thomas 		printf("argv[%d] = %s\n", n, argv[n]);
1362e8d1edaSArun Thomas 	fflush(stdout);
1372e8d1edaSArun Thomas #endif
1382e8d1edaSArun Thomas 
1392e8d1edaSArun Thomas  /*
1402e8d1edaSArun Thomas   * if argc == 3 and argv[2] is null, then we
1412e8d1edaSArun Thomas   * have macro-or-builtin() type call. We adjust
1422e8d1edaSArun Thomas   * argc to avoid further checking..
1432e8d1edaSArun Thomas   */
1442e8d1edaSArun Thomas  /* we keep the initial value for those built-ins that differentiate
1452e8d1edaSArun Thomas   * between builtin() and builtin.
1462e8d1edaSArun Thomas   */
1472e8d1edaSArun Thomas   	ac = argc;
1482e8d1edaSArun Thomas 
1492e8d1edaSArun Thomas 	if (argc == 3 && !*(argv[2]) && !mimic_gnu)
1502e8d1edaSArun Thomas 		argc--;
1512e8d1edaSArun Thomas 
1522e8d1edaSArun Thomas 	switch (td & TYPEMASK) {
1532e8d1edaSArun Thomas 
1542e8d1edaSArun Thomas 	case DEFITYPE:
1552e8d1edaSArun Thomas 		if (argc > 2)
1562e8d1edaSArun Thomas 			dodefine(argv[2], (argc > 3) ? argv[3] : null);
1572e8d1edaSArun Thomas 		break;
1582e8d1edaSArun Thomas 
1592e8d1edaSArun Thomas 	case PUSDTYPE:
1602e8d1edaSArun Thomas 		if (argc > 2)
1612e8d1edaSArun Thomas 			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
1622e8d1edaSArun Thomas 		break;
1632e8d1edaSArun Thomas 
1642e8d1edaSArun Thomas 	case DUMPTYPE:
1652e8d1edaSArun Thomas 		dodump(argv, argc);
1662e8d1edaSArun Thomas 		break;
1672e8d1edaSArun Thomas 
1682e8d1edaSArun Thomas 	case TRACEONTYPE:
1692e8d1edaSArun Thomas 		dotrace(argv, argc, 1);
1702e8d1edaSArun Thomas 		break;
1712e8d1edaSArun Thomas 
1722e8d1edaSArun Thomas 	case TRACEOFFTYPE:
1732e8d1edaSArun Thomas 		dotrace(argv, argc, 0);
1742e8d1edaSArun Thomas 		break;
1752e8d1edaSArun Thomas 
1762e8d1edaSArun Thomas 	case EXPRTYPE:
1772e8d1edaSArun Thomas 	/*
1782e8d1edaSArun Thomas 	 * doexpr - evaluate arithmetic
1792e8d1edaSArun Thomas 	 * expression
1802e8d1edaSArun Thomas 	 */
1812e8d1edaSArun Thomas 	{
1822e8d1edaSArun Thomas 		int base = 10;
1832e8d1edaSArun Thomas 		int maxdigits = 0;
184*0a6a1f1dSLionel Sambuc 		int e;
1852e8d1edaSArun Thomas 
1862e8d1edaSArun Thomas 		if (argc > 3) {
187*0a6a1f1dSLionel Sambuc 			base = strtoi(argv[3], NULL, 0, 2, 36, &e);
188*0a6a1f1dSLionel Sambuc 			if (e) {
1892e8d1edaSArun Thomas 				m4errx(1, "expr: base %s invalid.", argv[3]);
1902e8d1edaSArun Thomas 			}
1912e8d1edaSArun Thomas 		}
1922e8d1edaSArun Thomas 		if (argc > 4) {
193*0a6a1f1dSLionel Sambuc 			maxdigits = strtoi(argv[4], NULL, 0, 0, INT_MAX, &e);
194*0a6a1f1dSLionel Sambuc 			if (e) {
1952e8d1edaSArun Thomas 				m4errx(1, "expr: maxdigits %s invalid.", argv[4]);
1962e8d1edaSArun Thomas 			}
1972e8d1edaSArun Thomas 		}
1982e8d1edaSArun Thomas 		if (argc > 2)
1992e8d1edaSArun Thomas 			pbnumbase(expr(argv[2]), base, maxdigits);
2002e8d1edaSArun Thomas 		break;
2012e8d1edaSArun Thomas 	}
2022e8d1edaSArun Thomas 
2032e8d1edaSArun Thomas 	case IFELTYPE:
2042e8d1edaSArun Thomas 		if (argc > 4)
2052e8d1edaSArun Thomas 			doifelse(argv, argc);
2062e8d1edaSArun Thomas 		break;
2072e8d1edaSArun Thomas 
2082e8d1edaSArun Thomas 	case IFDFTYPE:
2092e8d1edaSArun Thomas 	/*
2102e8d1edaSArun Thomas 	 * doifdef - select one of two
2112e8d1edaSArun Thomas 	 * alternatives based on the existence of
2122e8d1edaSArun Thomas 	 * another definition
2132e8d1edaSArun Thomas 	 */
2142e8d1edaSArun Thomas 		if (argc > 3) {
2152e8d1edaSArun Thomas 			if (lookup_macro_definition(argv[2]) != NULL)
2162e8d1edaSArun Thomas 				pbstr(argv[3]);
2172e8d1edaSArun Thomas 			else if (argc > 4)
2182e8d1edaSArun Thomas 				pbstr(argv[4]);
2192e8d1edaSArun Thomas 		}
2202e8d1edaSArun Thomas 		break;
2212e8d1edaSArun Thomas 
2222e8d1edaSArun Thomas 	case LENGTYPE:
2232e8d1edaSArun Thomas 	/*
2242e8d1edaSArun Thomas 	 * dolen - find the length of the
2252e8d1edaSArun Thomas 	 * argument
2262e8d1edaSArun Thomas 	 */
2272e8d1edaSArun Thomas 		pbnum((argc > 2) ? strlen(argv[2]) : 0);
2282e8d1edaSArun Thomas 		break;
2292e8d1edaSArun Thomas 
2302e8d1edaSArun Thomas 	case INCRTYPE:
2312e8d1edaSArun Thomas 	/*
2322e8d1edaSArun Thomas 	 * doincr - increment the value of the
2332e8d1edaSArun Thomas 	 * argument
2342e8d1edaSArun Thomas 	 */
2352e8d1edaSArun Thomas 		if (argc > 2)
2362e8d1edaSArun Thomas 			pbnum(atoi(argv[2]) + 1);
2372e8d1edaSArun Thomas 		break;
2382e8d1edaSArun Thomas 
2392e8d1edaSArun Thomas 	case DECRTYPE:
2402e8d1edaSArun Thomas 	/*
2412e8d1edaSArun Thomas 	 * dodecr - decrement the value of the
2422e8d1edaSArun Thomas 	 * argument
2432e8d1edaSArun Thomas 	 */
2442e8d1edaSArun Thomas 		if (argc > 2)
2452e8d1edaSArun Thomas 			pbnum(atoi(argv[2]) - 1);
2462e8d1edaSArun Thomas 		break;
2472e8d1edaSArun Thomas 
2482e8d1edaSArun Thomas 	case SYSCTYPE:
2492e8d1edaSArun Thomas 	/*
2502e8d1edaSArun Thomas 	 * dosys - execute system command
2512e8d1edaSArun Thomas 	 */
2522e8d1edaSArun Thomas 		if (argc > 2) {
2532e8d1edaSArun Thomas 			fflush(stdout);
2542e8d1edaSArun Thomas 			sysval = system(argv[2]);
2552e8d1edaSArun Thomas 		}
2562e8d1edaSArun Thomas 		break;
2572e8d1edaSArun Thomas 
2582e8d1edaSArun Thomas 	case SYSVTYPE:
2592e8d1edaSArun Thomas 	/*
2602e8d1edaSArun Thomas 	 * dosysval - return value of the last
2612e8d1edaSArun Thomas 	 * system call.
2622e8d1edaSArun Thomas 	 *
2632e8d1edaSArun Thomas 	 */
2642e8d1edaSArun Thomas 		pbnum(sysval);
2652e8d1edaSArun Thomas 		break;
2662e8d1edaSArun Thomas 
2672e8d1edaSArun Thomas 	case ESYSCMDTYPE:
2682e8d1edaSArun Thomas 		if (argc > 2)
2692e8d1edaSArun Thomas 			doesyscmd(argv[2]);
2702e8d1edaSArun Thomas 	    	break;
2712e8d1edaSArun Thomas 	case INCLTYPE:
2722e8d1edaSArun Thomas 		if (argc > 2)
2732e8d1edaSArun Thomas 			if (!doincl(argv[2]))
2742e8d1edaSArun Thomas 				err(1, "%s at line %lu: include(%s)",
2752e8d1edaSArun Thomas 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
2762e8d1edaSArun Thomas 		break;
2772e8d1edaSArun Thomas 
2782e8d1edaSArun Thomas 	case SINCTYPE:
2792e8d1edaSArun Thomas 		if (argc > 2)
2802e8d1edaSArun Thomas 			(void) doincl(argv[2]);
2812e8d1edaSArun Thomas 		break;
2822e8d1edaSArun Thomas #ifdef EXTENDED
2832e8d1edaSArun Thomas 	case PASTTYPE:
2842e8d1edaSArun Thomas 		if (argc > 2)
2852e8d1edaSArun Thomas 			if (!dopaste(argv[2]))
2862e8d1edaSArun Thomas 				err(1, "%s at line %lu: paste(%s)",
2872e8d1edaSArun Thomas 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
2882e8d1edaSArun Thomas 		break;
2892e8d1edaSArun Thomas 
2902e8d1edaSArun Thomas 	case SPASTYPE:
2912e8d1edaSArun Thomas 		if (argc > 2)
2922e8d1edaSArun Thomas 			(void) dopaste(argv[2]);
2932e8d1edaSArun Thomas 		break;
2942e8d1edaSArun Thomas 	case FORMATTYPE:
2952e8d1edaSArun Thomas 		doformat(argv, argc);
2962e8d1edaSArun Thomas 		break;
2972e8d1edaSArun Thomas #endif
2982e8d1edaSArun Thomas 	case CHNQTYPE:
2992e8d1edaSArun Thomas 		dochq(argv, ac);
3002e8d1edaSArun Thomas 		break;
3012e8d1edaSArun Thomas 
3022e8d1edaSArun Thomas 	case CHNCTYPE:
3032e8d1edaSArun Thomas 		dochc(argv, argc);
3042e8d1edaSArun Thomas 		break;
3052e8d1edaSArun Thomas 
3062e8d1edaSArun Thomas 	case SUBSTYPE:
3072e8d1edaSArun Thomas 	/*
3082e8d1edaSArun Thomas 	 * dosub - select substring
3092e8d1edaSArun Thomas 	 *
3102e8d1edaSArun Thomas 	 */
3112e8d1edaSArun Thomas 		if (argc > 3)
3122e8d1edaSArun Thomas 			dosub(argv, argc);
3132e8d1edaSArun Thomas 		break;
3142e8d1edaSArun Thomas 
3152e8d1edaSArun Thomas 	case SHIFTYPE:
3162e8d1edaSArun Thomas 	/*
3172e8d1edaSArun Thomas 	 * doshift - push back all arguments
3182e8d1edaSArun Thomas 	 * except the first one (i.e. skip
3192e8d1edaSArun Thomas 	 * argv[2])
3202e8d1edaSArun Thomas 	 */
3212e8d1edaSArun Thomas 		if (argc > 3) {
3222e8d1edaSArun Thomas 			for (n = argc - 1; n > 3; n--) {
3232e8d1edaSArun Thomas 				pbstr(rquote);
3242e8d1edaSArun Thomas 				pbstr(argv[n]);
3252e8d1edaSArun Thomas 				pbstr(lquote);
3262e8d1edaSArun Thomas 				pushback(COMMA);
3272e8d1edaSArun Thomas 			}
3282e8d1edaSArun Thomas 			pbstr(rquote);
3292e8d1edaSArun Thomas 			pbstr(argv[3]);
3302e8d1edaSArun Thomas 			pbstr(lquote);
3312e8d1edaSArun Thomas 		}
3322e8d1edaSArun Thomas 		break;
3332e8d1edaSArun Thomas 
3342e8d1edaSArun Thomas 	case DIVRTYPE:
3352e8d1edaSArun Thomas 		if (argc > 2 && (n = atoi(argv[2])) != 0)
3362e8d1edaSArun Thomas 			dodiv(n);
3372e8d1edaSArun Thomas 		else {
3382e8d1edaSArun Thomas 			active = stdout;
3392e8d1edaSArun Thomas 			oindex = 0;
3402e8d1edaSArun Thomas 		}
3412e8d1edaSArun Thomas 		break;
3422e8d1edaSArun Thomas 
3432e8d1edaSArun Thomas 	case UNDVTYPE:
3442e8d1edaSArun Thomas 		doundiv(argv, argc);
3452e8d1edaSArun Thomas 		break;
3462e8d1edaSArun Thomas 
3472e8d1edaSArun Thomas 	case DIVNTYPE:
3482e8d1edaSArun Thomas 	/*
3492e8d1edaSArun Thomas 	 * dodivnum - return the number of
3502e8d1edaSArun Thomas 	 * current output diversion
3512e8d1edaSArun Thomas 	 */
3522e8d1edaSArun Thomas 		pbnum(oindex);
3532e8d1edaSArun Thomas 		break;
3542e8d1edaSArun Thomas 
3552e8d1edaSArun Thomas 	case UNDFTYPE:
3562e8d1edaSArun Thomas 	/*
3572e8d1edaSArun Thomas 	 * doundefine - undefine a previously
3582e8d1edaSArun Thomas 	 * defined macro(s) or m4 keyword(s).
3592e8d1edaSArun Thomas 	 */
3602e8d1edaSArun Thomas 		if (argc > 2)
3612e8d1edaSArun Thomas 			for (n = 2; n < argc; n++)
3622e8d1edaSArun Thomas 				macro_undefine(argv[n]);
3632e8d1edaSArun Thomas 		break;
3642e8d1edaSArun Thomas 
3652e8d1edaSArun Thomas 	case POPDTYPE:
3662e8d1edaSArun Thomas 	/*
3672e8d1edaSArun Thomas 	 * dopopdef - remove the topmost
3682e8d1edaSArun Thomas 	 * definitions of macro(s) or m4
3692e8d1edaSArun Thomas 	 * keyword(s).
3702e8d1edaSArun Thomas 	 */
3712e8d1edaSArun Thomas 		if (argc > 2)
3722e8d1edaSArun Thomas 			for (n = 2; n < argc; n++)
3732e8d1edaSArun Thomas 				macro_popdef(argv[n]);
3742e8d1edaSArun Thomas 		break;
3752e8d1edaSArun Thomas 
3762e8d1edaSArun Thomas 	case MKTMTYPE:
3772e8d1edaSArun Thomas 	/*
3782e8d1edaSArun Thomas 	 * dotemp - create a temporary file
3792e8d1edaSArun Thomas 	 */
3802e8d1edaSArun Thomas 		if (argc > 2) {
3812e8d1edaSArun Thomas 			int fd;
3822e8d1edaSArun Thomas 			char *temp;
3832e8d1edaSArun Thomas 
3842e8d1edaSArun Thomas 			temp = xstrdup(argv[2]);
3852e8d1edaSArun Thomas 
3862e8d1edaSArun Thomas 			fd = mkstemp(temp);
3872e8d1edaSArun Thomas 			if (fd == -1)
3882e8d1edaSArun Thomas 				err(1,
3892e8d1edaSArun Thomas 	    "%s at line %lu: couldn't make temp file %s",
3902e8d1edaSArun Thomas 	    CURRENT_NAME, CURRENT_LINE, argv[2]);
3912e8d1edaSArun Thomas 			close(fd);
3922e8d1edaSArun Thomas 			pbstr(temp);
3932e8d1edaSArun Thomas 			free(temp);
3942e8d1edaSArun Thomas 		}
3952e8d1edaSArun Thomas 		break;
3962e8d1edaSArun Thomas 
3972e8d1edaSArun Thomas 	case TRNLTYPE:
3982e8d1edaSArun Thomas 	/*
3992e8d1edaSArun Thomas 	 * dotranslit - replace all characters in
4002e8d1edaSArun Thomas 	 * the source string that appears in the
4012e8d1edaSArun Thomas 	 * "from" string with the corresponding
4022e8d1edaSArun Thomas 	 * characters in the "to" string.
4032e8d1edaSArun Thomas 	 */
4042e8d1edaSArun Thomas 		if (argc > 3) {
4052e8d1edaSArun Thomas 			char *temp;
4062e8d1edaSArun Thomas 
4072e8d1edaSArun Thomas 			temp = xalloc(strlen(argv[2])+1, NULL);
4082e8d1edaSArun Thomas 			if (argc > 4)
4092e8d1edaSArun Thomas 				map(temp, argv[2], argv[3], argv[4]);
4102e8d1edaSArun Thomas 			else
4112e8d1edaSArun Thomas 				map(temp, argv[2], argv[3], null);
4122e8d1edaSArun Thomas 			pbstr(temp);
4132e8d1edaSArun Thomas 			free(temp);
4142e8d1edaSArun Thomas 		} else if (argc > 2)
4152e8d1edaSArun Thomas 			pbstr(argv[2]);
4162e8d1edaSArun Thomas 		break;
4172e8d1edaSArun Thomas 
4182e8d1edaSArun Thomas 	case INDXTYPE:
4192e8d1edaSArun Thomas 	/*
4202e8d1edaSArun Thomas 	 * doindex - find the index of the second
4212e8d1edaSArun Thomas 	 * argument string in the first argument
4222e8d1edaSArun Thomas 	 * string. -1 if not present.
4232e8d1edaSArun Thomas 	 */
4242e8d1edaSArun Thomas 		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
4252e8d1edaSArun Thomas 		break;
4262e8d1edaSArun Thomas 
4272e8d1edaSArun Thomas 	case ERRPTYPE:
4282e8d1edaSArun Thomas 	/*
4292e8d1edaSArun Thomas 	 * doerrp - print the arguments to stderr
4302e8d1edaSArun Thomas 	 * file
4312e8d1edaSArun Thomas 	 */
4322e8d1edaSArun Thomas 		if (argc > 2) {
4332e8d1edaSArun Thomas 			for (n = 2; n < argc; n++)
43471c7dcb9SLionel Sambuc 				fprintf(stderr, "%s%s",
43571c7dcb9SLionel Sambuc 				    mimic_gnu && n == 2 ? "" : " ",
43671c7dcb9SLionel Sambuc 				    argv[n]);
43771c7dcb9SLionel Sambuc 			if (!mimic_gnu)
4382e8d1edaSArun Thomas 				fprintf(stderr, "\n");
4392e8d1edaSArun Thomas 		}
4402e8d1edaSArun Thomas 		break;
4412e8d1edaSArun Thomas 
4422e8d1edaSArun Thomas 	case DNLNTYPE:
4432e8d1edaSArun Thomas 	/*
4442e8d1edaSArun Thomas 	 * dodnl - eat-up-to and including
4452e8d1edaSArun Thomas 	 * newline
4462e8d1edaSArun Thomas 	 */
4472e8d1edaSArun Thomas 		while ((c = gpbc()) != '\n' && c != EOF)
4482e8d1edaSArun Thomas 			;
4492e8d1edaSArun Thomas 		break;
4502e8d1edaSArun Thomas 
4512e8d1edaSArun Thomas 	case M4WRTYPE:
4522e8d1edaSArun Thomas 	/*
4532e8d1edaSArun Thomas 	 * dom4wrap - set up for
4542e8d1edaSArun Thomas 	 * wrap-up/wind-down activity
4552e8d1edaSArun Thomas 	 */
4562e8d1edaSArun Thomas 		if (argc > 2)
4572e8d1edaSArun Thomas 			dom4wrap(argv[2]);
4582e8d1edaSArun Thomas 		break;
4592e8d1edaSArun Thomas 
4602e8d1edaSArun Thomas 	case EXITTYPE:
4612e8d1edaSArun Thomas 	/*
4622e8d1edaSArun Thomas 	 * doexit - immediate exit from m4.
4632e8d1edaSArun Thomas 	 */
4642e8d1edaSArun Thomas 		killdiv();
4652e8d1edaSArun Thomas 		exit((argc > 2) ? atoi(argv[2]) : 0);
4662e8d1edaSArun Thomas 		break;
4672e8d1edaSArun Thomas 
4682e8d1edaSArun Thomas 	case DEFNTYPE:
4692e8d1edaSArun Thomas 		if (argc > 2)
4702e8d1edaSArun Thomas 			for (n = 2; n < argc; n++)
4712e8d1edaSArun Thomas 				dodefn(argv[n]);
4722e8d1edaSArun Thomas 		break;
4732e8d1edaSArun Thomas 
4742e8d1edaSArun Thomas 	case INDIRTYPE:	/* Indirect call */
4752e8d1edaSArun Thomas 		if (argc > 2)
4762e8d1edaSArun Thomas 			doindir(argv, argc);
4772e8d1edaSArun Thomas 		break;
4782e8d1edaSArun Thomas 
4792e8d1edaSArun Thomas 	case BUILTINTYPE: /* Builtins only */
4802e8d1edaSArun Thomas 		if (argc > 2)
4812e8d1edaSArun Thomas 			dobuiltin(argv, argc);
4822e8d1edaSArun Thomas 		break;
4832e8d1edaSArun Thomas 
4842e8d1edaSArun Thomas 	case PATSTYPE:
4852e8d1edaSArun Thomas 		if (argc > 2)
4862e8d1edaSArun Thomas 			dopatsubst(argv, argc);
4872e8d1edaSArun Thomas 		break;
4882e8d1edaSArun Thomas 	case REGEXPTYPE:
4892e8d1edaSArun Thomas 		if (argc > 2)
4902e8d1edaSArun Thomas 			doregexp(argv, argc);
4912e8d1edaSArun Thomas 		break;
4922e8d1edaSArun Thomas 	case LINETYPE:
4932e8d1edaSArun Thomas 		doprintlineno(infile+ilevel);
4942e8d1edaSArun Thomas 		break;
4952e8d1edaSArun Thomas 	case FILENAMETYPE:
4962e8d1edaSArun Thomas 		doprintfilename(infile+ilevel);
4972e8d1edaSArun Thomas 		break;
4982e8d1edaSArun Thomas 	case SELFTYPE:
4992e8d1edaSArun Thomas 		pbstr(rquote);
5002e8d1edaSArun Thomas 		pbstr(argv[1]);
5012e8d1edaSArun Thomas 		pbstr(lquote);
5022e8d1edaSArun Thomas 		break;
5032e8d1edaSArun Thomas 	default:
5042e8d1edaSArun Thomas 		m4errx(1, "eval: major botch.");
5052e8d1edaSArun Thomas 		break;
5062e8d1edaSArun Thomas 	}
5072e8d1edaSArun Thomas }
5082e8d1edaSArun Thomas 
5092e8d1edaSArun Thomas /*
5102e8d1edaSArun Thomas  * expand_macro - user-defined macro expansion
5112e8d1edaSArun Thomas  */
5122e8d1edaSArun Thomas void
expand_macro(const char * argv[],int argc)5132e8d1edaSArun Thomas expand_macro(const char *argv[], int argc)
5142e8d1edaSArun Thomas {
5152e8d1edaSArun Thomas 	const char *t;
5162e8d1edaSArun Thomas 	const char *p;
5172e8d1edaSArun Thomas 	int n;
5182e8d1edaSArun Thomas 	int argno;
5192e8d1edaSArun Thomas 
5202e8d1edaSArun Thomas 	t = argv[0];		       /* defn string as a whole */
5212e8d1edaSArun Thomas 	p = t;
5222e8d1edaSArun Thomas 	while (*p)
5232e8d1edaSArun Thomas 		p++;
5242e8d1edaSArun Thomas 	p--;			       /* last character of defn */
5252e8d1edaSArun Thomas 	while (p > t) {
5262e8d1edaSArun Thomas 		if (*(p - 1) != ARGFLAG)
5272e8d1edaSArun Thomas 			PUSHBACK(*p);
5282e8d1edaSArun Thomas 		else {
5292e8d1edaSArun Thomas 			switch (*p) {
5302e8d1edaSArun Thomas 
5312e8d1edaSArun Thomas 			case '#':
5322e8d1edaSArun Thomas 				pbnum(argc - 2);
5332e8d1edaSArun Thomas 				break;
5342e8d1edaSArun Thomas 			case '0':
5352e8d1edaSArun Thomas 			case '1':
5362e8d1edaSArun Thomas 			case '2':
5372e8d1edaSArun Thomas 			case '3':
5382e8d1edaSArun Thomas 			case '4':
5392e8d1edaSArun Thomas 			case '5':
5402e8d1edaSArun Thomas 			case '6':
5412e8d1edaSArun Thomas 			case '7':
5422e8d1edaSArun Thomas 			case '8':
5432e8d1edaSArun Thomas 			case '9':
5442e8d1edaSArun Thomas 				if ((argno = *p - '0') < argc - 1)
5452e8d1edaSArun Thomas 					pbstr(argv[argno + 1]);
5462e8d1edaSArun Thomas 				break;
5472e8d1edaSArun Thomas 			case '*':
5482e8d1edaSArun Thomas 				if (argc > 2) {
5492e8d1edaSArun Thomas 					for (n = argc - 1; n > 2; n--) {
5502e8d1edaSArun Thomas 						pbstr(argv[n]);
5512e8d1edaSArun Thomas 						pushback(COMMA);
5522e8d1edaSArun Thomas 					}
5532e8d1edaSArun Thomas 					pbstr(argv[2]);
5542e8d1edaSArun Thomas 			    	}
5552e8d1edaSArun Thomas 				break;
5562e8d1edaSArun Thomas                         case '@':
5572e8d1edaSArun Thomas 				if (argc > 2) {
5582e8d1edaSArun Thomas 					for (n = argc - 1; n > 2; n--) {
5592e8d1edaSArun Thomas 						pbstr(rquote);
5602e8d1edaSArun Thomas 						pbstr(argv[n]);
5612e8d1edaSArun Thomas 						pbstr(lquote);
5622e8d1edaSArun Thomas 						pushback(COMMA);
5632e8d1edaSArun Thomas 					}
5642e8d1edaSArun Thomas 					pbstr(rquote);
5652e8d1edaSArun Thomas 					pbstr(argv[2]);
5662e8d1edaSArun Thomas 					pbstr(lquote);
5672e8d1edaSArun Thomas 				}
5682e8d1edaSArun Thomas                                 break;
5692e8d1edaSArun Thomas 			default:
5702e8d1edaSArun Thomas 				PUSHBACK(*p);
5712e8d1edaSArun Thomas 				PUSHBACK('$');
5722e8d1edaSArun Thomas 				break;
5732e8d1edaSArun Thomas 			}
5742e8d1edaSArun Thomas 			p--;
5752e8d1edaSArun Thomas 		}
5762e8d1edaSArun Thomas 		p--;
5772e8d1edaSArun Thomas 	}
5782e8d1edaSArun Thomas 	if (p == t)		       /* do last character */
5792e8d1edaSArun Thomas 		PUSHBACK(*p);
5802e8d1edaSArun Thomas }
5812e8d1edaSArun Thomas 
5822e8d1edaSArun Thomas 
5832e8d1edaSArun Thomas /*
5842e8d1edaSArun Thomas  * dodefine - install definition in the table
5852e8d1edaSArun Thomas  */
5862e8d1edaSArun Thomas void
dodefine(const char * name,const char * defn)5872e8d1edaSArun Thomas dodefine(const char *name, const char *defn)
5882e8d1edaSArun Thomas {
5892e8d1edaSArun Thomas 	if (!*name && !mimic_gnu)
5902e8d1edaSArun Thomas 		m4errx(1, "null definition.");
5912e8d1edaSArun Thomas 	else
5922e8d1edaSArun Thomas 		macro_define(name, defn);
5932e8d1edaSArun Thomas }
5942e8d1edaSArun Thomas 
5952e8d1edaSArun Thomas /*
5962e8d1edaSArun Thomas  * dodefn - push back a quoted definition of
5972e8d1edaSArun Thomas  *      the given name.
5982e8d1edaSArun Thomas  */
5992e8d1edaSArun Thomas static void
dodefn(const char * name)6002e8d1edaSArun Thomas dodefn(const char *name)
6012e8d1edaSArun Thomas {
6022e8d1edaSArun Thomas 	struct macro_definition *p;
6032e8d1edaSArun Thomas 
6042e8d1edaSArun Thomas 	if ((p = lookup_macro_definition(name)) != NULL) {
6052e8d1edaSArun Thomas 		if ((p->type & TYPEMASK) == MACRTYPE) {
6062e8d1edaSArun Thomas 			pbstr(rquote);
6072e8d1edaSArun Thomas 			pbstr(p->defn);
6082e8d1edaSArun Thomas 			pbstr(lquote);
6092e8d1edaSArun Thomas 		} else {
6102e8d1edaSArun Thomas 			pbstr(p->defn);
6112e8d1edaSArun Thomas 			pbstr(BUILTIN_MARKER);
6122e8d1edaSArun Thomas 		}
6132e8d1edaSArun Thomas 	}
6142e8d1edaSArun Thomas }
6152e8d1edaSArun Thomas 
6162e8d1edaSArun Thomas /*
6172e8d1edaSArun Thomas  * dopushdef - install a definition in the hash table
6182e8d1edaSArun Thomas  *      without removing a previous definition. Since
6192e8d1edaSArun Thomas  *      each new entry is entered in *front* of the
6202e8d1edaSArun Thomas  *      hash bucket, it hides a previous definition from
6212e8d1edaSArun Thomas  *      lookup.
6222e8d1edaSArun Thomas  */
6232e8d1edaSArun Thomas static void
dopushdef(const char * name,const char * defn)6242e8d1edaSArun Thomas dopushdef(const char *name, const char *defn)
6252e8d1edaSArun Thomas {
6262e8d1edaSArun Thomas 	if (!*name && !mimic_gnu)
6272e8d1edaSArun Thomas 		m4errx(1, "null definition.");
6282e8d1edaSArun Thomas 	else
6292e8d1edaSArun Thomas 		macro_pushdef(name, defn);
6302e8d1edaSArun Thomas }
6312e8d1edaSArun Thomas 
6322e8d1edaSArun Thomas /*
6332e8d1edaSArun Thomas  * dump_one_def - dump the specified definition.
6342e8d1edaSArun Thomas  */
6352e8d1edaSArun Thomas static void
dump_one_def(const char * name,struct macro_definition * p)6362e8d1edaSArun Thomas dump_one_def(const char *name, struct macro_definition *p)
6372e8d1edaSArun Thomas {
6382e8d1edaSArun Thomas 	if (!traceout)
6392e8d1edaSArun Thomas 		traceout = stderr;
6402e8d1edaSArun Thomas 	if (mimic_gnu) {
6412e8d1edaSArun Thomas 		if ((p->type & TYPEMASK) == MACRTYPE)
6422e8d1edaSArun Thomas 			fprintf(traceout, "%s:\t%s\n", name, p->defn);
6432e8d1edaSArun Thomas 		else {
6442e8d1edaSArun Thomas 			fprintf(traceout, "%s:\t<%s>\n", name, p->defn);
6452e8d1edaSArun Thomas 	    	}
6462e8d1edaSArun Thomas 	} else
6472e8d1edaSArun Thomas 		fprintf(traceout, "`%s'\t`%s'\n", name, p->defn);
6482e8d1edaSArun Thomas }
6492e8d1edaSArun Thomas 
6502e8d1edaSArun Thomas /*
6512e8d1edaSArun Thomas  * dodumpdef - dump the specified definitions in the hash
6522e8d1edaSArun Thomas  *      table to stderr. If nothing is specified, the entire
6532e8d1edaSArun Thomas  *      hash table is dumped.
6542e8d1edaSArun Thomas  */
6552e8d1edaSArun Thomas static void
dodump(const char * argv[],int argc)6562e8d1edaSArun Thomas dodump(const char *argv[], int argc)
6572e8d1edaSArun Thomas {
6582e8d1edaSArun Thomas 	int n;
6592e8d1edaSArun Thomas 	struct macro_definition *p;
6602e8d1edaSArun Thomas 
6612e8d1edaSArun Thomas 	if (argc > 2) {
6622e8d1edaSArun Thomas 		for (n = 2; n < argc; n++)
6632e8d1edaSArun Thomas 			if ((p = lookup_macro_definition(argv[n])) != NULL)
6642e8d1edaSArun Thomas 				dump_one_def(argv[n], p);
6652e8d1edaSArun Thomas 	} else
6662e8d1edaSArun Thomas 		macro_for_all(dump_one_def);
6672e8d1edaSArun Thomas }
6682e8d1edaSArun Thomas 
6692e8d1edaSArun Thomas /*
6702e8d1edaSArun Thomas  * dotrace - mark some macros as traced/untraced depending upon on.
6712e8d1edaSArun Thomas  */
6722e8d1edaSArun Thomas static void
dotrace(const char * argv[],int argc,int on)6732e8d1edaSArun Thomas dotrace(const char *argv[], int argc, int on)
6742e8d1edaSArun Thomas {
6752e8d1edaSArun Thomas 	int n;
6762e8d1edaSArun Thomas 
6772e8d1edaSArun Thomas 	if (argc > 2) {
6782e8d1edaSArun Thomas 		for (n = 2; n < argc; n++)
6792e8d1edaSArun Thomas 			mark_traced(argv[n], on);
6802e8d1edaSArun Thomas 	} else
6812e8d1edaSArun Thomas 		mark_traced(NULL, on);
6822e8d1edaSArun Thomas }
6832e8d1edaSArun Thomas 
6842e8d1edaSArun Thomas /*
6852e8d1edaSArun Thomas  * doifelse - select one of two alternatives - loop.
6862e8d1edaSArun Thomas  */
6872e8d1edaSArun Thomas static void
doifelse(const char * argv[],int argc)6882e8d1edaSArun Thomas doifelse(const char *argv[], int argc)
6892e8d1edaSArun Thomas {
6902e8d1edaSArun Thomas 	cycle {
6912e8d1edaSArun Thomas 		if (STREQ(argv[2], argv[3]))
6922e8d1edaSArun Thomas 			pbstr(argv[4]);
6932e8d1edaSArun Thomas 		else if (argc == 6)
6942e8d1edaSArun Thomas 			pbstr(argv[5]);
6952e8d1edaSArun Thomas 		else if (argc > 6) {
6962e8d1edaSArun Thomas 			argv += 3;
6972e8d1edaSArun Thomas 			argc -= 3;
6982e8d1edaSArun Thomas 			continue;
6992e8d1edaSArun Thomas 		}
7002e8d1edaSArun Thomas 		break;
7012e8d1edaSArun Thomas 	}
7022e8d1edaSArun Thomas }
7032e8d1edaSArun Thomas 
7042e8d1edaSArun Thomas /*
7052e8d1edaSArun Thomas  * doinclude - include a given file.
7062e8d1edaSArun Thomas  */
7072e8d1edaSArun Thomas static int
doincl(const char * ifile)7082e8d1edaSArun Thomas doincl(const char *ifile)
7092e8d1edaSArun Thomas {
7102e8d1edaSArun Thomas 	if (ilevel + 1 == MAXINP)
7112e8d1edaSArun Thomas 		m4errx(1, "too many include files.");
7122e8d1edaSArun Thomas 	if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
7132e8d1edaSArun Thomas 		ilevel++;
7142e8d1edaSArun Thomas 		bbase[ilevel] = bufbase = bp;
7152e8d1edaSArun Thomas 		return (1);
7162e8d1edaSArun Thomas 	} else
7172e8d1edaSArun Thomas 		return (0);
7182e8d1edaSArun Thomas }
7192e8d1edaSArun Thomas 
7202e8d1edaSArun Thomas #ifdef EXTENDED
7212e8d1edaSArun Thomas /*
7222e8d1edaSArun Thomas  * dopaste - include a given file without any
7232e8d1edaSArun Thomas  *           macro processing.
7242e8d1edaSArun Thomas  */
7252e8d1edaSArun Thomas static int
dopaste(const char * pfile)7262e8d1edaSArun Thomas dopaste(const char *pfile)
7272e8d1edaSArun Thomas {
7282e8d1edaSArun Thomas 	FILE *pf;
7292e8d1edaSArun Thomas 	int c;
7302e8d1edaSArun Thomas 
7312e8d1edaSArun Thomas 	if ((pf = fopen(pfile, "r")) != NULL) {
7322e8d1edaSArun Thomas 		if (synch_lines)
7332e8d1edaSArun Thomas 		    fprintf(active, "#line 1 \"%s\"\n", pfile);
7342e8d1edaSArun Thomas 		while ((c = getc(pf)) != EOF)
7352e8d1edaSArun Thomas 			putc(c, active);
7362e8d1edaSArun Thomas 		(void) fclose(pf);
7372e8d1edaSArun Thomas 		emit_synchline();
7382e8d1edaSArun Thomas 		return (1);
7392e8d1edaSArun Thomas 	} else
7402e8d1edaSArun Thomas 		return (0);
7412e8d1edaSArun Thomas }
7422e8d1edaSArun Thomas #endif
7432e8d1edaSArun Thomas 
7442e8d1edaSArun Thomas /*
7452e8d1edaSArun Thomas  * dochq - change quote characters
7462e8d1edaSArun Thomas  */
7472e8d1edaSArun Thomas static void
dochq(const char * argv[],int ac)7482e8d1edaSArun Thomas dochq(const char *argv[], int ac)
7492e8d1edaSArun Thomas {
7502e8d1edaSArun Thomas 	if (ac == 2) {
7512e8d1edaSArun Thomas 		lquote[0] = LQUOTE; lquote[1] = EOS;
7522e8d1edaSArun Thomas 		rquote[0] = RQUOTE; rquote[1] = EOS;
7532e8d1edaSArun Thomas 	} else {
7542e8d1edaSArun Thomas 		strlcpy(lquote, argv[2], sizeof(lquote));
7552e8d1edaSArun Thomas 		if (ac > 3) {
7562e8d1edaSArun Thomas 			strlcpy(rquote, argv[3], sizeof(rquote));
7572e8d1edaSArun Thomas 		} else {
7582e8d1edaSArun Thomas 			rquote[0] = ECOMMT; rquote[1] = EOS;
7592e8d1edaSArun Thomas 		}
7602e8d1edaSArun Thomas 	}
7612e8d1edaSArun Thomas }
7622e8d1edaSArun Thomas 
7632e8d1edaSArun Thomas /*
7642e8d1edaSArun Thomas  * dochc - change comment characters
7652e8d1edaSArun Thomas  */
7662e8d1edaSArun Thomas static void
dochc(const char * argv[],int argc)7672e8d1edaSArun Thomas dochc(const char *argv[], int argc)
7682e8d1edaSArun Thomas {
7692e8d1edaSArun Thomas /* XXX Note that there is no difference between no argument and a single
7702e8d1edaSArun Thomas  * empty argument.
7712e8d1edaSArun Thomas  */
7722e8d1edaSArun Thomas 	if (argc == 2) {
7732e8d1edaSArun Thomas 		scommt[0] = EOS;
7742e8d1edaSArun Thomas 		ecommt[0] = EOS;
7752e8d1edaSArun Thomas 	} else {
7762e8d1edaSArun Thomas 		strlcpy(scommt, argv[2], sizeof(scommt));
7772e8d1edaSArun Thomas 		if (argc == 3) {
7782e8d1edaSArun Thomas 			ecommt[0] = ECOMMT; ecommt[1] = EOS;
7792e8d1edaSArun Thomas 		} else {
7802e8d1edaSArun Thomas 			strlcpy(ecommt, argv[3], sizeof(ecommt));
7812e8d1edaSArun Thomas 		}
7822e8d1edaSArun Thomas 	}
7832e8d1edaSArun Thomas }
7842e8d1edaSArun Thomas 
7852e8d1edaSArun Thomas /*
7862e8d1edaSArun Thomas  * dom4wrap - expand text at EOF
7872e8d1edaSArun Thomas  */
7882e8d1edaSArun Thomas static void
dom4wrap(const char * text)7892e8d1edaSArun Thomas dom4wrap(const char *text)
7902e8d1edaSArun Thomas {
7912e8d1edaSArun Thomas 	if (wrapindex >= maxwraps) {
7922e8d1edaSArun Thomas 		if (maxwraps == 0)
7932e8d1edaSArun Thomas 			maxwraps = 16;
7942e8d1edaSArun Thomas 		else
7952e8d1edaSArun Thomas 			maxwraps *= 2;
7962e8d1edaSArun Thomas 		m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps),
7972e8d1edaSArun Thomas 		   "too many m4wraps");
7982e8d1edaSArun Thomas 	}
7992e8d1edaSArun Thomas 	m4wraps[wrapindex++] = xstrdup(text);
8002e8d1edaSArun Thomas }
8012e8d1edaSArun Thomas 
8022e8d1edaSArun Thomas /*
8032e8d1edaSArun Thomas  * dodivert - divert the output to a temporary file
8042e8d1edaSArun Thomas  */
8052e8d1edaSArun Thomas static void
dodiv(int n)8062e8d1edaSArun Thomas dodiv(int n)
8072e8d1edaSArun Thomas {
8082e8d1edaSArun Thomas 	int fd;
8092e8d1edaSArun Thomas 
8102e8d1edaSArun Thomas 	oindex = n;
8112e8d1edaSArun Thomas 	if (n >= maxout) {
8122e8d1edaSArun Thomas 		if (mimic_gnu)
8132e8d1edaSArun Thomas 			resizedivs(n + 10);
8142e8d1edaSArun Thomas 		else
8152e8d1edaSArun Thomas 			n = 0;		/* bitbucket */
8162e8d1edaSArun Thomas     	}
8172e8d1edaSArun Thomas 
8182e8d1edaSArun Thomas 	if (n < 0)
8192e8d1edaSArun Thomas 		n = 0;		       /* bitbucket */
8202e8d1edaSArun Thomas 	if (outfile[n] == NULL) {
8212e8d1edaSArun Thomas 		char fname[] = _PATH_DIVNAME;
8222e8d1edaSArun Thomas 
8232e8d1edaSArun Thomas 		if ((fd = mkstemp(fname)) < 0 ||
8242e8d1edaSArun Thomas 			(outfile[n] = fdopen(fd, "w+")) == NULL)
8252e8d1edaSArun Thomas 				err(1, "%s: cannot divert", fname);
8262e8d1edaSArun Thomas 		if (unlink(fname) == -1)
8272e8d1edaSArun Thomas 			err(1, "%s: cannot unlink", fname);
8282e8d1edaSArun Thomas 	}
8292e8d1edaSArun Thomas 	active = outfile[n];
8302e8d1edaSArun Thomas }
8312e8d1edaSArun Thomas 
8322e8d1edaSArun Thomas /*
8332e8d1edaSArun Thomas  * doundivert - undivert a specified output, or all
8342e8d1edaSArun Thomas  *              other outputs, in numerical order.
8352e8d1edaSArun Thomas  */
8362e8d1edaSArun Thomas static void
doundiv(const char * argv[],int argc)8372e8d1edaSArun Thomas doundiv(const char *argv[], int argc)
8382e8d1edaSArun Thomas {
8392e8d1edaSArun Thomas 	int ind;
8402e8d1edaSArun Thomas 	int n;
8412e8d1edaSArun Thomas 
8422e8d1edaSArun Thomas 	if (argc > 2) {
8432e8d1edaSArun Thomas 		for (ind = 2; ind < argc; ind++) {
844*0a6a1f1dSLionel Sambuc 			int e;
845*0a6a1f1dSLionel Sambuc 			n = strtoi(argv[ind], NULL, 0, 1, INT_MAX, &e);
846*0a6a1f1dSLionel Sambuc 			if (e) {
8472e8d1edaSArun Thomas 				if (errno == EINVAL && mimic_gnu)
8482e8d1edaSArun Thomas 					getdivfile(argv[ind]);
8492e8d1edaSArun Thomas 			} else {
8502e8d1edaSArun Thomas 				if (n < maxout && outfile[n] != NULL)
8512e8d1edaSArun Thomas 					getdiv(n);
8522e8d1edaSArun Thomas 			}
8532e8d1edaSArun Thomas 		}
8542e8d1edaSArun Thomas 	}
8552e8d1edaSArun Thomas 	else
8562e8d1edaSArun Thomas 		for (n = 1; n < maxout; n++)
8572e8d1edaSArun Thomas 			if (outfile[n] != NULL)
8582e8d1edaSArun Thomas 				getdiv(n);
8592e8d1edaSArun Thomas }
8602e8d1edaSArun Thomas 
8612e8d1edaSArun Thomas /*
8622e8d1edaSArun Thomas  * dosub - select substring
8632e8d1edaSArun Thomas  */
8642e8d1edaSArun Thomas static void
dosub(const char * argv[],int argc)8652e8d1edaSArun Thomas dosub(const char *argv[], int argc)
8662e8d1edaSArun Thomas {
8672e8d1edaSArun Thomas 	const char *ap, *fc, *k;
8682e8d1edaSArun Thomas 	int nc;
8692e8d1edaSArun Thomas 
8702e8d1edaSArun Thomas 	ap = argv[2];		       /* target string */
8712e8d1edaSArun Thomas #ifdef EXPR
8722e8d1edaSArun Thomas 	fc = ap + expr(argv[3]);       /* first char */
8732e8d1edaSArun Thomas #else
8742e8d1edaSArun Thomas 	fc = ap + atoi(argv[3]);       /* first char */
8752e8d1edaSArun Thomas #endif
8762e8d1edaSArun Thomas 	nc = strlen(fc);
8772e8d1edaSArun Thomas 	if (argc >= 5)
8782e8d1edaSArun Thomas #ifdef EXPR
8792e8d1edaSArun Thomas 		nc = min(nc, expr(argv[4]));
8802e8d1edaSArun Thomas #else
8812e8d1edaSArun Thomas 		nc = min(nc, atoi(argv[4]));
8822e8d1edaSArun Thomas #endif
8832e8d1edaSArun Thomas 	if (fc >= ap && fc < ap + strlen(ap))
8842e8d1edaSArun Thomas 		for (k = fc + nc - 1; k >= fc; k--)
8852e8d1edaSArun Thomas 			pushback(*k);
8862e8d1edaSArun Thomas }
8872e8d1edaSArun Thomas 
8882e8d1edaSArun Thomas /*
8892e8d1edaSArun Thomas  * map:
8902e8d1edaSArun Thomas  * map every character of s1 that is specified in from
8912e8d1edaSArun Thomas  * into s3 and replace in s. (source s1 remains untouched)
8922e8d1edaSArun Thomas  *
8932e8d1edaSArun Thomas  * This is a standard implementation of map(s,from,to) function of ICON
8942e8d1edaSArun Thomas  * language. Within mapvec, we replace every character of "from" with
8952e8d1edaSArun Thomas  * the corresponding character in "to". If "to" is shorter than "from",
8962e8d1edaSArun Thomas  * than the corresponding entries are null, which means that those
8972e8d1edaSArun Thomas  * characters dissapear altogether. Furthermore, imagine
8982e8d1edaSArun Thomas  * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
8992e8d1edaSArun Thomas  * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
9002e8d1edaSArun Thomas  * ultimately maps to `*'. In order to achieve this effect in an efficient
9012e8d1edaSArun Thomas  * manner (i.e. without multiple passes over the destination string), we
9022e8d1edaSArun Thomas  * loop over mapvec, starting with the initial source character. if the
9032e8d1edaSArun Thomas  * character value (dch) in this location is different than the source
9042e8d1edaSArun Thomas  * character (sch), sch becomes dch, once again to index into mapvec, until
9052e8d1edaSArun Thomas  * the character value stabilizes (i.e. sch = dch, in other words
9062e8d1edaSArun Thomas  * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
9072e8d1edaSArun Thomas  * character, it will stabilize, since mapvec[0] == 0 at all times. At the
9082e8d1edaSArun Thomas  * end, we restore mapvec* back to normal where mapvec[n] == n for
9092e8d1edaSArun Thomas  * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
9102e8d1edaSArun Thomas  * about 5 times faster than any algorithm that makes multiple passes over
9112e8d1edaSArun Thomas  * destination string.
9122e8d1edaSArun Thomas  */
9132e8d1edaSArun Thomas static void
map(char * dest,const char * src,const char * from,const char * to)9142e8d1edaSArun Thomas map(char *dest, const char *src, const char *from, const char *to)
9152e8d1edaSArun Thomas {
9162e8d1edaSArun Thomas 	const char *tmp;
9172e8d1edaSArun Thomas 	unsigned char sch, dch;
9182e8d1edaSArun Thomas 	static char frombis[257];
9192e8d1edaSArun Thomas 	static char tobis[257];
9202e8d1edaSArun Thomas 	static unsigned char mapvec[256] = {
9212e8d1edaSArun Thomas 	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
9222e8d1edaSArun Thomas 	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
9232e8d1edaSArun Thomas 	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
9242e8d1edaSArun Thomas 	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
9252e8d1edaSArun Thomas 	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
9262e8d1edaSArun Thomas 	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
9272e8d1edaSArun Thomas 	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
9282e8d1edaSArun Thomas 	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
9292e8d1edaSArun Thomas 	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
9302e8d1edaSArun Thomas 	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
9312e8d1edaSArun Thomas 	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
9322e8d1edaSArun Thomas 	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
9332e8d1edaSArun Thomas 	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
9342e8d1edaSArun Thomas 	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
9352e8d1edaSArun Thomas 	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
9362e8d1edaSArun Thomas 	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
9372e8d1edaSArun Thomas 	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
9382e8d1edaSArun Thomas 	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
9392e8d1edaSArun Thomas 	};
9402e8d1edaSArun Thomas 
9412e8d1edaSArun Thomas 	if (*src) {
9422e8d1edaSArun Thomas 		if (mimic_gnu) {
9432e8d1edaSArun Thomas 			/*
9442e8d1edaSArun Thomas 			 * expand character ranges on the fly
9452e8d1edaSArun Thomas 			 */
9462e8d1edaSArun Thomas 			from = handledash(frombis, frombis + 256, from);
9472e8d1edaSArun Thomas 			to = handledash(tobis, tobis + 256, to);
9482e8d1edaSArun Thomas 		}
9492e8d1edaSArun Thomas 		tmp = from;
9502e8d1edaSArun Thomas 	/*
9512e8d1edaSArun Thomas 	 * create a mapping between "from" and
9522e8d1edaSArun Thomas 	 * "to"
9532e8d1edaSArun Thomas 	 */
9542e8d1edaSArun Thomas 		while (*from)
9552e8d1edaSArun Thomas 			mapvec[(unsigned char)(*from++)] = (*to) ?
9562e8d1edaSArun Thomas 				(unsigned char)(*to++) : 0;
9572e8d1edaSArun Thomas 
9582e8d1edaSArun Thomas 		while (*src) {
9592e8d1edaSArun Thomas 			sch = (unsigned char)(*src++);
9602e8d1edaSArun Thomas 			dch = mapvec[sch];
9612e8d1edaSArun Thomas 			while (dch != sch) {
9622e8d1edaSArun Thomas 				sch = dch;
9632e8d1edaSArun Thomas 				dch = mapvec[sch];
9642e8d1edaSArun Thomas 			}
9652e8d1edaSArun Thomas 			if ((*dest = (char)dch))
9662e8d1edaSArun Thomas 				dest++;
9672e8d1edaSArun Thomas 		}
9682e8d1edaSArun Thomas 	/*
9692e8d1edaSArun Thomas 	 * restore all the changed characters
9702e8d1edaSArun Thomas 	 */
9712e8d1edaSArun Thomas 		while (*tmp) {
9722e8d1edaSArun Thomas 			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
9732e8d1edaSArun Thomas 			tmp++;
9742e8d1edaSArun Thomas 		}
9752e8d1edaSArun Thomas 	}
9762e8d1edaSArun Thomas 	*dest = '\0';
9772e8d1edaSArun Thomas }
9782e8d1edaSArun Thomas 
9792e8d1edaSArun Thomas 
9802e8d1edaSArun Thomas /*
9812e8d1edaSArun Thomas  * handledash:
9822e8d1edaSArun Thomas  *  use buffer to copy the src string, expanding character ranges
9832e8d1edaSArun Thomas  * on the way.
9842e8d1edaSArun Thomas  */
9852e8d1edaSArun Thomas static const char *
handledash(char * buffer,char * end,const char * src)9862e8d1edaSArun Thomas handledash(char *buffer, char *end, const char *src)
9872e8d1edaSArun Thomas {
9882e8d1edaSArun Thomas 	char *p;
9892e8d1edaSArun Thomas 
9902e8d1edaSArun Thomas 	p = buffer;
9912e8d1edaSArun Thomas 	while(*src) {
9922e8d1edaSArun Thomas 		if (src[1] == '-' && src[2]) {
9932e8d1edaSArun Thomas 			unsigned char i;
9942e8d1edaSArun Thomas 			if ((unsigned char)src[0] <= (unsigned char)src[2]) {
9952e8d1edaSArun Thomas 				for (i = (unsigned char)src[0];
9962e8d1edaSArun Thomas 				    i <= (unsigned char)src[2]; i++) {
9972e8d1edaSArun Thomas 					*p++ = i;
9982e8d1edaSArun Thomas 					if (p == end) {
9992e8d1edaSArun Thomas 						*p = '\0';
10002e8d1edaSArun Thomas 						return buffer;
10012e8d1edaSArun Thomas 					}
10022e8d1edaSArun Thomas 				}
10032e8d1edaSArun Thomas 			} else {
10042e8d1edaSArun Thomas 				for (i = (unsigned char)src[0];
10052e8d1edaSArun Thomas 				    i >= (unsigned char)src[2]; i--) {
10062e8d1edaSArun Thomas 					*p++ = i;
10072e8d1edaSArun Thomas 					if (p == end) {
10082e8d1edaSArun Thomas 						*p = '\0';
10092e8d1edaSArun Thomas 						return buffer;
10102e8d1edaSArun Thomas 					}
10112e8d1edaSArun Thomas 				}
10122e8d1edaSArun Thomas 			}
10132e8d1edaSArun Thomas 			src += 3;
10142e8d1edaSArun Thomas 		} else
10152e8d1edaSArun Thomas 			*p++ = *src++;
10162e8d1edaSArun Thomas 		if (p == end)
10172e8d1edaSArun Thomas 			break;
10182e8d1edaSArun Thomas 	}
10192e8d1edaSArun Thomas 	*p = '\0';
10202e8d1edaSArun Thomas 	return buffer;
10212e8d1edaSArun Thomas }
1022