xref: /openbsd/usr.bin/m4/eval.c (revision 5ddad5cc)
1*5ddad5ccSespie /*	$OpenBSD: eval.c,v 1.48 2003/06/18 21:08:07 espie Exp $	*/
2aa676ce1Smillert /*	$NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1989, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * This code is derived from software contributed to Berkeley by
9df930be7Sderaadt  * Ozan Yigit at York University.
10df930be7Sderaadt  *
11df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
12df930be7Sderaadt  * modification, are permitted provided that the following conditions
13df930be7Sderaadt  * are met:
14df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
15df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
16df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
17df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
18df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
19f75387cbSmillert  * 3. Neither the name of the University nor the names of its contributors
20df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
21df930be7Sderaadt  *    without specific prior written permission.
22df930be7Sderaadt  *
23df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33df930be7Sderaadt  * SUCH DAMAGE.
34df930be7Sderaadt  */
35df930be7Sderaadt 
36df930be7Sderaadt #ifndef lint
37df930be7Sderaadt #if 0
38df930be7Sderaadt static char sccsid[] = "@(#)eval.c	8.2 (Berkeley) 4/27/95";
39df930be7Sderaadt #else
40*5ddad5ccSespie static char rcsid[] = "$OpenBSD: eval.c,v 1.48 2003/06/18 21:08:07 espie Exp $";
41df930be7Sderaadt #endif
42df930be7Sderaadt #endif /* not lint */
43df930be7Sderaadt 
44df930be7Sderaadt /*
45df930be7Sderaadt  * eval.c
46df930be7Sderaadt  * Facility: m4 macro processor
47df930be7Sderaadt  * by: oz
48df930be7Sderaadt  */
49df930be7Sderaadt 
50df930be7Sderaadt #include <sys/types.h>
51df930be7Sderaadt #include <errno.h>
52df930be7Sderaadt #include <unistd.h>
53df930be7Sderaadt #include <stdio.h>
54df930be7Sderaadt #include <stdlib.h>
553f42598dSespie #include <stddef.h>
56df930be7Sderaadt #include <string.h>
57445b77f7Smillert #include <fcntl.h>
5881c2181eSespie #include <err.h>
59df930be7Sderaadt #include "mdef.h"
60df930be7Sderaadt #include "stdd.h"
61df930be7Sderaadt #include "extern.h"
62df930be7Sderaadt #include "pathnames.h"
63df930be7Sderaadt 
64f8b42d48Sespie #define BUILTIN_MARKER	"__builtin_"
65f8b42d48Sespie 
66*5ddad5ccSespie static void 	setup_definition(ndptr, const char *);
67c72b5b24Smillert static void	dodefn(const char *);
68c72b5b24Smillert static void	dopushdef(const char *, const char *);
69c72b5b24Smillert static void	dodump(const char *[], int);
70c72b5b24Smillert static void	dotrace(const char *[], int, int);
71c72b5b24Smillert static void	doifelse(const char *[], int);
72c72b5b24Smillert static int	doincl(const char *);
73c72b5b24Smillert static int	dopaste(const char *);
74c72b5b24Smillert static void	gnu_dochq(const char *[], int);
75c72b5b24Smillert static void	dochq(const char *[], int);
76c72b5b24Smillert static void	gnu_dochc(const char *[], int);
77c72b5b24Smillert static void	dochc(const char *[], int);
78c72b5b24Smillert static void	dodiv(int);
79c72b5b24Smillert static void	doundiv(const char *[], int);
80c72b5b24Smillert static void	dosub(const char *[], int);
81c72b5b24Smillert static void	map(char *, const char *, const char *, const char *);
82c72b5b24Smillert static const char *handledash(char *, char *, const char *);
83c72b5b24Smillert static void	expand_builtin(const char *[], int, int);
84c72b5b24Smillert static void	expand_macro(const char *[], int);
85c72b5b24Smillert static void	dump_one_def(ndptr);
8608f7f207Sespie 
8799f77b33Sespie unsigned long	expansion_id;
8808f7f207Sespie 
89df930be7Sderaadt /*
9008f7f207Sespie  * eval - eval all macros and builtins calls
91054026c0Sespie  *	  argc - number of elements in argv.
92054026c0Sespie  *	  argv - element vector :
93054026c0Sespie  *			argv[0] = definition of a user
94054026c0Sespie  *				  macro or nil if built-in.
95054026c0Sespie  *			argv[1] = name of the macro or
96054026c0Sespie  *				  built-in.
97054026c0Sespie  *			argv[2] = parameters to user-defined
98054026c0Sespie  *			   .	  macro or built-in.
99054026c0Sespie  *			   .
100054026c0Sespie  *
101054026c0Sespie  * A call in the form of macro-or-builtin() will result in:
102054026c0Sespie  *			argv[0] = nullstr
103054026c0Sespie  *			argv[1] = macro-or-builtin
104054026c0Sespie  *			argv[2] = nullstr
105054026c0Sespie  *
106054026c0Sespie  * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
10708f7f207Sespie  */
10808f7f207Sespie void
1098e061d4bSespie eval(const char *argv[], int argc, int td)
11008f7f207Sespie {
1114dac505dSespie 	ssize_t mark = -1;
1124dac505dSespie 
11399f77b33Sespie 	expansion_id++;
11408f7f207Sespie 	if (td & RECDEF)
11508f7f207Sespie 		errx(1, "%s at line %lu: expanding recursive definition for %s",
11608f7f207Sespie 			CURRENT_NAME, CURRENT_LINE, argv[1]);
1174dac505dSespie 	if (traced_macros && is_traced(argv[1]))
1184dac505dSespie 		mark = trace(argv, argc, infile+ilevel);
11908f7f207Sespie 	if (td == MACRTYPE)
12008f7f207Sespie 		expand_macro(argv, argc);
12108f7f207Sespie 	else
12208f7f207Sespie 		expand_builtin(argv, argc, td);
1234dac505dSespie     	if (mark != -1)
1244dac505dSespie 		finish_trace(mark);
12508f7f207Sespie }
12608f7f207Sespie 
12708f7f207Sespie /*
12808f7f207Sespie  * expand_builtin - evaluate built-in macros.
129df930be7Sderaadt  */
130df930be7Sderaadt void
1318e061d4bSespie expand_builtin(const char *argv[], int argc, int td)
132df930be7Sderaadt {
13353f6f6bfSespie 	int c, n;
134054026c0Sespie 	int ac;
135df930be7Sderaadt 	static int sysval = 0;
136df930be7Sderaadt 
137df930be7Sderaadt #ifdef DEBUG
138df930be7Sderaadt 	printf("argc = %d\n", argc);
139df930be7Sderaadt 	for (n = 0; n < argc; n++)
140df930be7Sderaadt 		printf("argv[%d] = %s\n", n, argv[n]);
14128728804Sespie 	fflush(stdout);
142df930be7Sderaadt #endif
143718b194dSespie 
144df930be7Sderaadt  /*
145df930be7Sderaadt   * if argc == 3 and argv[2] is null, then we
146df930be7Sderaadt   * have macro-or-builtin() type call. We adjust
147df930be7Sderaadt   * argc to avoid further checking..
148df930be7Sderaadt   */
149054026c0Sespie   	ac = argc;
150054026c0Sespie 
151df930be7Sderaadt 	if (argc == 3 && !*(argv[2]))
152df930be7Sderaadt 		argc--;
153df930be7Sderaadt 
154718b194dSespie 	switch (td & TYPEMASK) {
155df930be7Sderaadt 
156df930be7Sderaadt 	case DEFITYPE:
157df930be7Sderaadt 		if (argc > 2)
158df930be7Sderaadt 			dodefine(argv[2], (argc > 3) ? argv[3] : null);
159df930be7Sderaadt 		break;
160df930be7Sderaadt 
161df930be7Sderaadt 	case PUSDTYPE:
162df930be7Sderaadt 		if (argc > 2)
163df930be7Sderaadt 			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
164df930be7Sderaadt 		break;
165df930be7Sderaadt 
166df930be7Sderaadt 	case DUMPTYPE:
167df930be7Sderaadt 		dodump(argv, argc);
168df930be7Sderaadt 		break;
169df930be7Sderaadt 
17034970243Sespie 	case TRACEONTYPE:
17134970243Sespie 		dotrace(argv, argc, 1);
17234970243Sespie 		break;
17334970243Sespie 
17434970243Sespie 	case TRACEOFFTYPE:
17534970243Sespie 		dotrace(argv, argc, 0);
17634970243Sespie 		break;
17734970243Sespie 
178df930be7Sderaadt 	case EXPRTYPE:
179df930be7Sderaadt 	/*
180df930be7Sderaadt 	 * doexpr - evaluate arithmetic
181df930be7Sderaadt 	 * expression
182df930be7Sderaadt 	 */
183df930be7Sderaadt 		if (argc > 2)
184df930be7Sderaadt 			pbnum(expr(argv[2]));
185df930be7Sderaadt 		break;
186df930be7Sderaadt 
187df930be7Sderaadt 	case IFELTYPE:
188df930be7Sderaadt 		if (argc > 4)
189df930be7Sderaadt 			doifelse(argv, argc);
190df930be7Sderaadt 		break;
191df930be7Sderaadt 
192df930be7Sderaadt 	case IFDFTYPE:
193df930be7Sderaadt 	/*
194df930be7Sderaadt 	 * doifdef - select one of two
195df930be7Sderaadt 	 * alternatives based on the existence of
196df930be7Sderaadt 	 * another definition
197df930be7Sderaadt 	 */
198df930be7Sderaadt 		if (argc > 3) {
199df930be7Sderaadt 			if (lookup(argv[2]) != nil)
200df930be7Sderaadt 				pbstr(argv[3]);
201df930be7Sderaadt 			else if (argc > 4)
202df930be7Sderaadt 				pbstr(argv[4]);
203df930be7Sderaadt 		}
204df930be7Sderaadt 		break;
205df930be7Sderaadt 
206df930be7Sderaadt 	case LENGTYPE:
207df930be7Sderaadt 	/*
208df930be7Sderaadt 	 * dolen - find the length of the
209df930be7Sderaadt 	 * argument
210df930be7Sderaadt 	 */
211df930be7Sderaadt 		pbnum((argc > 2) ? strlen(argv[2]) : 0);
212df930be7Sderaadt 		break;
213df930be7Sderaadt 
214df930be7Sderaadt 	case INCRTYPE:
215df930be7Sderaadt 	/*
216df930be7Sderaadt 	 * doincr - increment the value of the
217df930be7Sderaadt 	 * argument
218df930be7Sderaadt 	 */
219df930be7Sderaadt 		if (argc > 2)
220df930be7Sderaadt 			pbnum(atoi(argv[2]) + 1);
221df930be7Sderaadt 		break;
222df930be7Sderaadt 
223df930be7Sderaadt 	case DECRTYPE:
224df930be7Sderaadt 	/*
225df930be7Sderaadt 	 * dodecr - decrement the value of the
226df930be7Sderaadt 	 * argument
227df930be7Sderaadt 	 */
228df930be7Sderaadt 		if (argc > 2)
229df930be7Sderaadt 			pbnum(atoi(argv[2]) - 1);
230df930be7Sderaadt 		break;
231df930be7Sderaadt 
232df930be7Sderaadt 	case SYSCTYPE:
233df930be7Sderaadt 	/*
234df930be7Sderaadt 	 * dosys - execute system command
235df930be7Sderaadt 	 */
236df930be7Sderaadt 		if (argc > 2)
237df930be7Sderaadt 			sysval = system(argv[2]);
238df930be7Sderaadt 		break;
239df930be7Sderaadt 
240df930be7Sderaadt 	case SYSVTYPE:
241df930be7Sderaadt 	/*
242df930be7Sderaadt 	 * dosysval - return value of the last
243df930be7Sderaadt 	 * system call.
244df930be7Sderaadt 	 *
245df930be7Sderaadt 	 */
246df930be7Sderaadt 		pbnum(sysval);
247df930be7Sderaadt 		break;
248df930be7Sderaadt 
249c91edbbbSespie 	case ESYSCMDTYPE:
250c91edbbbSespie 		if (argc > 2)
251c91edbbbSespie 			doesyscmd(argv[2]);
252c91edbbbSespie 	    	break;
253df930be7Sderaadt 	case INCLTYPE:
254df930be7Sderaadt 		if (argc > 2)
255df930be7Sderaadt 			if (!doincl(argv[2]))
2560d3ffe1dSespie 				err(1, "%s at line %lu: include(%s)",
2570d3ffe1dSespie 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
258df930be7Sderaadt 		break;
259df930be7Sderaadt 
260df930be7Sderaadt 	case SINCTYPE:
261df930be7Sderaadt 		if (argc > 2)
262df930be7Sderaadt 			(void) doincl(argv[2]);
263df930be7Sderaadt 		break;
264df930be7Sderaadt #ifdef EXTENDED
265df930be7Sderaadt 	case PASTTYPE:
266df930be7Sderaadt 		if (argc > 2)
267df930be7Sderaadt 			if (!dopaste(argv[2]))
2680d3ffe1dSespie 				err(1, "%s at line %lu: paste(%s)",
2690d3ffe1dSespie 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
270df930be7Sderaadt 		break;
271df930be7Sderaadt 
272df930be7Sderaadt 	case SPASTYPE:
273df930be7Sderaadt 		if (argc > 2)
274df930be7Sderaadt 			(void) dopaste(argv[2]);
275df930be7Sderaadt 		break;
276df930be7Sderaadt #endif
277df930be7Sderaadt 	case CHNQTYPE:
278054026c0Sespie 		if (mimic_gnu)
279054026c0Sespie 			gnu_dochq(argv, ac);
280054026c0Sespie 		else
281df930be7Sderaadt 			dochq(argv, argc);
282df930be7Sderaadt 		break;
283df930be7Sderaadt 
284df930be7Sderaadt 	case CHNCTYPE:
285054026c0Sespie 		if (mimic_gnu)
286054026c0Sespie 			gnu_dochc(argv, ac);
287054026c0Sespie 		else
288df930be7Sderaadt 			dochc(argv, argc);
289df930be7Sderaadt 		break;
290df930be7Sderaadt 
291df930be7Sderaadt 	case SUBSTYPE:
292df930be7Sderaadt 	/*
293df930be7Sderaadt 	 * dosub - select substring
294df930be7Sderaadt 	 *
295df930be7Sderaadt 	 */
296df930be7Sderaadt 		if (argc > 3)
297df930be7Sderaadt 			dosub(argv, argc);
298df930be7Sderaadt 		break;
299df930be7Sderaadt 
300df930be7Sderaadt 	case SHIFTYPE:
301df930be7Sderaadt 	/*
302df930be7Sderaadt 	 * doshift - push back all arguments
303df930be7Sderaadt 	 * except the first one (i.e. skip
304df930be7Sderaadt 	 * argv[2])
305df930be7Sderaadt 	 */
306df930be7Sderaadt 		if (argc > 3) {
307df930be7Sderaadt 			for (n = argc - 1; n > 3; n--) {
3083a73db8cSderaadt 				pbstr(rquote);
309df930be7Sderaadt 				pbstr(argv[n]);
3103a73db8cSderaadt 				pbstr(lquote);
311aa676ce1Smillert 				putback(COMMA);
312df930be7Sderaadt 			}
3133a73db8cSderaadt 			pbstr(rquote);
314df930be7Sderaadt 			pbstr(argv[3]);
3153a73db8cSderaadt 			pbstr(lquote);
316df930be7Sderaadt 		}
317df930be7Sderaadt 		break;
318df930be7Sderaadt 
319df930be7Sderaadt 	case DIVRTYPE:
320df930be7Sderaadt 		if (argc > 2 && (n = atoi(argv[2])) != 0)
321df930be7Sderaadt 			dodiv(n);
322df930be7Sderaadt 		else {
323df930be7Sderaadt 			active = stdout;
324df930be7Sderaadt 			oindex = 0;
325df930be7Sderaadt 		}
326df930be7Sderaadt 		break;
327df930be7Sderaadt 
328df930be7Sderaadt 	case UNDVTYPE:
329df930be7Sderaadt 		doundiv(argv, argc);
330df930be7Sderaadt 		break;
331df930be7Sderaadt 
332df930be7Sderaadt 	case DIVNTYPE:
333df930be7Sderaadt 	/*
334df930be7Sderaadt 	 * dodivnum - return the number of
335df930be7Sderaadt 	 * current output diversion
336df930be7Sderaadt 	 */
337df930be7Sderaadt 		pbnum(oindex);
338df930be7Sderaadt 		break;
339df930be7Sderaadt 
340df930be7Sderaadt 	case UNDFTYPE:
341df930be7Sderaadt 	/*
342df930be7Sderaadt 	 * doundefine - undefine a previously
343df930be7Sderaadt 	 * defined macro(s) or m4 keyword(s).
344df930be7Sderaadt 	 */
345df930be7Sderaadt 		if (argc > 2)
346df930be7Sderaadt 			for (n = 2; n < argc; n++)
347df930be7Sderaadt 				remhash(argv[n], ALL);
348df930be7Sderaadt 		break;
349df930be7Sderaadt 
350df930be7Sderaadt 	case POPDTYPE:
351df930be7Sderaadt 	/*
352df930be7Sderaadt 	 * dopopdef - remove the topmost
353df930be7Sderaadt 	 * definitions of macro(s) or m4
354df930be7Sderaadt 	 * keyword(s).
355df930be7Sderaadt 	 */
356df930be7Sderaadt 		if (argc > 2)
357df930be7Sderaadt 			for (n = 2; n < argc; n++)
358df930be7Sderaadt 				remhash(argv[n], TOP);
359df930be7Sderaadt 		break;
360df930be7Sderaadt 
361df930be7Sderaadt 	case MKTMTYPE:
362df930be7Sderaadt 	/*
363df930be7Sderaadt 	 * dotemp - create a temporary file
364df930be7Sderaadt 	 */
36501e71e69Sespie 		if (argc > 2) {
36601e71e69Sespie 			int fd;
367bb34cd6cSespie 			char *temp;
36801e71e69Sespie 
369bb34cd6cSespie 			temp = xstrdup(argv[2]);
370bb34cd6cSespie 
371bb34cd6cSespie 			fd = mkstemp(temp);
37201e71e69Sespie 			if (fd == -1)
3730d3ffe1dSespie 				err(1,
3740d3ffe1dSespie 	    "%s at line %lu: couldn't make temp file %s",
3750d3ffe1dSespie 	    CURRENT_NAME, CURRENT_LINE, argv[2]);
37601e71e69Sespie 			close(fd);
377bb34cd6cSespie 			pbstr(temp);
378bb34cd6cSespie 			free(temp);
37901e71e69Sespie 		}
380df930be7Sderaadt 		break;
381df930be7Sderaadt 
382df930be7Sderaadt 	case TRNLTYPE:
383df930be7Sderaadt 	/*
384df930be7Sderaadt 	 * dotranslit - replace all characters in
385df930be7Sderaadt 	 * the source string that appears in the
386df930be7Sderaadt 	 * "from" string with the corresponding
387df930be7Sderaadt 	 * characters in the "to" string.
388df930be7Sderaadt 	 */
389df930be7Sderaadt 		if (argc > 3) {
39028728804Sespie 			char *temp;
39128728804Sespie 
39228728804Sespie 			temp = xalloc(strlen(argv[2])+1);
393df930be7Sderaadt 			if (argc > 4)
394df930be7Sderaadt 				map(temp, argv[2], argv[3], argv[4]);
395df930be7Sderaadt 			else
396df930be7Sderaadt 				map(temp, argv[2], argv[3], null);
397df930be7Sderaadt 			pbstr(temp);
39828728804Sespie 			free(temp);
3997d3e0b6bSderaadt 		} else if (argc > 2)
400df930be7Sderaadt 			pbstr(argv[2]);
401df930be7Sderaadt 		break;
402df930be7Sderaadt 
403df930be7Sderaadt 	case INDXTYPE:
404df930be7Sderaadt 	/*
405df930be7Sderaadt 	 * doindex - find the index of the second
406df930be7Sderaadt 	 * argument string in the first argument
407df930be7Sderaadt 	 * string. -1 if not present.
408df930be7Sderaadt 	 */
409df930be7Sderaadt 		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
410df930be7Sderaadt 		break;
411df930be7Sderaadt 
412df930be7Sderaadt 	case ERRPTYPE:
413df930be7Sderaadt 	/*
414df930be7Sderaadt 	 * doerrp - print the arguments to stderr
415df930be7Sderaadt 	 * file
416df930be7Sderaadt 	 */
417df930be7Sderaadt 		if (argc > 2) {
418df930be7Sderaadt 			for (n = 2; n < argc; n++)
419df930be7Sderaadt 				fprintf(stderr, "%s ", argv[n]);
420df930be7Sderaadt 			fprintf(stderr, "\n");
421df930be7Sderaadt 		}
422df930be7Sderaadt 		break;
423df930be7Sderaadt 
424df930be7Sderaadt 	case DNLNTYPE:
425df930be7Sderaadt 	/*
426df930be7Sderaadt 	 * dodnl - eat-up-to and including
427df930be7Sderaadt 	 * newline
428df930be7Sderaadt 	 */
429df930be7Sderaadt 		while ((c = gpbc()) != '\n' && c != EOF)
430df930be7Sderaadt 			;
431df930be7Sderaadt 		break;
432df930be7Sderaadt 
433df930be7Sderaadt 	case M4WRTYPE:
434df930be7Sderaadt 	/*
435df930be7Sderaadt 	 * dom4wrap - set up for
436df930be7Sderaadt 	 * wrap-up/wind-down activity
437df930be7Sderaadt 	 */
438df930be7Sderaadt 		m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
439df930be7Sderaadt 		break;
440df930be7Sderaadt 
441df930be7Sderaadt 	case EXITTYPE:
442df930be7Sderaadt 	/*
443df930be7Sderaadt 	 * doexit - immediate exit from m4.
444df930be7Sderaadt 	 */
445df930be7Sderaadt 		killdiv();
446df930be7Sderaadt 		exit((argc > 2) ? atoi(argv[2]) : 0);
447df930be7Sderaadt 		break;
448df930be7Sderaadt 
449df930be7Sderaadt 	case DEFNTYPE:
450df930be7Sderaadt 		if (argc > 2)
451df930be7Sderaadt 			for (n = 2; n < argc; n++)
452df930be7Sderaadt 				dodefn(argv[n]);
453df930be7Sderaadt 		break;
454df930be7Sderaadt 
455b8161682Sespie 	case INDIRTYPE:	/* Indirect call */
456b8161682Sespie 		if (argc > 2)
457b8161682Sespie 			doindir(argv, argc);
458b8161682Sespie 		break;
459b8161682Sespie 
460b8161682Sespie 	case BUILTINTYPE: /* Builtins only */
461b8161682Sespie 		if (argc > 2)
462b8161682Sespie 			dobuiltin(argv, argc);
463b8161682Sespie 		break;
464b8161682Sespie 
465b8161682Sespie 	case PATSTYPE:
466b8161682Sespie 		if (argc > 2)
467b8161682Sespie 			dopatsubst(argv, argc);
468b8161682Sespie 		break;
469b8161682Sespie 	case REGEXPTYPE:
470b8161682Sespie 		if (argc > 2)
471b8161682Sespie 			doregexp(argv, argc);
472b8161682Sespie 		break;
473b8161682Sespie 	case LINETYPE:
474b8161682Sespie 		doprintlineno(infile+ilevel);
475b8161682Sespie 		break;
476b8161682Sespie 	case FILENAMETYPE:
477b8161682Sespie 		doprintfilename(infile+ilevel);
478b8161682Sespie 		break;
479423624b7Sespie 	case SELFTYPE:
480423624b7Sespie 		pbstr(rquote);
481423624b7Sespie 		pbstr(argv[1]);
482423624b7Sespie 		pbstr(lquote);
483423624b7Sespie 		break;
484df930be7Sderaadt 	default:
4850d3ffe1dSespie 		errx(1, "%s at line %lu: eval: major botch.",
4860d3ffe1dSespie 			CURRENT_NAME, CURRENT_LINE);
487df930be7Sderaadt 		break;
488df930be7Sderaadt 	}
489df930be7Sderaadt }
490df930be7Sderaadt 
491df930be7Sderaadt /*
49208f7f207Sespie  * expand_macro - user-defined macro expansion
493df930be7Sderaadt  */
494df930be7Sderaadt void
4958e061d4bSespie expand_macro(const char *argv[], int argc)
496df930be7Sderaadt {
497bb34cd6cSespie 	const char *t;
498bb34cd6cSespie 	const char *p;
49953f6f6bfSespie 	int n;
50053f6f6bfSespie 	int argno;
501df930be7Sderaadt 
502df930be7Sderaadt 	t = argv[0];		       /* defn string as a whole */
503df930be7Sderaadt 	p = t;
504df930be7Sderaadt 	while (*p)
505df930be7Sderaadt 		p++;
506df930be7Sderaadt 	p--;			       /* last character of defn */
507df930be7Sderaadt 	while (p > t) {
508df930be7Sderaadt 		if (*(p - 1) != ARGFLAG)
509dd84b4a6Sespie 			PUTBACK(*p);
510df930be7Sderaadt 		else {
511df930be7Sderaadt 			switch (*p) {
512df930be7Sderaadt 
513df930be7Sderaadt 			case '#':
514df930be7Sderaadt 				pbnum(argc - 2);
515df930be7Sderaadt 				break;
516df930be7Sderaadt 			case '0':
517df930be7Sderaadt 			case '1':
518df930be7Sderaadt 			case '2':
519df930be7Sderaadt 			case '3':
520df930be7Sderaadt 			case '4':
521df930be7Sderaadt 			case '5':
522df930be7Sderaadt 			case '6':
523df930be7Sderaadt 			case '7':
524df930be7Sderaadt 			case '8':
525df930be7Sderaadt 			case '9':
526df930be7Sderaadt 				if ((argno = *p - '0') < argc - 1)
527df930be7Sderaadt 					pbstr(argv[argno + 1]);
528df930be7Sderaadt 				break;
529df930be7Sderaadt 			case '*':
530faa30e49Sespie 				if (argc > 2) {
531df930be7Sderaadt 					for (n = argc - 1; n > 2; n--) {
532df930be7Sderaadt 						pbstr(argv[n]);
533aa676ce1Smillert 						putback(COMMA);
534df930be7Sderaadt 					}
535df930be7Sderaadt 					pbstr(argv[2]);
536faa30e49Sespie 			    	}
537df930be7Sderaadt 				break;
538aa676ce1Smillert                         case '@':
539faa30e49Sespie 				if (argc > 2) {
540aa676ce1Smillert 					for (n = argc - 1; n > 2; n--) {
541aa676ce1Smillert 						pbstr(rquote);
542aa676ce1Smillert 						pbstr(argv[n]);
543aa676ce1Smillert 						pbstr(lquote);
544aa676ce1Smillert 						putback(COMMA);
545aa676ce1Smillert 					}
546aa676ce1Smillert 					pbstr(rquote);
547aa676ce1Smillert 					pbstr(argv[2]);
548aa676ce1Smillert 					pbstr(lquote);
549faa30e49Sespie 				}
550aa676ce1Smillert                                 break;
551df930be7Sderaadt 			default:
552dd84b4a6Sespie 				PUTBACK(*p);
553dd84b4a6Sespie 				PUTBACK('$');
554df930be7Sderaadt 				break;
555df930be7Sderaadt 			}
556df930be7Sderaadt 			p--;
557df930be7Sderaadt 		}
558df930be7Sderaadt 		p--;
559df930be7Sderaadt 	}
560df930be7Sderaadt 	if (p == t)		       /* do last character */
561dd84b4a6Sespie 		PUTBACK(*p);
562df930be7Sderaadt }
563df930be7Sderaadt 
564*5ddad5ccSespie 
565df930be7Sderaadt /*
566*5ddad5ccSespie  * common part to dodefine and dopushdef
567df930be7Sderaadt  */
568*5ddad5ccSespie static void
569*5ddad5ccSespie setup_definition(ndptr p, const char *defn)
570df930be7Sderaadt {
571f8b42d48Sespie 	int n;
572df930be7Sderaadt 
573f8b42d48Sespie 	if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0) {
574f8b42d48Sespie 		n = builtin_type(defn+sizeof(BUILTIN_MARKER)-1);
575f8b42d48Sespie 		if (n != -1) {
57655d92b07Sespie 			p->type = n & TYPEMASK;
57755d92b07Sespie 			if ((n & NOARGS) == 0)
57855d92b07Sespie 				p->type |= NEEDARGS;
579*5ddad5ccSespie 			p->defn = xstrdup(defn+sizeof(BUILTIN_MARKER)-1);
580f8b42d48Sespie 			return;
581f8b42d48Sespie 		}
582f8b42d48Sespie 	}
583df930be7Sderaadt 	if (!*defn)
584df930be7Sderaadt 		p->defn = null;
585df930be7Sderaadt 	else
586df930be7Sderaadt 		p->defn = xstrdup(defn);
587df930be7Sderaadt 	p->type = MACRTYPE;
588*5ddad5ccSespie }
589*5ddad5ccSespie 
590*5ddad5ccSespie /*
591*5ddad5ccSespie  * dodefine - install definition in the table
592*5ddad5ccSespie  */
593*5ddad5ccSespie void
594*5ddad5ccSespie dodefine(const char *name, const char *defn)
595*5ddad5ccSespie {
596*5ddad5ccSespie 	ndptr p;
597*5ddad5ccSespie 
598*5ddad5ccSespie 	if (!*name)
599*5ddad5ccSespie 		errx(1, "%s at line %lu: null definition.", CURRENT_NAME,
600*5ddad5ccSespie 		    CURRENT_LINE);
601*5ddad5ccSespie 	if ((p = lookup(name)) == nil)
602*5ddad5ccSespie 		p = addent(name);
603*5ddad5ccSespie 	else if (p->defn != null)
604*5ddad5ccSespie 		free((char *) p->defn);
605*5ddad5ccSespie 	setup_definition(p, defn);
606718b194dSespie 	if (STREQ(name, defn))
607718b194dSespie 		p->type |= RECDEF;
608df930be7Sderaadt }
609df930be7Sderaadt 
610df930be7Sderaadt /*
611df930be7Sderaadt  * dodefn - push back a quoted definition of
612df930be7Sderaadt  *      the given name.
613df930be7Sderaadt  */
614bb34cd6cSespie static void
6158e061d4bSespie dodefn(const char *name)
616df930be7Sderaadt {
61753f6f6bfSespie 	ndptr p;
618f8b42d48Sespie 	char *real;
619df930be7Sderaadt 
620f8b42d48Sespie 	if ((p = lookup(name)) != nil) {
621*5ddad5ccSespie 		if ((p->type & TYPEMASK) == MACRTYPE) {
6223a73db8cSderaadt 			pbstr(rquote);
623df930be7Sderaadt 			pbstr(p->defn);
6243a73db8cSderaadt 			pbstr(lquote);
625*5ddad5ccSespie 		} else {
626*5ddad5ccSespie 			pbstr(p->defn);
627f8b42d48Sespie 			pbstr(BUILTIN_MARKER);
628f8b42d48Sespie 		}
629df930be7Sderaadt 	}
630df930be7Sderaadt }
631df930be7Sderaadt 
632df930be7Sderaadt /*
633df930be7Sderaadt  * dopushdef - install a definition in the hash table
634df930be7Sderaadt  *      without removing a previous definition. Since
635df930be7Sderaadt  *      each new entry is entered in *front* of the
636df930be7Sderaadt  *      hash bucket, it hides a previous definition from
637df930be7Sderaadt  *      lookup.
638df930be7Sderaadt  */
639bb34cd6cSespie static void
6408e061d4bSespie dopushdef(const char *name, const char *defn)
641df930be7Sderaadt {
64253f6f6bfSespie 	ndptr p;
643df930be7Sderaadt 
644df930be7Sderaadt 	if (!*name)
6450d3ffe1dSespie 		errx(1, "%s at line %lu: null definition", CURRENT_NAME,
6460d3ffe1dSespie 		    CURRENT_LINE);
647df930be7Sderaadt 	p = addent(name);
648*5ddad5ccSespie 	setup_definition(p, defn);
649718b194dSespie 	if (STREQ(name, defn))
650718b194dSespie 		p->type |= RECDEF;
651df930be7Sderaadt }
652df930be7Sderaadt 
653df930be7Sderaadt /*
654dd0682a2Sespie  * dump_one_def - dump the specified definition.
655dd0682a2Sespie  */
656dd0682a2Sespie static void
6578e061d4bSespie dump_one_def(ndptr p)
658dd0682a2Sespie {
6595191fa0aSespie 	if (mimic_gnu) {
6605191fa0aSespie 		if ((p->type & TYPEMASK) == MACRTYPE)
6614dac505dSespie 			fprintf(traceout, "%s:\t%s\n", p->name, p->defn);
6625191fa0aSespie 		else {
663*5ddad5ccSespie 			fprintf(traceout, "%s:\t<%s>\n", p->name, p->defn);
6645191fa0aSespie 	    	}
6655191fa0aSespie 	} else
6664dac505dSespie 		fprintf(traceout, "`%s'\t`%s'\n", p->name, p->defn);
667dd0682a2Sespie }
668dd0682a2Sespie 
669dd0682a2Sespie /*
670df930be7Sderaadt  * dodumpdef - dump the specified definitions in the hash
671df930be7Sderaadt  *      table to stderr. If nothing is specified, the entire
672df930be7Sderaadt  *      hash table is dumped.
673df930be7Sderaadt  */
674bb34cd6cSespie static void
6758e061d4bSespie dodump(const char *argv[], int argc)
676df930be7Sderaadt {
67753f6f6bfSespie 	int n;
678df930be7Sderaadt 	ndptr p;
679df930be7Sderaadt 
680df930be7Sderaadt 	if (argc > 2) {
681df930be7Sderaadt 		for (n = 2; n < argc; n++)
682df930be7Sderaadt 			if ((p = lookup(argv[n])) != nil)
683dd0682a2Sespie 				dump_one_def(p);
6847d3e0b6bSderaadt 	} else {
685df930be7Sderaadt 		for (n = 0; n < HASHSIZE; n++)
686df930be7Sderaadt 			for (p = hashtab[n]; p != nil; p = p->nxtptr)
687dd0682a2Sespie 				dump_one_def(p);
688df930be7Sderaadt 	}
689df930be7Sderaadt }
690df930be7Sderaadt 
691df930be7Sderaadt /*
69234970243Sespie  * dotrace - mark some macros as traced/untraced depending upon on.
69334970243Sespie  */
69434970243Sespie static void
6958e061d4bSespie dotrace(const char *argv[], int argc, int on)
69634970243Sespie {
69734970243Sespie 	int n;
69834970243Sespie 
69934970243Sespie 	if (argc > 2) {
70034970243Sespie 		for (n = 2; n < argc; n++)
70134970243Sespie 			mark_traced(argv[n], on);
70234970243Sespie 	} else
70334970243Sespie 		mark_traced(NULL, on);
70434970243Sespie }
70534970243Sespie 
70634970243Sespie /*
707df930be7Sderaadt  * doifelse - select one of two alternatives - loop.
708df930be7Sderaadt  */
709bb34cd6cSespie static void
7108e061d4bSespie doifelse(const char *argv[], int argc)
711df930be7Sderaadt {
712df930be7Sderaadt 	cycle {
713df930be7Sderaadt 		if (STREQ(argv[2], argv[3]))
714df930be7Sderaadt 			pbstr(argv[4]);
715df930be7Sderaadt 		else if (argc == 6)
716df930be7Sderaadt 			pbstr(argv[5]);
717df930be7Sderaadt 		else if (argc > 6) {
718df930be7Sderaadt 			argv += 3;
719df930be7Sderaadt 			argc -= 3;
720df930be7Sderaadt 			continue;
721df930be7Sderaadt 		}
722df930be7Sderaadt 		break;
723df930be7Sderaadt 	}
724df930be7Sderaadt }
725df930be7Sderaadt 
726df930be7Sderaadt /*
727df930be7Sderaadt  * doinclude - include a given file.
728df930be7Sderaadt  */
729bb34cd6cSespie static int
7308e061d4bSespie doincl(const char *ifile)
731df930be7Sderaadt {
732df930be7Sderaadt 	if (ilevel + 1 == MAXINP)
7330d3ffe1dSespie 		errx(1, "%s at line %lu: too many include files.",
7340d3ffe1dSespie 		    CURRENT_NAME, CURRENT_LINE);
7350d3ffe1dSespie 	if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
736df930be7Sderaadt 		ilevel++;
737df930be7Sderaadt 		bbase[ilevel] = bufbase = bp;
738df930be7Sderaadt 		return (1);
7397d3e0b6bSderaadt 	} else
740df930be7Sderaadt 		return (0);
741df930be7Sderaadt }
742df930be7Sderaadt 
743df930be7Sderaadt #ifdef EXTENDED
744df930be7Sderaadt /*
745df930be7Sderaadt  * dopaste - include a given file without any
746df930be7Sderaadt  *           macro processing.
747df930be7Sderaadt  */
748bb34cd6cSespie static int
7498e061d4bSespie dopaste(const char *pfile)
750df930be7Sderaadt {
751df930be7Sderaadt 	FILE *pf;
75253f6f6bfSespie 	int c;
753df930be7Sderaadt 
754df930be7Sderaadt 	if ((pf = fopen(pfile, "r")) != NULL) {
755aec72266Sespie 		if (synch_lines)
756aec72266Sespie 		    fprintf(active, "#line 1 \"%s\"\n", pfile);
757df930be7Sderaadt 		while ((c = getc(pf)) != EOF)
758df930be7Sderaadt 			putc(c, active);
759df930be7Sderaadt 		(void) fclose(pf);
760aec72266Sespie 		emit_synchline();
761df930be7Sderaadt 		return (1);
7627d3e0b6bSderaadt 	} else
763df930be7Sderaadt 		return (0);
764df930be7Sderaadt }
765df930be7Sderaadt #endif
766df930be7Sderaadt 
767054026c0Sespie static void
7688e061d4bSespie gnu_dochq(const char *argv[], int ac)
769054026c0Sespie {
770054026c0Sespie 	/* In gnu-m4 mode, the only way to restore quotes is to have no
771054026c0Sespie 	 * arguments at all. */
772054026c0Sespie 	if (ac == 2) {
773054026c0Sespie 		lquote[0] = LQUOTE, lquote[1] = EOS;
774054026c0Sespie 		rquote[0] = RQUOTE, rquote[1] = EOS;
775054026c0Sespie 	} else {
776054026c0Sespie 		strlcpy(lquote, argv[2], sizeof(lquote));
777054026c0Sespie 		if(ac > 3)
778054026c0Sespie 			strlcpy(rquote, argv[3], sizeof(rquote));
779054026c0Sespie 		else
780054026c0Sespie 			rquote[0] = EOS;
781054026c0Sespie 	}
782054026c0Sespie }
783054026c0Sespie 
784df930be7Sderaadt /*
785df930be7Sderaadt  * dochq - change quote characters
786df930be7Sderaadt  */
787bb34cd6cSespie static void
7888e061d4bSespie dochq(const char *argv[], int argc)
789df930be7Sderaadt {
790df930be7Sderaadt 	if (argc > 2) {
79118a1973bSderaadt 		if (*argv[2])
792b81b15b2Sespie 			strlcpy(lquote, argv[2], sizeof(lquote));
79318a1973bSderaadt 		else {
79418a1973bSderaadt 			lquote[0] = LQUOTE;
795f0484631Sespie 			lquote[1] = EOS;
79618a1973bSderaadt 		}
797df930be7Sderaadt 		if (argc > 3) {
798df930be7Sderaadt 			if (*argv[3])
799b81b15b2Sespie 				strlcpy(rquote, argv[3], sizeof(rquote));
8007d3e0b6bSderaadt 		} else
8013ffdcb07Sespie 			strlcpy(rquote, lquote, sizeof(rquote));
8027d3e0b6bSderaadt 	} else {
803f0484631Sespie 		lquote[0] = LQUOTE, lquote[1] = EOS;
804f0484631Sespie 		rquote[0] = RQUOTE, rquote[1] = EOS;
805df930be7Sderaadt 	}
806df930be7Sderaadt }
807df930be7Sderaadt 
808054026c0Sespie static void
8098e061d4bSespie gnu_dochc(const char *argv[], int ac)
810054026c0Sespie {
811054026c0Sespie 	/* In gnu-m4 mode, no arguments mean no comment
812054026c0Sespie 	 * arguments at all. */
813054026c0Sespie 	if (ac == 2) {
814054026c0Sespie 		scommt[0] = EOS;
815054026c0Sespie 		ecommt[0] = EOS;
816054026c0Sespie 	} else {
817054026c0Sespie 		if (*argv[2])
818054026c0Sespie 			strlcpy(scommt, argv[2], sizeof(scommt));
819054026c0Sespie 		else
820054026c0Sespie 			scommt[0] = SCOMMT, scommt[1] = EOS;
821054026c0Sespie 		if(ac > 3 && *argv[3])
822054026c0Sespie 			strlcpy(ecommt, argv[3], sizeof(ecommt));
823054026c0Sespie 		else
824054026c0Sespie 			ecommt[0] = ECOMMT, ecommt[1] = EOS;
825054026c0Sespie 	}
826054026c0Sespie }
827df930be7Sderaadt /*
828df930be7Sderaadt  * dochc - change comment characters
829df930be7Sderaadt  */
830bb34cd6cSespie static void
8318e061d4bSespie dochc(const char *argv[], int argc)
832df930be7Sderaadt {
833df930be7Sderaadt 	if (argc > 2) {
834df930be7Sderaadt 		if (*argv[2])
835b81b15b2Sespie 			strlcpy(scommt, argv[2], sizeof(scommt));
836df930be7Sderaadt 		if (argc > 3) {
837df930be7Sderaadt 			if (*argv[3])
838b81b15b2Sespie 				strlcpy(ecommt, argv[3], sizeof(ecommt));
839df930be7Sderaadt 		}
840df930be7Sderaadt 		else
841f0484631Sespie 			ecommt[0] = ECOMMT, ecommt[1] = EOS;
842df930be7Sderaadt 	}
843df930be7Sderaadt 	else {
844f0484631Sespie 		scommt[0] = SCOMMT, scommt[1] = EOS;
845f0484631Sespie 		ecommt[0] = ECOMMT, ecommt[1] = EOS;
846df930be7Sderaadt 	}
847df930be7Sderaadt }
848df930be7Sderaadt 
849df930be7Sderaadt /*
850df930be7Sderaadt  * dodivert - divert the output to a temporary file
851df930be7Sderaadt  */
852bb34cd6cSespie static void
8538e061d4bSespie dodiv(int n)
854df930be7Sderaadt {
855445b77f7Smillert 	int fd;
856445b77f7Smillert 
8577d3e0b6bSderaadt 	oindex = n;
85825afcddbSespie 	if (n >= maxout) {
85925afcddbSespie 		if (mimic_gnu)
86025afcddbSespie 			resizedivs(n + 10);
86125afcddbSespie 		else
86225afcddbSespie 			n = 0;		/* bitbucket */
86325afcddbSespie     	}
86425afcddbSespie 
86525afcddbSespie 	if (n < 0)
866df930be7Sderaadt 		n = 0;		       /* bitbucket */
867df930be7Sderaadt 	if (outfile[n] == NULL) {
8683f42598dSespie 		char fname[] = _PATH_DIVNAME;
8693f42598dSespie 
8703f42598dSespie 		if ((fd = mkstemp(fname)) < 0 ||
8713f42598dSespie 			(outfile[n] = fdopen(fd, "w+")) == NULL)
8723f42598dSespie 				err(1, "%s: cannot divert", fname);
8733f42598dSespie 		if (unlink(fname) == -1)
8743f42598dSespie 			err(1, "%s: cannot unlink", fname);
875df930be7Sderaadt 	}
876df930be7Sderaadt 	active = outfile[n];
877df930be7Sderaadt }
878df930be7Sderaadt 
879df930be7Sderaadt /*
880df930be7Sderaadt  * doundivert - undivert a specified output, or all
881df930be7Sderaadt  *              other outputs, in numerical order.
882df930be7Sderaadt  */
883bb34cd6cSespie static void
8848e061d4bSespie doundiv(const char *argv[], int argc)
885df930be7Sderaadt {
88653f6f6bfSespie 	int ind;
88753f6f6bfSespie 	int n;
888df930be7Sderaadt 
889df930be7Sderaadt 	if (argc > 2) {
890df930be7Sderaadt 		for (ind = 2; ind < argc; ind++) {
891df930be7Sderaadt 			n = atoi(argv[ind]);
89225afcddbSespie 			if (n > 0 && n < maxout && outfile[n] != NULL)
893df930be7Sderaadt 				getdiv(n);
894df930be7Sderaadt 
895df930be7Sderaadt 		}
896df930be7Sderaadt 	}
897df930be7Sderaadt 	else
89825afcddbSespie 		for (n = 1; n < maxout; n++)
899df930be7Sderaadt 			if (outfile[n] != NULL)
900df930be7Sderaadt 				getdiv(n);
901df930be7Sderaadt }
902df930be7Sderaadt 
903df930be7Sderaadt /*
904df930be7Sderaadt  * dosub - select substring
905df930be7Sderaadt  */
906bb34cd6cSespie static void
9078e061d4bSespie dosub(const char *argv[], int argc)
908df930be7Sderaadt {
909bb34cd6cSespie 	const char *ap, *fc, *k;
91053f6f6bfSespie 	int nc;
911df930be7Sderaadt 
912df930be7Sderaadt 	ap = argv[2];		       /* target string */
913df930be7Sderaadt #ifdef EXPR
914df930be7Sderaadt 	fc = ap + expr(argv[3]);       /* first char */
915df930be7Sderaadt #else
916df930be7Sderaadt 	fc = ap + atoi(argv[3]);       /* first char */
917df930be7Sderaadt #endif
918bee8364eSespie 	nc = strlen(fc);
919bee8364eSespie 	if (argc >= 5)
920bee8364eSespie #ifdef EXPR
921bee8364eSespie 		nc = min(nc, expr(argv[4]));
922bee8364eSespie #else
923bee8364eSespie 		nc = min(nc, atoi(argv[4]));
924bee8364eSespie #endif
925df930be7Sderaadt 	if (fc >= ap && fc < ap + strlen(ap))
926bee8364eSespie 		for (k = fc + nc - 1; k >= fc; k--)
927df930be7Sderaadt 			putback(*k);
928df930be7Sderaadt }
929df930be7Sderaadt 
930df930be7Sderaadt /*
931df930be7Sderaadt  * map:
932df930be7Sderaadt  * map every character of s1 that is specified in from
933df930be7Sderaadt  * into s3 and replace in s. (source s1 remains untouched)
934df930be7Sderaadt  *
935df930be7Sderaadt  * This is a standard implementation of map(s,from,to) function of ICON
936df930be7Sderaadt  * language. Within mapvec, we replace every character of "from" with
937df930be7Sderaadt  * the corresponding character in "to". If "to" is shorter than "from",
938df930be7Sderaadt  * than the corresponding entries are null, which means that those
939df930be7Sderaadt  * characters dissapear altogether. Furthermore, imagine
940df930be7Sderaadt  * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
941df930be7Sderaadt  * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
942df930be7Sderaadt  * ultimately maps to `*'. In order to achieve this effect in an efficient
943df930be7Sderaadt  * manner (i.e. without multiple passes over the destination string), we
944df930be7Sderaadt  * loop over mapvec, starting with the initial source character. if the
945df930be7Sderaadt  * character value (dch) in this location is different than the source
946df930be7Sderaadt  * character (sch), sch becomes dch, once again to index into mapvec, until
947df930be7Sderaadt  * the character value stabilizes (i.e. sch = dch, in other words
948df930be7Sderaadt  * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
949df930be7Sderaadt  * character, it will stabilize, since mapvec[0] == 0 at all times. At the
950df930be7Sderaadt  * end, we restore mapvec* back to normal where mapvec[n] == n for
951df930be7Sderaadt  * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
952df930be7Sderaadt  * about 5 times faster than any algorithm that makes multiple passes over
953df930be7Sderaadt  * destination string.
954df930be7Sderaadt  */
955bb34cd6cSespie static void
9568e061d4bSespie map(char *dest, const char *src, const char *from, const char *to)
957df930be7Sderaadt {
958bb34cd6cSespie 	const char *tmp;
959ee3599c7Sespie 	unsigned char sch, dch;
96087c5c065Sespie 	static char frombis[257];
96187c5c065Sespie 	static char tobis[257];
962ee3599c7Sespie 	static unsigned char mapvec[256] = {
963ee3599c7Sespie 	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
964ee3599c7Sespie 	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
965ee3599c7Sespie 	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
966ee3599c7Sespie 	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
967ee3599c7Sespie 	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
968ee3599c7Sespie 	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
969ee3599c7Sespie 	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
970ee3599c7Sespie 	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
971ee3599c7Sespie 	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
972ee3599c7Sespie 	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
973ee3599c7Sespie 	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
974ee3599c7Sespie 	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
975ee3599c7Sespie 	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
976ee3599c7Sespie 	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
977ee3599c7Sespie 	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
978ee3599c7Sespie 	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
979ee3599c7Sespie 	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
980ee3599c7Sespie 	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
981df930be7Sderaadt 	};
982df930be7Sderaadt 
983df930be7Sderaadt 	if (*src) {
98487c5c065Sespie 		if (mimic_gnu) {
98587c5c065Sespie 			/*
98687c5c065Sespie 			 * expand character ranges on the fly
98787c5c065Sespie 			 */
98887c5c065Sespie 			from = handledash(frombis, frombis + 256, from);
98987c5c065Sespie 			to = handledash(tobis, tobis + 256, to);
99087c5c065Sespie 		}
991df930be7Sderaadt 		tmp = from;
992df930be7Sderaadt 	/*
993df930be7Sderaadt 	 * create a mapping between "from" and
994df930be7Sderaadt 	 * "to"
995df930be7Sderaadt 	 */
996df930be7Sderaadt 		while (*from)
997ee3599c7Sespie 			mapvec[(unsigned char)(*from++)] = (*to) ?
998ee3599c7Sespie 				(unsigned char)(*to++) : 0;
999df930be7Sderaadt 
1000df930be7Sderaadt 		while (*src) {
1001ee3599c7Sespie 			sch = (unsigned char)(*src++);
1002df930be7Sderaadt 			dch = mapvec[sch];
1003df930be7Sderaadt 			while (dch != sch) {
1004df930be7Sderaadt 				sch = dch;
1005df930be7Sderaadt 				dch = mapvec[sch];
1006df930be7Sderaadt 			}
1007ee3599c7Sespie 			if ((*dest = (char)dch))
1008df930be7Sderaadt 				dest++;
1009df930be7Sderaadt 		}
1010df930be7Sderaadt 	/*
1011df930be7Sderaadt 	 * restore all the changed characters
1012df930be7Sderaadt 	 */
1013df930be7Sderaadt 		while (*tmp) {
1014ee3599c7Sespie 			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
1015df930be7Sderaadt 			tmp++;
1016df930be7Sderaadt 		}
1017df930be7Sderaadt 	}
1018ee3599c7Sespie 	*dest = '\0';
1019df930be7Sderaadt }
102087c5c065Sespie 
102187c5c065Sespie 
102287c5c065Sespie /*
102387c5c065Sespie  * handledash:
102487c5c065Sespie  *  use buffer to copy the src string, expanding character ranges
102587c5c065Sespie  * on the way.
102687c5c065Sespie  */
102787c5c065Sespie static const char *
10288e061d4bSespie handledash(char *buffer, char *end, const char *src)
102987c5c065Sespie {
103087c5c065Sespie 	char *p;
103187c5c065Sespie 
103287c5c065Sespie 	p = buffer;
103387c5c065Sespie 	while(*src) {
103487c5c065Sespie 		if (src[1] == '-' && src[2]) {
103587c5c065Sespie 			unsigned char i;
103687c5c065Sespie 			for (i = (unsigned char)src[0];
103787c5c065Sespie 			    i <= (unsigned char)src[2]; i++) {
103887c5c065Sespie 				*p++ = i;
103987c5c065Sespie 				if (p == end) {
104087c5c065Sespie 					*p = '\0';
104187c5c065Sespie 					return buffer;
104287c5c065Sespie 				}
104387c5c065Sespie 			}
104487c5c065Sespie 			src += 3;
104587c5c065Sespie 		} else
104687c5c065Sespie 			*p++ = *src++;
104787c5c065Sespie 		if (p == end)
104887c5c065Sespie 			break;
104987c5c065Sespie 	}
105087c5c065Sespie 	*p = '\0';
105187c5c065Sespie 	return buffer;
105287c5c065Sespie }
1053