xref: /minix3/usr.bin/m4/eval.c (revision 2e8d1eda1b10b1eefcc41d19325e6baa0615ae5c)
1*2e8d1edaSArun Thomas /*	$OpenBSD: eval.c,v 1.66 2008/08/21 21:01:47 espie Exp $	*/
2*2e8d1edaSArun Thomas /*	$NetBSD: eval.c,v 1.20 2009/10/26 21:11:28 christos Exp $	*/
3*2e8d1edaSArun Thomas 
4*2e8d1edaSArun Thomas /*
5*2e8d1edaSArun Thomas  * Copyright (c) 1989, 1993
6*2e8d1edaSArun Thomas  *	The Regents of the University of California.  All rights reserved.
7*2e8d1edaSArun Thomas  *
8*2e8d1edaSArun Thomas  * This code is derived from software contributed to Berkeley by
9*2e8d1edaSArun Thomas  * Ozan Yigit at York University.
10*2e8d1edaSArun Thomas  *
11*2e8d1edaSArun Thomas  * Redistribution and use in source and binary forms, with or without
12*2e8d1edaSArun Thomas  * modification, are permitted provided that the following conditions
13*2e8d1edaSArun Thomas  * are met:
14*2e8d1edaSArun Thomas  * 1. Redistributions of source code must retain the above copyright
15*2e8d1edaSArun Thomas  *    notice, this list of conditions and the following disclaimer.
16*2e8d1edaSArun Thomas  * 2. Redistributions in binary form must reproduce the above copyright
17*2e8d1edaSArun Thomas  *    notice, this list of conditions and the following disclaimer in the
18*2e8d1edaSArun Thomas  *    documentation and/or other materials provided with the distribution.
19*2e8d1edaSArun Thomas  * 3. Neither the name of the University nor the names of its contributors
20*2e8d1edaSArun Thomas  *    may be used to endorse or promote products derived from this software
21*2e8d1edaSArun Thomas  *    without specific prior written permission.
22*2e8d1edaSArun Thomas  *
23*2e8d1edaSArun Thomas  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24*2e8d1edaSArun Thomas  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25*2e8d1edaSArun Thomas  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26*2e8d1edaSArun Thomas  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27*2e8d1edaSArun Thomas  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28*2e8d1edaSArun Thomas  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29*2e8d1edaSArun Thomas  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30*2e8d1edaSArun Thomas  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31*2e8d1edaSArun Thomas  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32*2e8d1edaSArun Thomas  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33*2e8d1edaSArun Thomas  * SUCH DAMAGE.
34*2e8d1edaSArun Thomas  */
35*2e8d1edaSArun Thomas 
36*2e8d1edaSArun Thomas /*
37*2e8d1edaSArun Thomas  * eval.c
38*2e8d1edaSArun Thomas  * Facility: m4 macro processor
39*2e8d1edaSArun Thomas  * by: oz
40*2e8d1edaSArun Thomas  */
41*2e8d1edaSArun Thomas #if HAVE_NBTOOL_CONFIG_H
42*2e8d1edaSArun Thomas #include "nbtool_config.h"
43*2e8d1edaSArun Thomas #endif
44*2e8d1edaSArun Thomas #include <sys/cdefs.h>
45*2e8d1edaSArun Thomas __RCSID("$NetBSD: eval.c,v 1.20 2009/10/26 21:11:28 christos Exp $");
46*2e8d1edaSArun Thomas 
47*2e8d1edaSArun Thomas #include <sys/types.h>
48*2e8d1edaSArun Thomas #include <err.h>
49*2e8d1edaSArun Thomas #include <errno.h>
50*2e8d1edaSArun Thomas #include <limits.h>
51*2e8d1edaSArun Thomas #include <unistd.h>
52*2e8d1edaSArun Thomas #include <stdio.h>
53*2e8d1edaSArun Thomas #include <stdlib.h>
54*2e8d1edaSArun Thomas #include <stddef.h>
55*2e8d1edaSArun Thomas #include <string.h>
56*2e8d1edaSArun Thomas #include <fcntl.h>
57*2e8d1edaSArun Thomas #include "mdef.h"
58*2e8d1edaSArun Thomas #include "stdd.h"
59*2e8d1edaSArun Thomas #include "extern.h"
60*2e8d1edaSArun Thomas #include "pathnames.h"
61*2e8d1edaSArun Thomas 
62*2e8d1edaSArun Thomas static void	dodefn(const char *);
63*2e8d1edaSArun Thomas static void	dopushdef(const char *, const char *);
64*2e8d1edaSArun Thomas static void	dodump(const char *[], int);
65*2e8d1edaSArun Thomas static void	dotrace(const char *[], int, int);
66*2e8d1edaSArun Thomas static void	doifelse(const char *[], int);
67*2e8d1edaSArun Thomas static int	doincl(const char *);
68*2e8d1edaSArun Thomas static int	dopaste(const char *);
69*2e8d1edaSArun Thomas static void	dochq(const char *[], int);
70*2e8d1edaSArun Thomas static void	dochc(const char *[], int);
71*2e8d1edaSArun Thomas static void	dom4wrap(const char *);
72*2e8d1edaSArun Thomas static void	dodiv(int);
73*2e8d1edaSArun Thomas static void	doundiv(const char *[], int);
74*2e8d1edaSArun Thomas static void	dosub(const char *[], int);
75*2e8d1edaSArun Thomas static void	map(char *, const char *, const char *, const char *);
76*2e8d1edaSArun Thomas static const char *handledash(char *, char *, const char *);
77*2e8d1edaSArun Thomas static void	expand_builtin(const char *[], int, int);
78*2e8d1edaSArun Thomas static void	expand_macro(const char *[], int);
79*2e8d1edaSArun Thomas static void	dump_one_def(const char *, struct macro_definition *);
80*2e8d1edaSArun Thomas 
81*2e8d1edaSArun Thomas unsigned long	expansion_id;
82*2e8d1edaSArun Thomas 
83*2e8d1edaSArun Thomas /*
84*2e8d1edaSArun Thomas  * eval - eval all macros and builtins calls
85*2e8d1edaSArun Thomas  *	  argc - number of elements in argv.
86*2e8d1edaSArun Thomas  *	  argv - element vector :
87*2e8d1edaSArun Thomas  *			argv[0] = definition of a user
88*2e8d1edaSArun Thomas  *				  macro or NULL if built-in.
89*2e8d1edaSArun Thomas  *			argv[1] = name of the macro or
90*2e8d1edaSArun Thomas  *				  built-in.
91*2e8d1edaSArun Thomas  *			argv[2] = parameters to user-defined
92*2e8d1edaSArun Thomas  *			   .	  macro or built-in.
93*2e8d1edaSArun Thomas  *			   .
94*2e8d1edaSArun Thomas  *
95*2e8d1edaSArun Thomas  * A call in the form of macro-or-builtin() will result in:
96*2e8d1edaSArun Thomas  *			argv[0] = nullstr
97*2e8d1edaSArun Thomas  *			argv[1] = macro-or-builtin
98*2e8d1edaSArun Thomas  *			argv[2] = nullstr
99*2e8d1edaSArun Thomas  *
100*2e8d1edaSArun Thomas  * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
101*2e8d1edaSArun Thomas  */
102*2e8d1edaSArun Thomas void
103*2e8d1edaSArun Thomas eval(const char *argv[], int argc, int td, int is_traced)
104*2e8d1edaSArun Thomas {
105*2e8d1edaSArun Thomas 	size_t mark = SIZE_MAX;
106*2e8d1edaSArun Thomas 
107*2e8d1edaSArun Thomas 	expansion_id++;
108*2e8d1edaSArun Thomas 	if (td & RECDEF)
109*2e8d1edaSArun Thomas 		m4errx(1, "expanding recursive definition for %s.", argv[1]);
110*2e8d1edaSArun Thomas 	if (is_traced)
111*2e8d1edaSArun Thomas 		mark = trace(argv, argc, infile+ilevel);
112*2e8d1edaSArun Thomas 	if (td == MACRTYPE)
113*2e8d1edaSArun Thomas 		expand_macro(argv, argc);
114*2e8d1edaSArun Thomas 	else
115*2e8d1edaSArun Thomas 		expand_builtin(argv, argc, td);
116*2e8d1edaSArun Thomas     	if (mark != SIZE_MAX)
117*2e8d1edaSArun Thomas 		finish_trace(mark);
118*2e8d1edaSArun Thomas }
119*2e8d1edaSArun Thomas 
120*2e8d1edaSArun Thomas /*
121*2e8d1edaSArun Thomas  * expand_builtin - evaluate built-in macros.
122*2e8d1edaSArun Thomas  */
123*2e8d1edaSArun Thomas void
124*2e8d1edaSArun Thomas expand_builtin(const char *argv[], int argc, int td)
125*2e8d1edaSArun Thomas {
126*2e8d1edaSArun Thomas 	int c, n;
127*2e8d1edaSArun Thomas 	int ac;
128*2e8d1edaSArun Thomas 	static int sysval = 0;
129*2e8d1edaSArun Thomas 
130*2e8d1edaSArun Thomas #ifdef DEBUG
131*2e8d1edaSArun Thomas 	printf("argc = %d\n", argc);
132*2e8d1edaSArun Thomas 	for (n = 0; n < argc; n++)
133*2e8d1edaSArun Thomas 		printf("argv[%d] = %s\n", n, argv[n]);
134*2e8d1edaSArun Thomas 	fflush(stdout);
135*2e8d1edaSArun Thomas #endif
136*2e8d1edaSArun Thomas 
137*2e8d1edaSArun Thomas  /*
138*2e8d1edaSArun Thomas   * if argc == 3 and argv[2] is null, then we
139*2e8d1edaSArun Thomas   * have macro-or-builtin() type call. We adjust
140*2e8d1edaSArun Thomas   * argc to avoid further checking..
141*2e8d1edaSArun Thomas   */
142*2e8d1edaSArun Thomas  /* we keep the initial value for those built-ins that differentiate
143*2e8d1edaSArun Thomas   * between builtin() and builtin.
144*2e8d1edaSArun Thomas   */
145*2e8d1edaSArun Thomas   	ac = argc;
146*2e8d1edaSArun Thomas 
147*2e8d1edaSArun Thomas 	if (argc == 3 && !*(argv[2]) && !mimic_gnu)
148*2e8d1edaSArun Thomas 		argc--;
149*2e8d1edaSArun Thomas 
150*2e8d1edaSArun Thomas 	switch (td & TYPEMASK) {
151*2e8d1edaSArun Thomas 
152*2e8d1edaSArun Thomas 	case DEFITYPE:
153*2e8d1edaSArun Thomas 		if (argc > 2)
154*2e8d1edaSArun Thomas 			dodefine(argv[2], (argc > 3) ? argv[3] : null);
155*2e8d1edaSArun Thomas 		break;
156*2e8d1edaSArun Thomas 
157*2e8d1edaSArun Thomas 	case PUSDTYPE:
158*2e8d1edaSArun Thomas 		if (argc > 2)
159*2e8d1edaSArun Thomas 			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
160*2e8d1edaSArun Thomas 		break;
161*2e8d1edaSArun Thomas 
162*2e8d1edaSArun Thomas 	case DUMPTYPE:
163*2e8d1edaSArun Thomas 		dodump(argv, argc);
164*2e8d1edaSArun Thomas 		break;
165*2e8d1edaSArun Thomas 
166*2e8d1edaSArun Thomas 	case TRACEONTYPE:
167*2e8d1edaSArun Thomas 		dotrace(argv, argc, 1);
168*2e8d1edaSArun Thomas 		break;
169*2e8d1edaSArun Thomas 
170*2e8d1edaSArun Thomas 	case TRACEOFFTYPE:
171*2e8d1edaSArun Thomas 		dotrace(argv, argc, 0);
172*2e8d1edaSArun Thomas 		break;
173*2e8d1edaSArun Thomas 
174*2e8d1edaSArun Thomas 	case EXPRTYPE:
175*2e8d1edaSArun Thomas 	/*
176*2e8d1edaSArun Thomas 	 * doexpr - evaluate arithmetic
177*2e8d1edaSArun Thomas 	 * expression
178*2e8d1edaSArun Thomas 	 */
179*2e8d1edaSArun Thomas 	{
180*2e8d1edaSArun Thomas 		int base = 10;
181*2e8d1edaSArun Thomas 		int maxdigits = 0;
182*2e8d1edaSArun Thomas 		const char *errstr;
183*2e8d1edaSArun Thomas 
184*2e8d1edaSArun Thomas 		if (argc > 3) {
185*2e8d1edaSArun Thomas 			base = strtonum(argv[3], 2, 36, &errstr);
186*2e8d1edaSArun Thomas 			if (errstr) {
187*2e8d1edaSArun Thomas 				m4errx(1, "expr: base %s invalid.", argv[3]);
188*2e8d1edaSArun Thomas 			}
189*2e8d1edaSArun Thomas 		}
190*2e8d1edaSArun Thomas 		if (argc > 4) {
191*2e8d1edaSArun Thomas 			maxdigits = strtonum(argv[4], 0, INT_MAX, &errstr);
192*2e8d1edaSArun Thomas 			if (errstr) {
193*2e8d1edaSArun Thomas 				m4errx(1, "expr: maxdigits %s invalid.", argv[4]);
194*2e8d1edaSArun Thomas 			}
195*2e8d1edaSArun Thomas 		}
196*2e8d1edaSArun Thomas 		if (argc > 2)
197*2e8d1edaSArun Thomas 			pbnumbase(expr(argv[2]), base, maxdigits);
198*2e8d1edaSArun Thomas 		break;
199*2e8d1edaSArun Thomas 	}
200*2e8d1edaSArun Thomas 
201*2e8d1edaSArun Thomas 	case IFELTYPE:
202*2e8d1edaSArun Thomas 		if (argc > 4)
203*2e8d1edaSArun Thomas 			doifelse(argv, argc);
204*2e8d1edaSArun Thomas 		break;
205*2e8d1edaSArun Thomas 
206*2e8d1edaSArun Thomas 	case IFDFTYPE:
207*2e8d1edaSArun Thomas 	/*
208*2e8d1edaSArun Thomas 	 * doifdef - select one of two
209*2e8d1edaSArun Thomas 	 * alternatives based on the existence of
210*2e8d1edaSArun Thomas 	 * another definition
211*2e8d1edaSArun Thomas 	 */
212*2e8d1edaSArun Thomas 		if (argc > 3) {
213*2e8d1edaSArun Thomas 			if (lookup_macro_definition(argv[2]) != NULL)
214*2e8d1edaSArun Thomas 				pbstr(argv[3]);
215*2e8d1edaSArun Thomas 			else if (argc > 4)
216*2e8d1edaSArun Thomas 				pbstr(argv[4]);
217*2e8d1edaSArun Thomas 		}
218*2e8d1edaSArun Thomas 		break;
219*2e8d1edaSArun Thomas 
220*2e8d1edaSArun Thomas 	case LENGTYPE:
221*2e8d1edaSArun Thomas 	/*
222*2e8d1edaSArun Thomas 	 * dolen - find the length of the
223*2e8d1edaSArun Thomas 	 * argument
224*2e8d1edaSArun Thomas 	 */
225*2e8d1edaSArun Thomas 		pbnum((argc > 2) ? strlen(argv[2]) : 0);
226*2e8d1edaSArun Thomas 		break;
227*2e8d1edaSArun Thomas 
228*2e8d1edaSArun Thomas 	case INCRTYPE:
229*2e8d1edaSArun Thomas 	/*
230*2e8d1edaSArun Thomas 	 * doincr - increment the value of the
231*2e8d1edaSArun Thomas 	 * argument
232*2e8d1edaSArun Thomas 	 */
233*2e8d1edaSArun Thomas 		if (argc > 2)
234*2e8d1edaSArun Thomas 			pbnum(atoi(argv[2]) + 1);
235*2e8d1edaSArun Thomas 		break;
236*2e8d1edaSArun Thomas 
237*2e8d1edaSArun Thomas 	case DECRTYPE:
238*2e8d1edaSArun Thomas 	/*
239*2e8d1edaSArun Thomas 	 * dodecr - decrement the value of the
240*2e8d1edaSArun Thomas 	 * argument
241*2e8d1edaSArun Thomas 	 */
242*2e8d1edaSArun Thomas 		if (argc > 2)
243*2e8d1edaSArun Thomas 			pbnum(atoi(argv[2]) - 1);
244*2e8d1edaSArun Thomas 		break;
245*2e8d1edaSArun Thomas 
246*2e8d1edaSArun Thomas 	case SYSCTYPE:
247*2e8d1edaSArun Thomas 	/*
248*2e8d1edaSArun Thomas 	 * dosys - execute system command
249*2e8d1edaSArun Thomas 	 */
250*2e8d1edaSArun Thomas 		if (argc > 2) {
251*2e8d1edaSArun Thomas 			fflush(stdout);
252*2e8d1edaSArun Thomas 			sysval = system(argv[2]);
253*2e8d1edaSArun Thomas 		}
254*2e8d1edaSArun Thomas 		break;
255*2e8d1edaSArun Thomas 
256*2e8d1edaSArun Thomas 	case SYSVTYPE:
257*2e8d1edaSArun Thomas 	/*
258*2e8d1edaSArun Thomas 	 * dosysval - return value of the last
259*2e8d1edaSArun Thomas 	 * system call.
260*2e8d1edaSArun Thomas 	 *
261*2e8d1edaSArun Thomas 	 */
262*2e8d1edaSArun Thomas 		pbnum(sysval);
263*2e8d1edaSArun Thomas 		break;
264*2e8d1edaSArun Thomas 
265*2e8d1edaSArun Thomas 	case ESYSCMDTYPE:
266*2e8d1edaSArun Thomas 		if (argc > 2)
267*2e8d1edaSArun Thomas 			doesyscmd(argv[2]);
268*2e8d1edaSArun Thomas 	    	break;
269*2e8d1edaSArun Thomas 	case INCLTYPE:
270*2e8d1edaSArun Thomas 		if (argc > 2)
271*2e8d1edaSArun Thomas 			if (!doincl(argv[2]))
272*2e8d1edaSArun Thomas 				err(1, "%s at line %lu: include(%s)",
273*2e8d1edaSArun Thomas 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
274*2e8d1edaSArun Thomas 		break;
275*2e8d1edaSArun Thomas 
276*2e8d1edaSArun Thomas 	case SINCTYPE:
277*2e8d1edaSArun Thomas 		if (argc > 2)
278*2e8d1edaSArun Thomas 			(void) doincl(argv[2]);
279*2e8d1edaSArun Thomas 		break;
280*2e8d1edaSArun Thomas #ifdef EXTENDED
281*2e8d1edaSArun Thomas 	case PASTTYPE:
282*2e8d1edaSArun Thomas 		if (argc > 2)
283*2e8d1edaSArun Thomas 			if (!dopaste(argv[2]))
284*2e8d1edaSArun Thomas 				err(1, "%s at line %lu: paste(%s)",
285*2e8d1edaSArun Thomas 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
286*2e8d1edaSArun Thomas 		break;
287*2e8d1edaSArun Thomas 
288*2e8d1edaSArun Thomas 	case SPASTYPE:
289*2e8d1edaSArun Thomas 		if (argc > 2)
290*2e8d1edaSArun Thomas 			(void) dopaste(argv[2]);
291*2e8d1edaSArun Thomas 		break;
292*2e8d1edaSArun Thomas 	case FORMATTYPE:
293*2e8d1edaSArun Thomas 		doformat(argv, argc);
294*2e8d1edaSArun Thomas 		break;
295*2e8d1edaSArun Thomas #endif
296*2e8d1edaSArun Thomas 	case CHNQTYPE:
297*2e8d1edaSArun Thomas 		dochq(argv, ac);
298*2e8d1edaSArun Thomas 		break;
299*2e8d1edaSArun Thomas 
300*2e8d1edaSArun Thomas 	case CHNCTYPE:
301*2e8d1edaSArun Thomas 		dochc(argv, argc);
302*2e8d1edaSArun Thomas 		break;
303*2e8d1edaSArun Thomas 
304*2e8d1edaSArun Thomas 	case SUBSTYPE:
305*2e8d1edaSArun Thomas 	/*
306*2e8d1edaSArun Thomas 	 * dosub - select substring
307*2e8d1edaSArun Thomas 	 *
308*2e8d1edaSArun Thomas 	 */
309*2e8d1edaSArun Thomas 		if (argc > 3)
310*2e8d1edaSArun Thomas 			dosub(argv, argc);
311*2e8d1edaSArun Thomas 		break;
312*2e8d1edaSArun Thomas 
313*2e8d1edaSArun Thomas 	case SHIFTYPE:
314*2e8d1edaSArun Thomas 	/*
315*2e8d1edaSArun Thomas 	 * doshift - push back all arguments
316*2e8d1edaSArun Thomas 	 * except the first one (i.e. skip
317*2e8d1edaSArun Thomas 	 * argv[2])
318*2e8d1edaSArun Thomas 	 */
319*2e8d1edaSArun Thomas 		if (argc > 3) {
320*2e8d1edaSArun Thomas 			for (n = argc - 1; n > 3; n--) {
321*2e8d1edaSArun Thomas 				pbstr(rquote);
322*2e8d1edaSArun Thomas 				pbstr(argv[n]);
323*2e8d1edaSArun Thomas 				pbstr(lquote);
324*2e8d1edaSArun Thomas 				pushback(COMMA);
325*2e8d1edaSArun Thomas 			}
326*2e8d1edaSArun Thomas 			pbstr(rquote);
327*2e8d1edaSArun Thomas 			pbstr(argv[3]);
328*2e8d1edaSArun Thomas 			pbstr(lquote);
329*2e8d1edaSArun Thomas 		}
330*2e8d1edaSArun Thomas 		break;
331*2e8d1edaSArun Thomas 
332*2e8d1edaSArun Thomas 	case DIVRTYPE:
333*2e8d1edaSArun Thomas 		if (argc > 2 && (n = atoi(argv[2])) != 0)
334*2e8d1edaSArun Thomas 			dodiv(n);
335*2e8d1edaSArun Thomas 		else {
336*2e8d1edaSArun Thomas 			active = stdout;
337*2e8d1edaSArun Thomas 			oindex = 0;
338*2e8d1edaSArun Thomas 		}
339*2e8d1edaSArun Thomas 		break;
340*2e8d1edaSArun Thomas 
341*2e8d1edaSArun Thomas 	case UNDVTYPE:
342*2e8d1edaSArun Thomas 		doundiv(argv, argc);
343*2e8d1edaSArun Thomas 		break;
344*2e8d1edaSArun Thomas 
345*2e8d1edaSArun Thomas 	case DIVNTYPE:
346*2e8d1edaSArun Thomas 	/*
347*2e8d1edaSArun Thomas 	 * dodivnum - return the number of
348*2e8d1edaSArun Thomas 	 * current output diversion
349*2e8d1edaSArun Thomas 	 */
350*2e8d1edaSArun Thomas 		pbnum(oindex);
351*2e8d1edaSArun Thomas 		break;
352*2e8d1edaSArun Thomas 
353*2e8d1edaSArun Thomas 	case UNDFTYPE:
354*2e8d1edaSArun Thomas 	/*
355*2e8d1edaSArun Thomas 	 * doundefine - undefine a previously
356*2e8d1edaSArun Thomas 	 * defined macro(s) or m4 keyword(s).
357*2e8d1edaSArun Thomas 	 */
358*2e8d1edaSArun Thomas 		if (argc > 2)
359*2e8d1edaSArun Thomas 			for (n = 2; n < argc; n++)
360*2e8d1edaSArun Thomas 				macro_undefine(argv[n]);
361*2e8d1edaSArun Thomas 		break;
362*2e8d1edaSArun Thomas 
363*2e8d1edaSArun Thomas 	case POPDTYPE:
364*2e8d1edaSArun Thomas 	/*
365*2e8d1edaSArun Thomas 	 * dopopdef - remove the topmost
366*2e8d1edaSArun Thomas 	 * definitions of macro(s) or m4
367*2e8d1edaSArun Thomas 	 * keyword(s).
368*2e8d1edaSArun Thomas 	 */
369*2e8d1edaSArun Thomas 		if (argc > 2)
370*2e8d1edaSArun Thomas 			for (n = 2; n < argc; n++)
371*2e8d1edaSArun Thomas 				macro_popdef(argv[n]);
372*2e8d1edaSArun Thomas 		break;
373*2e8d1edaSArun Thomas 
374*2e8d1edaSArun Thomas 	case MKTMTYPE:
375*2e8d1edaSArun Thomas 	/*
376*2e8d1edaSArun Thomas 	 * dotemp - create a temporary file
377*2e8d1edaSArun Thomas 	 */
378*2e8d1edaSArun Thomas 		if (argc > 2) {
379*2e8d1edaSArun Thomas 			int fd;
380*2e8d1edaSArun Thomas 			char *temp;
381*2e8d1edaSArun Thomas 
382*2e8d1edaSArun Thomas 			temp = xstrdup(argv[2]);
383*2e8d1edaSArun Thomas 
384*2e8d1edaSArun Thomas 			fd = mkstemp(temp);
385*2e8d1edaSArun Thomas 			if (fd == -1)
386*2e8d1edaSArun Thomas 				err(1,
387*2e8d1edaSArun Thomas 	    "%s at line %lu: couldn't make temp file %s",
388*2e8d1edaSArun Thomas 	    CURRENT_NAME, CURRENT_LINE, argv[2]);
389*2e8d1edaSArun Thomas 			close(fd);
390*2e8d1edaSArun Thomas 			pbstr(temp);
391*2e8d1edaSArun Thomas 			free(temp);
392*2e8d1edaSArun Thomas 		}
393*2e8d1edaSArun Thomas 		break;
394*2e8d1edaSArun Thomas 
395*2e8d1edaSArun Thomas 	case TRNLTYPE:
396*2e8d1edaSArun Thomas 	/*
397*2e8d1edaSArun Thomas 	 * dotranslit - replace all characters in
398*2e8d1edaSArun Thomas 	 * the source string that appears in the
399*2e8d1edaSArun Thomas 	 * "from" string with the corresponding
400*2e8d1edaSArun Thomas 	 * characters in the "to" string.
401*2e8d1edaSArun Thomas 	 */
402*2e8d1edaSArun Thomas 		if (argc > 3) {
403*2e8d1edaSArun Thomas 			char *temp;
404*2e8d1edaSArun Thomas 
405*2e8d1edaSArun Thomas 			temp = xalloc(strlen(argv[2])+1, NULL);
406*2e8d1edaSArun Thomas 			if (argc > 4)
407*2e8d1edaSArun Thomas 				map(temp, argv[2], argv[3], argv[4]);
408*2e8d1edaSArun Thomas 			else
409*2e8d1edaSArun Thomas 				map(temp, argv[2], argv[3], null);
410*2e8d1edaSArun Thomas 			pbstr(temp);
411*2e8d1edaSArun Thomas 			free(temp);
412*2e8d1edaSArun Thomas 		} else if (argc > 2)
413*2e8d1edaSArun Thomas 			pbstr(argv[2]);
414*2e8d1edaSArun Thomas 		break;
415*2e8d1edaSArun Thomas 
416*2e8d1edaSArun Thomas 	case INDXTYPE:
417*2e8d1edaSArun Thomas 	/*
418*2e8d1edaSArun Thomas 	 * doindex - find the index of the second
419*2e8d1edaSArun Thomas 	 * argument string in the first argument
420*2e8d1edaSArun Thomas 	 * string. -1 if not present.
421*2e8d1edaSArun Thomas 	 */
422*2e8d1edaSArun Thomas 		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
423*2e8d1edaSArun Thomas 		break;
424*2e8d1edaSArun Thomas 
425*2e8d1edaSArun Thomas 	case ERRPTYPE:
426*2e8d1edaSArun Thomas 	/*
427*2e8d1edaSArun Thomas 	 * doerrp - print the arguments to stderr
428*2e8d1edaSArun Thomas 	 * file
429*2e8d1edaSArun Thomas 	 */
430*2e8d1edaSArun Thomas 		if (argc > 2) {
431*2e8d1edaSArun Thomas 			for (n = 2; n < argc; n++)
432*2e8d1edaSArun Thomas 				fprintf(stderr, "%s ", argv[n]);
433*2e8d1edaSArun Thomas 			fprintf(stderr, "\n");
434*2e8d1edaSArun Thomas 		}
435*2e8d1edaSArun Thomas 		break;
436*2e8d1edaSArun Thomas 
437*2e8d1edaSArun Thomas 	case DNLNTYPE:
438*2e8d1edaSArun Thomas 	/*
439*2e8d1edaSArun Thomas 	 * dodnl - eat-up-to and including
440*2e8d1edaSArun Thomas 	 * newline
441*2e8d1edaSArun Thomas 	 */
442*2e8d1edaSArun Thomas 		while ((c = gpbc()) != '\n' && c != EOF)
443*2e8d1edaSArun Thomas 			;
444*2e8d1edaSArun Thomas 		break;
445*2e8d1edaSArun Thomas 
446*2e8d1edaSArun Thomas 	case M4WRTYPE:
447*2e8d1edaSArun Thomas 	/*
448*2e8d1edaSArun Thomas 	 * dom4wrap - set up for
449*2e8d1edaSArun Thomas 	 * wrap-up/wind-down activity
450*2e8d1edaSArun Thomas 	 */
451*2e8d1edaSArun Thomas 		if (argc > 2)
452*2e8d1edaSArun Thomas 			dom4wrap(argv[2]);
453*2e8d1edaSArun Thomas 		break;
454*2e8d1edaSArun Thomas 
455*2e8d1edaSArun Thomas 	case EXITTYPE:
456*2e8d1edaSArun Thomas 	/*
457*2e8d1edaSArun Thomas 	 * doexit - immediate exit from m4.
458*2e8d1edaSArun Thomas 	 */
459*2e8d1edaSArun Thomas 		killdiv();
460*2e8d1edaSArun Thomas 		exit((argc > 2) ? atoi(argv[2]) : 0);
461*2e8d1edaSArun Thomas 		break;
462*2e8d1edaSArun Thomas 
463*2e8d1edaSArun Thomas 	case DEFNTYPE:
464*2e8d1edaSArun Thomas 		if (argc > 2)
465*2e8d1edaSArun Thomas 			for (n = 2; n < argc; n++)
466*2e8d1edaSArun Thomas 				dodefn(argv[n]);
467*2e8d1edaSArun Thomas 		break;
468*2e8d1edaSArun Thomas 
469*2e8d1edaSArun Thomas 	case INDIRTYPE:	/* Indirect call */
470*2e8d1edaSArun Thomas 		if (argc > 2)
471*2e8d1edaSArun Thomas 			doindir(argv, argc);
472*2e8d1edaSArun Thomas 		break;
473*2e8d1edaSArun Thomas 
474*2e8d1edaSArun Thomas 	case BUILTINTYPE: /* Builtins only */
475*2e8d1edaSArun Thomas 		if (argc > 2)
476*2e8d1edaSArun Thomas 			dobuiltin(argv, argc);
477*2e8d1edaSArun Thomas 		break;
478*2e8d1edaSArun Thomas 
479*2e8d1edaSArun Thomas 	case PATSTYPE:
480*2e8d1edaSArun Thomas 		if (argc > 2)
481*2e8d1edaSArun Thomas 			dopatsubst(argv, argc);
482*2e8d1edaSArun Thomas 		break;
483*2e8d1edaSArun Thomas 	case REGEXPTYPE:
484*2e8d1edaSArun Thomas 		if (argc > 2)
485*2e8d1edaSArun Thomas 			doregexp(argv, argc);
486*2e8d1edaSArun Thomas 		break;
487*2e8d1edaSArun Thomas 	case LINETYPE:
488*2e8d1edaSArun Thomas 		doprintlineno(infile+ilevel);
489*2e8d1edaSArun Thomas 		break;
490*2e8d1edaSArun Thomas 	case FILENAMETYPE:
491*2e8d1edaSArun Thomas 		doprintfilename(infile+ilevel);
492*2e8d1edaSArun Thomas 		break;
493*2e8d1edaSArun Thomas 	case SELFTYPE:
494*2e8d1edaSArun Thomas 		pbstr(rquote);
495*2e8d1edaSArun Thomas 		pbstr(argv[1]);
496*2e8d1edaSArun Thomas 		pbstr(lquote);
497*2e8d1edaSArun Thomas 		break;
498*2e8d1edaSArun Thomas 	default:
499*2e8d1edaSArun Thomas 		m4errx(1, "eval: major botch.");
500*2e8d1edaSArun Thomas 		break;
501*2e8d1edaSArun Thomas 	}
502*2e8d1edaSArun Thomas }
503*2e8d1edaSArun Thomas 
504*2e8d1edaSArun Thomas /*
505*2e8d1edaSArun Thomas  * expand_macro - user-defined macro expansion
506*2e8d1edaSArun Thomas  */
507*2e8d1edaSArun Thomas void
508*2e8d1edaSArun Thomas expand_macro(const char *argv[], int argc)
509*2e8d1edaSArun Thomas {
510*2e8d1edaSArun Thomas 	const char *t;
511*2e8d1edaSArun Thomas 	const char *p;
512*2e8d1edaSArun Thomas 	int n;
513*2e8d1edaSArun Thomas 	int argno;
514*2e8d1edaSArun Thomas 
515*2e8d1edaSArun Thomas 	t = argv[0];		       /* defn string as a whole */
516*2e8d1edaSArun Thomas 	p = t;
517*2e8d1edaSArun Thomas 	while (*p)
518*2e8d1edaSArun Thomas 		p++;
519*2e8d1edaSArun Thomas 	p--;			       /* last character of defn */
520*2e8d1edaSArun Thomas 	while (p > t) {
521*2e8d1edaSArun Thomas 		if (*(p - 1) != ARGFLAG)
522*2e8d1edaSArun Thomas 			PUSHBACK(*p);
523*2e8d1edaSArun Thomas 		else {
524*2e8d1edaSArun Thomas 			switch (*p) {
525*2e8d1edaSArun Thomas 
526*2e8d1edaSArun Thomas 			case '#':
527*2e8d1edaSArun Thomas 				pbnum(argc - 2);
528*2e8d1edaSArun Thomas 				break;
529*2e8d1edaSArun Thomas 			case '0':
530*2e8d1edaSArun Thomas 			case '1':
531*2e8d1edaSArun Thomas 			case '2':
532*2e8d1edaSArun Thomas 			case '3':
533*2e8d1edaSArun Thomas 			case '4':
534*2e8d1edaSArun Thomas 			case '5':
535*2e8d1edaSArun Thomas 			case '6':
536*2e8d1edaSArun Thomas 			case '7':
537*2e8d1edaSArun Thomas 			case '8':
538*2e8d1edaSArun Thomas 			case '9':
539*2e8d1edaSArun Thomas 				if ((argno = *p - '0') < argc - 1)
540*2e8d1edaSArun Thomas 					pbstr(argv[argno + 1]);
541*2e8d1edaSArun Thomas 				break;
542*2e8d1edaSArun Thomas 			case '*':
543*2e8d1edaSArun Thomas 				if (argc > 2) {
544*2e8d1edaSArun Thomas 					for (n = argc - 1; n > 2; n--) {
545*2e8d1edaSArun Thomas 						pbstr(argv[n]);
546*2e8d1edaSArun Thomas 						pushback(COMMA);
547*2e8d1edaSArun Thomas 					}
548*2e8d1edaSArun Thomas 					pbstr(argv[2]);
549*2e8d1edaSArun Thomas 			    	}
550*2e8d1edaSArun Thomas 				break;
551*2e8d1edaSArun Thomas                         case '@':
552*2e8d1edaSArun Thomas 				if (argc > 2) {
553*2e8d1edaSArun Thomas 					for (n = argc - 1; n > 2; n--) {
554*2e8d1edaSArun Thomas 						pbstr(rquote);
555*2e8d1edaSArun Thomas 						pbstr(argv[n]);
556*2e8d1edaSArun Thomas 						pbstr(lquote);
557*2e8d1edaSArun Thomas 						pushback(COMMA);
558*2e8d1edaSArun Thomas 					}
559*2e8d1edaSArun Thomas 					pbstr(rquote);
560*2e8d1edaSArun Thomas 					pbstr(argv[2]);
561*2e8d1edaSArun Thomas 					pbstr(lquote);
562*2e8d1edaSArun Thomas 				}
563*2e8d1edaSArun Thomas                                 break;
564*2e8d1edaSArun Thomas 			default:
565*2e8d1edaSArun Thomas 				PUSHBACK(*p);
566*2e8d1edaSArun Thomas 				PUSHBACK('$');
567*2e8d1edaSArun Thomas 				break;
568*2e8d1edaSArun Thomas 			}
569*2e8d1edaSArun Thomas 			p--;
570*2e8d1edaSArun Thomas 		}
571*2e8d1edaSArun Thomas 		p--;
572*2e8d1edaSArun Thomas 	}
573*2e8d1edaSArun Thomas 	if (p == t)		       /* do last character */
574*2e8d1edaSArun Thomas 		PUSHBACK(*p);
575*2e8d1edaSArun Thomas }
576*2e8d1edaSArun Thomas 
577*2e8d1edaSArun Thomas 
578*2e8d1edaSArun Thomas /*
579*2e8d1edaSArun Thomas  * dodefine - install definition in the table
580*2e8d1edaSArun Thomas  */
581*2e8d1edaSArun Thomas void
582*2e8d1edaSArun Thomas dodefine(const char *name, const char *defn)
583*2e8d1edaSArun Thomas {
584*2e8d1edaSArun Thomas 	if (!*name && !mimic_gnu)
585*2e8d1edaSArun Thomas 		m4errx(1, "null definition.");
586*2e8d1edaSArun Thomas 	else
587*2e8d1edaSArun Thomas 		macro_define(name, defn);
588*2e8d1edaSArun Thomas }
589*2e8d1edaSArun Thomas 
590*2e8d1edaSArun Thomas /*
591*2e8d1edaSArun Thomas  * dodefn - push back a quoted definition of
592*2e8d1edaSArun Thomas  *      the given name.
593*2e8d1edaSArun Thomas  */
594*2e8d1edaSArun Thomas static void
595*2e8d1edaSArun Thomas dodefn(const char *name)
596*2e8d1edaSArun Thomas {
597*2e8d1edaSArun Thomas 	struct macro_definition *p;
598*2e8d1edaSArun Thomas 
599*2e8d1edaSArun Thomas 	if ((p = lookup_macro_definition(name)) != NULL) {
600*2e8d1edaSArun Thomas 		if ((p->type & TYPEMASK) == MACRTYPE) {
601*2e8d1edaSArun Thomas 			pbstr(rquote);
602*2e8d1edaSArun Thomas 			pbstr(p->defn);
603*2e8d1edaSArun Thomas 			pbstr(lquote);
604*2e8d1edaSArun Thomas 		} else {
605*2e8d1edaSArun Thomas 			pbstr(p->defn);
606*2e8d1edaSArun Thomas 			pbstr(BUILTIN_MARKER);
607*2e8d1edaSArun Thomas 		}
608*2e8d1edaSArun Thomas 	}
609*2e8d1edaSArun Thomas }
610*2e8d1edaSArun Thomas 
611*2e8d1edaSArun Thomas /*
612*2e8d1edaSArun Thomas  * dopushdef - install a definition in the hash table
613*2e8d1edaSArun Thomas  *      without removing a previous definition. Since
614*2e8d1edaSArun Thomas  *      each new entry is entered in *front* of the
615*2e8d1edaSArun Thomas  *      hash bucket, it hides a previous definition from
616*2e8d1edaSArun Thomas  *      lookup.
617*2e8d1edaSArun Thomas  */
618*2e8d1edaSArun Thomas static void
619*2e8d1edaSArun Thomas dopushdef(const char *name, const char *defn)
620*2e8d1edaSArun Thomas {
621*2e8d1edaSArun Thomas 	if (!*name && !mimic_gnu)
622*2e8d1edaSArun Thomas 		m4errx(1, "null definition.");
623*2e8d1edaSArun Thomas 	else
624*2e8d1edaSArun Thomas 		macro_pushdef(name, defn);
625*2e8d1edaSArun Thomas }
626*2e8d1edaSArun Thomas 
627*2e8d1edaSArun Thomas /*
628*2e8d1edaSArun Thomas  * dump_one_def - dump the specified definition.
629*2e8d1edaSArun Thomas  */
630*2e8d1edaSArun Thomas static void
631*2e8d1edaSArun Thomas dump_one_def(const char *name, struct macro_definition *p)
632*2e8d1edaSArun Thomas {
633*2e8d1edaSArun Thomas 	if (!traceout)
634*2e8d1edaSArun Thomas 		traceout = stderr;
635*2e8d1edaSArun Thomas 	if (mimic_gnu) {
636*2e8d1edaSArun Thomas 		if ((p->type & TYPEMASK) == MACRTYPE)
637*2e8d1edaSArun Thomas 			fprintf(traceout, "%s:\t%s\n", name, p->defn);
638*2e8d1edaSArun Thomas 		else {
639*2e8d1edaSArun Thomas 			fprintf(traceout, "%s:\t<%s>\n", name, p->defn);
640*2e8d1edaSArun Thomas 	    	}
641*2e8d1edaSArun Thomas 	} else
642*2e8d1edaSArun Thomas 		fprintf(traceout, "`%s'\t`%s'\n", name, p->defn);
643*2e8d1edaSArun Thomas }
644*2e8d1edaSArun Thomas 
645*2e8d1edaSArun Thomas /*
646*2e8d1edaSArun Thomas  * dodumpdef - dump the specified definitions in the hash
647*2e8d1edaSArun Thomas  *      table to stderr. If nothing is specified, the entire
648*2e8d1edaSArun Thomas  *      hash table is dumped.
649*2e8d1edaSArun Thomas  */
650*2e8d1edaSArun Thomas static void
651*2e8d1edaSArun Thomas dodump(const char *argv[], int argc)
652*2e8d1edaSArun Thomas {
653*2e8d1edaSArun Thomas 	int n;
654*2e8d1edaSArun Thomas 	struct macro_definition *p;
655*2e8d1edaSArun Thomas 
656*2e8d1edaSArun Thomas 	if (argc > 2) {
657*2e8d1edaSArun Thomas 		for (n = 2; n < argc; n++)
658*2e8d1edaSArun Thomas 			if ((p = lookup_macro_definition(argv[n])) != NULL)
659*2e8d1edaSArun Thomas 				dump_one_def(argv[n], p);
660*2e8d1edaSArun Thomas 	} else
661*2e8d1edaSArun Thomas 		macro_for_all(dump_one_def);
662*2e8d1edaSArun Thomas }
663*2e8d1edaSArun Thomas 
664*2e8d1edaSArun Thomas /*
665*2e8d1edaSArun Thomas  * dotrace - mark some macros as traced/untraced depending upon on.
666*2e8d1edaSArun Thomas  */
667*2e8d1edaSArun Thomas static void
668*2e8d1edaSArun Thomas dotrace(const char *argv[], int argc, int on)
669*2e8d1edaSArun Thomas {
670*2e8d1edaSArun Thomas 	int n;
671*2e8d1edaSArun Thomas 
672*2e8d1edaSArun Thomas 	if (argc > 2) {
673*2e8d1edaSArun Thomas 		for (n = 2; n < argc; n++)
674*2e8d1edaSArun Thomas 			mark_traced(argv[n], on);
675*2e8d1edaSArun Thomas 	} else
676*2e8d1edaSArun Thomas 		mark_traced(NULL, on);
677*2e8d1edaSArun Thomas }
678*2e8d1edaSArun Thomas 
679*2e8d1edaSArun Thomas /*
680*2e8d1edaSArun Thomas  * doifelse - select one of two alternatives - loop.
681*2e8d1edaSArun Thomas  */
682*2e8d1edaSArun Thomas static void
683*2e8d1edaSArun Thomas doifelse(const char *argv[], int argc)
684*2e8d1edaSArun Thomas {
685*2e8d1edaSArun Thomas 	cycle {
686*2e8d1edaSArun Thomas 		if (STREQ(argv[2], argv[3]))
687*2e8d1edaSArun Thomas 			pbstr(argv[4]);
688*2e8d1edaSArun Thomas 		else if (argc == 6)
689*2e8d1edaSArun Thomas 			pbstr(argv[5]);
690*2e8d1edaSArun Thomas 		else if (argc > 6) {
691*2e8d1edaSArun Thomas 			argv += 3;
692*2e8d1edaSArun Thomas 			argc -= 3;
693*2e8d1edaSArun Thomas 			continue;
694*2e8d1edaSArun Thomas 		}
695*2e8d1edaSArun Thomas 		break;
696*2e8d1edaSArun Thomas 	}
697*2e8d1edaSArun Thomas }
698*2e8d1edaSArun Thomas 
699*2e8d1edaSArun Thomas /*
700*2e8d1edaSArun Thomas  * doinclude - include a given file.
701*2e8d1edaSArun Thomas  */
702*2e8d1edaSArun Thomas static int
703*2e8d1edaSArun Thomas doincl(const char *ifile)
704*2e8d1edaSArun Thomas {
705*2e8d1edaSArun Thomas 	if (ilevel + 1 == MAXINP)
706*2e8d1edaSArun Thomas 		m4errx(1, "too many include files.");
707*2e8d1edaSArun Thomas 	if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
708*2e8d1edaSArun Thomas 		ilevel++;
709*2e8d1edaSArun Thomas 		bbase[ilevel] = bufbase = bp;
710*2e8d1edaSArun Thomas 		return (1);
711*2e8d1edaSArun Thomas 	} else
712*2e8d1edaSArun Thomas 		return (0);
713*2e8d1edaSArun Thomas }
714*2e8d1edaSArun Thomas 
715*2e8d1edaSArun Thomas #ifdef EXTENDED
716*2e8d1edaSArun Thomas /*
717*2e8d1edaSArun Thomas  * dopaste - include a given file without any
718*2e8d1edaSArun Thomas  *           macro processing.
719*2e8d1edaSArun Thomas  */
720*2e8d1edaSArun Thomas static int
721*2e8d1edaSArun Thomas dopaste(const char *pfile)
722*2e8d1edaSArun Thomas {
723*2e8d1edaSArun Thomas 	FILE *pf;
724*2e8d1edaSArun Thomas 	int c;
725*2e8d1edaSArun Thomas 
726*2e8d1edaSArun Thomas 	if ((pf = fopen(pfile, "r")) != NULL) {
727*2e8d1edaSArun Thomas 		if (synch_lines)
728*2e8d1edaSArun Thomas 		    fprintf(active, "#line 1 \"%s\"\n", pfile);
729*2e8d1edaSArun Thomas 		while ((c = getc(pf)) != EOF)
730*2e8d1edaSArun Thomas 			putc(c, active);
731*2e8d1edaSArun Thomas 		(void) fclose(pf);
732*2e8d1edaSArun Thomas 		emit_synchline();
733*2e8d1edaSArun Thomas 		return (1);
734*2e8d1edaSArun Thomas 	} else
735*2e8d1edaSArun Thomas 		return (0);
736*2e8d1edaSArun Thomas }
737*2e8d1edaSArun Thomas #endif
738*2e8d1edaSArun Thomas 
739*2e8d1edaSArun Thomas /*
740*2e8d1edaSArun Thomas  * dochq - change quote characters
741*2e8d1edaSArun Thomas  */
742*2e8d1edaSArun Thomas static void
743*2e8d1edaSArun Thomas dochq(const char *argv[], int ac)
744*2e8d1edaSArun Thomas {
745*2e8d1edaSArun Thomas 	if (ac == 2) {
746*2e8d1edaSArun Thomas 		lquote[0] = LQUOTE; lquote[1] = EOS;
747*2e8d1edaSArun Thomas 		rquote[0] = RQUOTE; rquote[1] = EOS;
748*2e8d1edaSArun Thomas 	} else {
749*2e8d1edaSArun Thomas 		strlcpy(lquote, argv[2], sizeof(lquote));
750*2e8d1edaSArun Thomas 		if (ac > 3) {
751*2e8d1edaSArun Thomas 			strlcpy(rquote, argv[3], sizeof(rquote));
752*2e8d1edaSArun Thomas 		} else {
753*2e8d1edaSArun Thomas 			rquote[0] = ECOMMT; rquote[1] = EOS;
754*2e8d1edaSArun Thomas 		}
755*2e8d1edaSArun Thomas 	}
756*2e8d1edaSArun Thomas }
757*2e8d1edaSArun Thomas 
758*2e8d1edaSArun Thomas /*
759*2e8d1edaSArun Thomas  * dochc - change comment characters
760*2e8d1edaSArun Thomas  */
761*2e8d1edaSArun Thomas static void
762*2e8d1edaSArun Thomas dochc(const char *argv[], int argc)
763*2e8d1edaSArun Thomas {
764*2e8d1edaSArun Thomas /* XXX Note that there is no difference between no argument and a single
765*2e8d1edaSArun Thomas  * empty argument.
766*2e8d1edaSArun Thomas  */
767*2e8d1edaSArun Thomas 	if (argc == 2) {
768*2e8d1edaSArun Thomas 		scommt[0] = EOS;
769*2e8d1edaSArun Thomas 		ecommt[0] = EOS;
770*2e8d1edaSArun Thomas 	} else {
771*2e8d1edaSArun Thomas 		strlcpy(scommt, argv[2], sizeof(scommt));
772*2e8d1edaSArun Thomas 		if (argc == 3) {
773*2e8d1edaSArun Thomas 			ecommt[0] = ECOMMT; ecommt[1] = EOS;
774*2e8d1edaSArun Thomas 		} else {
775*2e8d1edaSArun Thomas 			strlcpy(ecommt, argv[3], sizeof(ecommt));
776*2e8d1edaSArun Thomas 		}
777*2e8d1edaSArun Thomas 	}
778*2e8d1edaSArun Thomas }
779*2e8d1edaSArun Thomas 
780*2e8d1edaSArun Thomas /*
781*2e8d1edaSArun Thomas  * dom4wrap - expand text at EOF
782*2e8d1edaSArun Thomas  */
783*2e8d1edaSArun Thomas static void
784*2e8d1edaSArun Thomas dom4wrap(const char *text)
785*2e8d1edaSArun Thomas {
786*2e8d1edaSArun Thomas 	if (wrapindex >= maxwraps) {
787*2e8d1edaSArun Thomas 		if (maxwraps == 0)
788*2e8d1edaSArun Thomas 			maxwraps = 16;
789*2e8d1edaSArun Thomas 		else
790*2e8d1edaSArun Thomas 			maxwraps *= 2;
791*2e8d1edaSArun Thomas 		m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps),
792*2e8d1edaSArun Thomas 		   "too many m4wraps");
793*2e8d1edaSArun Thomas 	}
794*2e8d1edaSArun Thomas 	m4wraps[wrapindex++] = xstrdup(text);
795*2e8d1edaSArun Thomas }
796*2e8d1edaSArun Thomas 
797*2e8d1edaSArun Thomas /*
798*2e8d1edaSArun Thomas  * dodivert - divert the output to a temporary file
799*2e8d1edaSArun Thomas  */
800*2e8d1edaSArun Thomas static void
801*2e8d1edaSArun Thomas dodiv(int n)
802*2e8d1edaSArun Thomas {
803*2e8d1edaSArun Thomas 	int fd;
804*2e8d1edaSArun Thomas 
805*2e8d1edaSArun Thomas 	oindex = n;
806*2e8d1edaSArun Thomas 	if (n >= maxout) {
807*2e8d1edaSArun Thomas 		if (mimic_gnu)
808*2e8d1edaSArun Thomas 			resizedivs(n + 10);
809*2e8d1edaSArun Thomas 		else
810*2e8d1edaSArun Thomas 			n = 0;		/* bitbucket */
811*2e8d1edaSArun Thomas     	}
812*2e8d1edaSArun Thomas 
813*2e8d1edaSArun Thomas 	if (n < 0)
814*2e8d1edaSArun Thomas 		n = 0;		       /* bitbucket */
815*2e8d1edaSArun Thomas 	if (outfile[n] == NULL) {
816*2e8d1edaSArun Thomas 		char fname[] = _PATH_DIVNAME;
817*2e8d1edaSArun Thomas 
818*2e8d1edaSArun Thomas 		if ((fd = mkstemp(fname)) < 0 ||
819*2e8d1edaSArun Thomas 			(outfile[n] = fdopen(fd, "w+")) == NULL)
820*2e8d1edaSArun Thomas 				err(1, "%s: cannot divert", fname);
821*2e8d1edaSArun Thomas 		if (unlink(fname) == -1)
822*2e8d1edaSArun Thomas 			err(1, "%s: cannot unlink", fname);
823*2e8d1edaSArun Thomas 	}
824*2e8d1edaSArun Thomas 	active = outfile[n];
825*2e8d1edaSArun Thomas }
826*2e8d1edaSArun Thomas 
827*2e8d1edaSArun Thomas /*
828*2e8d1edaSArun Thomas  * doundivert - undivert a specified output, or all
829*2e8d1edaSArun Thomas  *              other outputs, in numerical order.
830*2e8d1edaSArun Thomas  */
831*2e8d1edaSArun Thomas static void
832*2e8d1edaSArun Thomas doundiv(const char *argv[], int argc)
833*2e8d1edaSArun Thomas {
834*2e8d1edaSArun Thomas 	int ind;
835*2e8d1edaSArun Thomas 	int n;
836*2e8d1edaSArun Thomas 
837*2e8d1edaSArun Thomas 	if (argc > 2) {
838*2e8d1edaSArun Thomas 		for (ind = 2; ind < argc; ind++) {
839*2e8d1edaSArun Thomas 			const char *errstr;
840*2e8d1edaSArun Thomas 			n = strtonum(argv[ind], 1, INT_MAX, &errstr);
841*2e8d1edaSArun Thomas 			if (errstr) {
842*2e8d1edaSArun Thomas 				if (errno == EINVAL && mimic_gnu)
843*2e8d1edaSArun Thomas 					getdivfile(argv[ind]);
844*2e8d1edaSArun Thomas 			} else {
845*2e8d1edaSArun Thomas 				if (n < maxout && outfile[n] != NULL)
846*2e8d1edaSArun Thomas 					getdiv(n);
847*2e8d1edaSArun Thomas 			}
848*2e8d1edaSArun Thomas 		}
849*2e8d1edaSArun Thomas 	}
850*2e8d1edaSArun Thomas 	else
851*2e8d1edaSArun Thomas 		for (n = 1; n < maxout; n++)
852*2e8d1edaSArun Thomas 			if (outfile[n] != NULL)
853*2e8d1edaSArun Thomas 				getdiv(n);
854*2e8d1edaSArun Thomas }
855*2e8d1edaSArun Thomas 
856*2e8d1edaSArun Thomas /*
857*2e8d1edaSArun Thomas  * dosub - select substring
858*2e8d1edaSArun Thomas  */
859*2e8d1edaSArun Thomas static void
860*2e8d1edaSArun Thomas dosub(const char *argv[], int argc)
861*2e8d1edaSArun Thomas {
862*2e8d1edaSArun Thomas 	const char *ap, *fc, *k;
863*2e8d1edaSArun Thomas 	int nc;
864*2e8d1edaSArun Thomas 
865*2e8d1edaSArun Thomas 	ap = argv[2];		       /* target string */
866*2e8d1edaSArun Thomas #ifdef EXPR
867*2e8d1edaSArun Thomas 	fc = ap + expr(argv[3]);       /* first char */
868*2e8d1edaSArun Thomas #else
869*2e8d1edaSArun Thomas 	fc = ap + atoi(argv[3]);       /* first char */
870*2e8d1edaSArun Thomas #endif
871*2e8d1edaSArun Thomas 	nc = strlen(fc);
872*2e8d1edaSArun Thomas 	if (argc >= 5)
873*2e8d1edaSArun Thomas #ifdef EXPR
874*2e8d1edaSArun Thomas 		nc = min(nc, expr(argv[4]));
875*2e8d1edaSArun Thomas #else
876*2e8d1edaSArun Thomas 		nc = min(nc, atoi(argv[4]));
877*2e8d1edaSArun Thomas #endif
878*2e8d1edaSArun Thomas 	if (fc >= ap && fc < ap + strlen(ap))
879*2e8d1edaSArun Thomas 		for (k = fc + nc - 1; k >= fc; k--)
880*2e8d1edaSArun Thomas 			pushback(*k);
881*2e8d1edaSArun Thomas }
882*2e8d1edaSArun Thomas 
883*2e8d1edaSArun Thomas /*
884*2e8d1edaSArun Thomas  * map:
885*2e8d1edaSArun Thomas  * map every character of s1 that is specified in from
886*2e8d1edaSArun Thomas  * into s3 and replace in s. (source s1 remains untouched)
887*2e8d1edaSArun Thomas  *
888*2e8d1edaSArun Thomas  * This is a standard implementation of map(s,from,to) function of ICON
889*2e8d1edaSArun Thomas  * language. Within mapvec, we replace every character of "from" with
890*2e8d1edaSArun Thomas  * the corresponding character in "to". If "to" is shorter than "from",
891*2e8d1edaSArun Thomas  * than the corresponding entries are null, which means that those
892*2e8d1edaSArun Thomas  * characters dissapear altogether. Furthermore, imagine
893*2e8d1edaSArun Thomas  * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
894*2e8d1edaSArun Thomas  * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
895*2e8d1edaSArun Thomas  * ultimately maps to `*'. In order to achieve this effect in an efficient
896*2e8d1edaSArun Thomas  * manner (i.e. without multiple passes over the destination string), we
897*2e8d1edaSArun Thomas  * loop over mapvec, starting with the initial source character. if the
898*2e8d1edaSArun Thomas  * character value (dch) in this location is different than the source
899*2e8d1edaSArun Thomas  * character (sch), sch becomes dch, once again to index into mapvec, until
900*2e8d1edaSArun Thomas  * the character value stabilizes (i.e. sch = dch, in other words
901*2e8d1edaSArun Thomas  * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
902*2e8d1edaSArun Thomas  * character, it will stabilize, since mapvec[0] == 0 at all times. At the
903*2e8d1edaSArun Thomas  * end, we restore mapvec* back to normal where mapvec[n] == n for
904*2e8d1edaSArun Thomas  * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
905*2e8d1edaSArun Thomas  * about 5 times faster than any algorithm that makes multiple passes over
906*2e8d1edaSArun Thomas  * destination string.
907*2e8d1edaSArun Thomas  */
908*2e8d1edaSArun Thomas static void
909*2e8d1edaSArun Thomas map(char *dest, const char *src, const char *from, const char *to)
910*2e8d1edaSArun Thomas {
911*2e8d1edaSArun Thomas 	const char *tmp;
912*2e8d1edaSArun Thomas 	unsigned char sch, dch;
913*2e8d1edaSArun Thomas 	static char frombis[257];
914*2e8d1edaSArun Thomas 	static char tobis[257];
915*2e8d1edaSArun Thomas 	static unsigned char mapvec[256] = {
916*2e8d1edaSArun Thomas 	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
917*2e8d1edaSArun Thomas 	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
918*2e8d1edaSArun Thomas 	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
919*2e8d1edaSArun Thomas 	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
920*2e8d1edaSArun Thomas 	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
921*2e8d1edaSArun Thomas 	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
922*2e8d1edaSArun Thomas 	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
923*2e8d1edaSArun Thomas 	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
924*2e8d1edaSArun Thomas 	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
925*2e8d1edaSArun Thomas 	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
926*2e8d1edaSArun Thomas 	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
927*2e8d1edaSArun Thomas 	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
928*2e8d1edaSArun Thomas 	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
929*2e8d1edaSArun Thomas 	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
930*2e8d1edaSArun Thomas 	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
931*2e8d1edaSArun Thomas 	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
932*2e8d1edaSArun Thomas 	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
933*2e8d1edaSArun Thomas 	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
934*2e8d1edaSArun Thomas 	};
935*2e8d1edaSArun Thomas 
936*2e8d1edaSArun Thomas 	if (*src) {
937*2e8d1edaSArun Thomas 		if (mimic_gnu) {
938*2e8d1edaSArun Thomas 			/*
939*2e8d1edaSArun Thomas 			 * expand character ranges on the fly
940*2e8d1edaSArun Thomas 			 */
941*2e8d1edaSArun Thomas 			from = handledash(frombis, frombis + 256, from);
942*2e8d1edaSArun Thomas 			to = handledash(tobis, tobis + 256, to);
943*2e8d1edaSArun Thomas 		}
944*2e8d1edaSArun Thomas 		tmp = from;
945*2e8d1edaSArun Thomas 	/*
946*2e8d1edaSArun Thomas 	 * create a mapping between "from" and
947*2e8d1edaSArun Thomas 	 * "to"
948*2e8d1edaSArun Thomas 	 */
949*2e8d1edaSArun Thomas 		while (*from)
950*2e8d1edaSArun Thomas 			mapvec[(unsigned char)(*from++)] = (*to) ?
951*2e8d1edaSArun Thomas 				(unsigned char)(*to++) : 0;
952*2e8d1edaSArun Thomas 
953*2e8d1edaSArun Thomas 		while (*src) {
954*2e8d1edaSArun Thomas 			sch = (unsigned char)(*src++);
955*2e8d1edaSArun Thomas 			dch = mapvec[sch];
956*2e8d1edaSArun Thomas 			while (dch != sch) {
957*2e8d1edaSArun Thomas 				sch = dch;
958*2e8d1edaSArun Thomas 				dch = mapvec[sch];
959*2e8d1edaSArun Thomas 			}
960*2e8d1edaSArun Thomas 			if ((*dest = (char)dch))
961*2e8d1edaSArun Thomas 				dest++;
962*2e8d1edaSArun Thomas 		}
963*2e8d1edaSArun Thomas 	/*
964*2e8d1edaSArun Thomas 	 * restore all the changed characters
965*2e8d1edaSArun Thomas 	 */
966*2e8d1edaSArun Thomas 		while (*tmp) {
967*2e8d1edaSArun Thomas 			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
968*2e8d1edaSArun Thomas 			tmp++;
969*2e8d1edaSArun Thomas 		}
970*2e8d1edaSArun Thomas 	}
971*2e8d1edaSArun Thomas 	*dest = '\0';
972*2e8d1edaSArun Thomas }
973*2e8d1edaSArun Thomas 
974*2e8d1edaSArun Thomas 
975*2e8d1edaSArun Thomas /*
976*2e8d1edaSArun Thomas  * handledash:
977*2e8d1edaSArun Thomas  *  use buffer to copy the src string, expanding character ranges
978*2e8d1edaSArun Thomas  * on the way.
979*2e8d1edaSArun Thomas  */
980*2e8d1edaSArun Thomas static const char *
981*2e8d1edaSArun Thomas handledash(char *buffer, char *end, const char *src)
982*2e8d1edaSArun Thomas {
983*2e8d1edaSArun Thomas 	char *p;
984*2e8d1edaSArun Thomas 
985*2e8d1edaSArun Thomas 	p = buffer;
986*2e8d1edaSArun Thomas 	while(*src) {
987*2e8d1edaSArun Thomas 		if (src[1] == '-' && src[2]) {
988*2e8d1edaSArun Thomas 			unsigned char i;
989*2e8d1edaSArun Thomas 			if ((unsigned char)src[0] <= (unsigned char)src[2]) {
990*2e8d1edaSArun Thomas 				for (i = (unsigned char)src[0];
991*2e8d1edaSArun Thomas 				    i <= (unsigned char)src[2]; i++) {
992*2e8d1edaSArun Thomas 					*p++ = i;
993*2e8d1edaSArun Thomas 					if (p == end) {
994*2e8d1edaSArun Thomas 						*p = '\0';
995*2e8d1edaSArun Thomas 						return buffer;
996*2e8d1edaSArun Thomas 					}
997*2e8d1edaSArun Thomas 				}
998*2e8d1edaSArun Thomas 			} else {
999*2e8d1edaSArun Thomas 				for (i = (unsigned char)src[0];
1000*2e8d1edaSArun Thomas 				    i >= (unsigned char)src[2]; i--) {
1001*2e8d1edaSArun Thomas 					*p++ = i;
1002*2e8d1edaSArun Thomas 					if (p == end) {
1003*2e8d1edaSArun Thomas 						*p = '\0';
1004*2e8d1edaSArun Thomas 						return buffer;
1005*2e8d1edaSArun Thomas 					}
1006*2e8d1edaSArun Thomas 				}
1007*2e8d1edaSArun Thomas 			}
1008*2e8d1edaSArun Thomas 			src += 3;
1009*2e8d1edaSArun Thomas 		} else
1010*2e8d1edaSArun Thomas 			*p++ = *src++;
1011*2e8d1edaSArun Thomas 		if (p == end)
1012*2e8d1edaSArun Thomas 			break;
1013*2e8d1edaSArun Thomas 	}
1014*2e8d1edaSArun Thomas 	*p = '\0';
1015*2e8d1edaSArun Thomas 	return buffer;
1016*2e8d1edaSArun Thomas }
1017