xref: /openbsd/usr.bin/m4/eval.c (revision 8e061d4b)
1*8e061d4bSespie /*	$OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 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.
19df930be7Sderaadt  * 3. All advertising materials mentioning features or use of this software
20df930be7Sderaadt  *    must display the following acknowledgement:
21df930be7Sderaadt  *	This product includes software developed by the University of
22df930be7Sderaadt  *	California, Berkeley and its contributors.
23df930be7Sderaadt  * 4. Neither the name of the University nor the names of its contributors
24df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
25df930be7Sderaadt  *    without specific prior written permission.
26df930be7Sderaadt  *
27df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37df930be7Sderaadt  * SUCH DAMAGE.
38df930be7Sderaadt  */
39df930be7Sderaadt 
40df930be7Sderaadt #ifndef lint
41df930be7Sderaadt #if 0
42df930be7Sderaadt static char sccsid[] = "@(#)eval.c	8.2 (Berkeley) 4/27/95";
43df930be7Sderaadt #else
44*8e061d4bSespie static char rcsid[] = "$OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $";
45df930be7Sderaadt #endif
46df930be7Sderaadt #endif /* not lint */
47df930be7Sderaadt 
48df930be7Sderaadt /*
49df930be7Sderaadt  * eval.c
50df930be7Sderaadt  * Facility: m4 macro processor
51df930be7Sderaadt  * by: oz
52df930be7Sderaadt  */
53df930be7Sderaadt 
54df930be7Sderaadt #include <sys/types.h>
55df930be7Sderaadt #include <errno.h>
56df930be7Sderaadt #include <unistd.h>
57df930be7Sderaadt #include <stdio.h>
58df930be7Sderaadt #include <stdlib.h>
593f42598dSespie #include <stddef.h>
60df930be7Sderaadt #include <string.h>
61445b77f7Smillert #include <fcntl.h>
6281c2181eSespie #include <err.h>
63df930be7Sderaadt #include "mdef.h"
64df930be7Sderaadt #include "stdd.h"
65df930be7Sderaadt #include "extern.h"
66df930be7Sderaadt #include "pathnames.h"
67df930be7Sderaadt 
68f8b42d48Sespie #define BUILTIN_MARKER	"__builtin_"
69f8b42d48Sespie 
70c72b5b24Smillert static void	dodefn(const char *);
71c72b5b24Smillert static void	dopushdef(const char *, const char *);
72c72b5b24Smillert static void	dodump(const char *[], int);
73c72b5b24Smillert static void	dotrace(const char *[], int, int);
74c72b5b24Smillert static void	doifelse(const char *[], int);
75c72b5b24Smillert static int	doincl(const char *);
76c72b5b24Smillert static int	dopaste(const char *);
77c72b5b24Smillert static void	gnu_dochq(const char *[], int);
78c72b5b24Smillert static void	dochq(const char *[], int);
79c72b5b24Smillert static void	gnu_dochc(const char *[], int);
80c72b5b24Smillert static void	dochc(const char *[], int);
81c72b5b24Smillert static void	dodiv(int);
82c72b5b24Smillert static void	doundiv(const char *[], int);
83c72b5b24Smillert static void	dosub(const char *[], int);
84c72b5b24Smillert static void	map(char *, const char *, const char *, const char *);
85c72b5b24Smillert static const char *handledash(char *, char *, const char *);
86c72b5b24Smillert static void	expand_builtin(const char *[], int, int);
87c72b5b24Smillert static void	expand_macro(const char *[], int);
88c72b5b24Smillert static void	dump_one_def(ndptr);
8908f7f207Sespie 
9099f77b33Sespie unsigned long	expansion_id;
9108f7f207Sespie 
92df930be7Sderaadt /*
9308f7f207Sespie  * eval - eval all macros and builtins calls
94054026c0Sespie  *	  argc - number of elements in argv.
95054026c0Sespie  *	  argv - element vector :
96054026c0Sespie  *			argv[0] = definition of a user
97054026c0Sespie  *				  macro or nil if built-in.
98054026c0Sespie  *			argv[1] = name of the macro or
99054026c0Sespie  *				  built-in.
100054026c0Sespie  *			argv[2] = parameters to user-defined
101054026c0Sespie  *			   .	  macro or built-in.
102054026c0Sespie  *			   .
103054026c0Sespie  *
104054026c0Sespie  * A call in the form of macro-or-builtin() will result in:
105054026c0Sespie  *			argv[0] = nullstr
106054026c0Sespie  *			argv[1] = macro-or-builtin
107054026c0Sespie  *			argv[2] = nullstr
108054026c0Sespie  *
109054026c0Sespie  * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
11008f7f207Sespie  */
11108f7f207Sespie void
112*8e061d4bSespie eval(const char *argv[], int argc, int td)
11308f7f207Sespie {
1144dac505dSespie 	ssize_t mark = -1;
1154dac505dSespie 
11699f77b33Sespie 	expansion_id++;
11708f7f207Sespie 	if (td & RECDEF)
11808f7f207Sespie 		errx(1, "%s at line %lu: expanding recursive definition for %s",
11908f7f207Sespie 			CURRENT_NAME, CURRENT_LINE, argv[1]);
1204dac505dSespie 	if (traced_macros && is_traced(argv[1]))
1214dac505dSespie 		mark = trace(argv, argc, infile+ilevel);
12208f7f207Sespie 	if (td == MACRTYPE)
12308f7f207Sespie 		expand_macro(argv, argc);
12408f7f207Sespie 	else
12508f7f207Sespie 		expand_builtin(argv, argc, td);
1264dac505dSespie     	if (mark != -1)
1274dac505dSespie 		finish_trace(mark);
12808f7f207Sespie }
12908f7f207Sespie 
13008f7f207Sespie /*
13108f7f207Sespie  * expand_builtin - evaluate built-in macros.
132df930be7Sderaadt  */
133df930be7Sderaadt void
134*8e061d4bSespie expand_builtin(const char *argv[], int argc, int td)
135df930be7Sderaadt {
13653f6f6bfSespie 	int c, n;
137054026c0Sespie 	int ac;
138df930be7Sderaadt 	static int sysval = 0;
139df930be7Sderaadt 
140df930be7Sderaadt #ifdef DEBUG
141df930be7Sderaadt 	printf("argc = %d\n", argc);
142df930be7Sderaadt 	for (n = 0; n < argc; n++)
143df930be7Sderaadt 		printf("argv[%d] = %s\n", n, argv[n]);
14428728804Sespie 	fflush(stdout);
145df930be7Sderaadt #endif
146718b194dSespie 
147df930be7Sderaadt  /*
148df930be7Sderaadt   * if argc == 3 and argv[2] is null, then we
149df930be7Sderaadt   * have macro-or-builtin() type call. We adjust
150df930be7Sderaadt   * argc to avoid further checking..
151df930be7Sderaadt   */
152054026c0Sespie   	ac = argc;
153054026c0Sespie 
154df930be7Sderaadt 	if (argc == 3 && !*(argv[2]))
155df930be7Sderaadt 		argc--;
156df930be7Sderaadt 
157718b194dSespie 	switch (td & TYPEMASK) {
158df930be7Sderaadt 
159df930be7Sderaadt 	case DEFITYPE:
160df930be7Sderaadt 		if (argc > 2)
161df930be7Sderaadt 			dodefine(argv[2], (argc > 3) ? argv[3] : null);
162df930be7Sderaadt 		break;
163df930be7Sderaadt 
164df930be7Sderaadt 	case PUSDTYPE:
165df930be7Sderaadt 		if (argc > 2)
166df930be7Sderaadt 			dopushdef(argv[2], (argc > 3) ? argv[3] : null);
167df930be7Sderaadt 		break;
168df930be7Sderaadt 
169df930be7Sderaadt 	case DUMPTYPE:
170df930be7Sderaadt 		dodump(argv, argc);
171df930be7Sderaadt 		break;
172df930be7Sderaadt 
17334970243Sespie 	case TRACEONTYPE:
17434970243Sespie 		dotrace(argv, argc, 1);
17534970243Sespie 		break;
17634970243Sespie 
17734970243Sespie 	case TRACEOFFTYPE:
17834970243Sespie 		dotrace(argv, argc, 0);
17934970243Sespie 		break;
18034970243Sespie 
181df930be7Sderaadt 	case EXPRTYPE:
182df930be7Sderaadt 	/*
183df930be7Sderaadt 	 * doexpr - evaluate arithmetic
184df930be7Sderaadt 	 * expression
185df930be7Sderaadt 	 */
186df930be7Sderaadt 		if (argc > 2)
187df930be7Sderaadt 			pbnum(expr(argv[2]));
188df930be7Sderaadt 		break;
189df930be7Sderaadt 
190df930be7Sderaadt 	case IFELTYPE:
191df930be7Sderaadt 		if (argc > 4)
192df930be7Sderaadt 			doifelse(argv, argc);
193df930be7Sderaadt 		break;
194df930be7Sderaadt 
195df930be7Sderaadt 	case IFDFTYPE:
196df930be7Sderaadt 	/*
197df930be7Sderaadt 	 * doifdef - select one of two
198df930be7Sderaadt 	 * alternatives based on the existence of
199df930be7Sderaadt 	 * another definition
200df930be7Sderaadt 	 */
201df930be7Sderaadt 		if (argc > 3) {
202df930be7Sderaadt 			if (lookup(argv[2]) != nil)
203df930be7Sderaadt 				pbstr(argv[3]);
204df930be7Sderaadt 			else if (argc > 4)
205df930be7Sderaadt 				pbstr(argv[4]);
206df930be7Sderaadt 		}
207df930be7Sderaadt 		break;
208df930be7Sderaadt 
209df930be7Sderaadt 	case LENGTYPE:
210df930be7Sderaadt 	/*
211df930be7Sderaadt 	 * dolen - find the length of the
212df930be7Sderaadt 	 * argument
213df930be7Sderaadt 	 */
214df930be7Sderaadt 		pbnum((argc > 2) ? strlen(argv[2]) : 0);
215df930be7Sderaadt 		break;
216df930be7Sderaadt 
217df930be7Sderaadt 	case INCRTYPE:
218df930be7Sderaadt 	/*
219df930be7Sderaadt 	 * doincr - increment the value of the
220df930be7Sderaadt 	 * argument
221df930be7Sderaadt 	 */
222df930be7Sderaadt 		if (argc > 2)
223df930be7Sderaadt 			pbnum(atoi(argv[2]) + 1);
224df930be7Sderaadt 		break;
225df930be7Sderaadt 
226df930be7Sderaadt 	case DECRTYPE:
227df930be7Sderaadt 	/*
228df930be7Sderaadt 	 * dodecr - decrement the value of the
229df930be7Sderaadt 	 * argument
230df930be7Sderaadt 	 */
231df930be7Sderaadt 		if (argc > 2)
232df930be7Sderaadt 			pbnum(atoi(argv[2]) - 1);
233df930be7Sderaadt 		break;
234df930be7Sderaadt 
235df930be7Sderaadt 	case SYSCTYPE:
236df930be7Sderaadt 	/*
237df930be7Sderaadt 	 * dosys - execute system command
238df930be7Sderaadt 	 */
239df930be7Sderaadt 		if (argc > 2)
240df930be7Sderaadt 			sysval = system(argv[2]);
241df930be7Sderaadt 		break;
242df930be7Sderaadt 
243df930be7Sderaadt 	case SYSVTYPE:
244df930be7Sderaadt 	/*
245df930be7Sderaadt 	 * dosysval - return value of the last
246df930be7Sderaadt 	 * system call.
247df930be7Sderaadt 	 *
248df930be7Sderaadt 	 */
249df930be7Sderaadt 		pbnum(sysval);
250df930be7Sderaadt 		break;
251df930be7Sderaadt 
252c91edbbbSespie 	case ESYSCMDTYPE:
253c91edbbbSespie 		if (argc > 2)
254c91edbbbSespie 			doesyscmd(argv[2]);
255c91edbbbSespie 	    	break;
256df930be7Sderaadt 	case INCLTYPE:
257df930be7Sderaadt 		if (argc > 2)
258df930be7Sderaadt 			if (!doincl(argv[2]))
2590d3ffe1dSespie 				err(1, "%s at line %lu: include(%s)",
2600d3ffe1dSespie 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
261df930be7Sderaadt 		break;
262df930be7Sderaadt 
263df930be7Sderaadt 	case SINCTYPE:
264df930be7Sderaadt 		if (argc > 2)
265df930be7Sderaadt 			(void) doincl(argv[2]);
266df930be7Sderaadt 		break;
267df930be7Sderaadt #ifdef EXTENDED
268df930be7Sderaadt 	case PASTTYPE:
269df930be7Sderaadt 		if (argc > 2)
270df930be7Sderaadt 			if (!dopaste(argv[2]))
2710d3ffe1dSespie 				err(1, "%s at line %lu: paste(%s)",
2720d3ffe1dSespie 				    CURRENT_NAME, CURRENT_LINE, argv[2]);
273df930be7Sderaadt 		break;
274df930be7Sderaadt 
275df930be7Sderaadt 	case SPASTYPE:
276df930be7Sderaadt 		if (argc > 2)
277df930be7Sderaadt 			(void) dopaste(argv[2]);
278df930be7Sderaadt 		break;
279df930be7Sderaadt #endif
280df930be7Sderaadt 	case CHNQTYPE:
281054026c0Sespie 		if (mimic_gnu)
282054026c0Sespie 			gnu_dochq(argv, ac);
283054026c0Sespie 		else
284df930be7Sderaadt 			dochq(argv, argc);
285df930be7Sderaadt 		break;
286df930be7Sderaadt 
287df930be7Sderaadt 	case CHNCTYPE:
288054026c0Sespie 		if (mimic_gnu)
289054026c0Sespie 			gnu_dochc(argv, ac);
290054026c0Sespie 		else
291df930be7Sderaadt 			dochc(argv, argc);
292df930be7Sderaadt 		break;
293df930be7Sderaadt 
294df930be7Sderaadt 	case SUBSTYPE:
295df930be7Sderaadt 	/*
296df930be7Sderaadt 	 * dosub - select substring
297df930be7Sderaadt 	 *
298df930be7Sderaadt 	 */
299df930be7Sderaadt 		if (argc > 3)
300df930be7Sderaadt 			dosub(argv, argc);
301df930be7Sderaadt 		break;
302df930be7Sderaadt 
303df930be7Sderaadt 	case SHIFTYPE:
304df930be7Sderaadt 	/*
305df930be7Sderaadt 	 * doshift - push back all arguments
306df930be7Sderaadt 	 * except the first one (i.e. skip
307df930be7Sderaadt 	 * argv[2])
308df930be7Sderaadt 	 */
309df930be7Sderaadt 		if (argc > 3) {
310df930be7Sderaadt 			for (n = argc - 1; n > 3; n--) {
3113a73db8cSderaadt 				pbstr(rquote);
312df930be7Sderaadt 				pbstr(argv[n]);
3133a73db8cSderaadt 				pbstr(lquote);
314aa676ce1Smillert 				putback(COMMA);
315df930be7Sderaadt 			}
3163a73db8cSderaadt 			pbstr(rquote);
317df930be7Sderaadt 			pbstr(argv[3]);
3183a73db8cSderaadt 			pbstr(lquote);
319df930be7Sderaadt 		}
320df930be7Sderaadt 		break;
321df930be7Sderaadt 
322df930be7Sderaadt 	case DIVRTYPE:
323df930be7Sderaadt 		if (argc > 2 && (n = atoi(argv[2])) != 0)
324df930be7Sderaadt 			dodiv(n);
325df930be7Sderaadt 		else {
326df930be7Sderaadt 			active = stdout;
327df930be7Sderaadt 			oindex = 0;
328df930be7Sderaadt 		}
329df930be7Sderaadt 		break;
330df930be7Sderaadt 
331df930be7Sderaadt 	case UNDVTYPE:
332df930be7Sderaadt 		doundiv(argv, argc);
333df930be7Sderaadt 		break;
334df930be7Sderaadt 
335df930be7Sderaadt 	case DIVNTYPE:
336df930be7Sderaadt 	/*
337df930be7Sderaadt 	 * dodivnum - return the number of
338df930be7Sderaadt 	 * current output diversion
339df930be7Sderaadt 	 */
340df930be7Sderaadt 		pbnum(oindex);
341df930be7Sderaadt 		break;
342df930be7Sderaadt 
343df930be7Sderaadt 	case UNDFTYPE:
344df930be7Sderaadt 	/*
345df930be7Sderaadt 	 * doundefine - undefine a previously
346df930be7Sderaadt 	 * defined macro(s) or m4 keyword(s).
347df930be7Sderaadt 	 */
348df930be7Sderaadt 		if (argc > 2)
349df930be7Sderaadt 			for (n = 2; n < argc; n++)
350df930be7Sderaadt 				remhash(argv[n], ALL);
351df930be7Sderaadt 		break;
352df930be7Sderaadt 
353df930be7Sderaadt 	case POPDTYPE:
354df930be7Sderaadt 	/*
355df930be7Sderaadt 	 * dopopdef - remove the topmost
356df930be7Sderaadt 	 * definitions of macro(s) or m4
357df930be7Sderaadt 	 * keyword(s).
358df930be7Sderaadt 	 */
359df930be7Sderaadt 		if (argc > 2)
360df930be7Sderaadt 			for (n = 2; n < argc; n++)
361df930be7Sderaadt 				remhash(argv[n], TOP);
362df930be7Sderaadt 		break;
363df930be7Sderaadt 
364df930be7Sderaadt 	case MKTMTYPE:
365df930be7Sderaadt 	/*
366df930be7Sderaadt 	 * dotemp - create a temporary file
367df930be7Sderaadt 	 */
36801e71e69Sespie 		if (argc > 2) {
36901e71e69Sespie 			int fd;
370bb34cd6cSespie 			char *temp;
37101e71e69Sespie 
372bb34cd6cSespie 			temp = xstrdup(argv[2]);
373bb34cd6cSespie 
374bb34cd6cSespie 			fd = mkstemp(temp);
37501e71e69Sespie 			if (fd == -1)
3760d3ffe1dSespie 				err(1,
3770d3ffe1dSespie 	    "%s at line %lu: couldn't make temp file %s",
3780d3ffe1dSespie 	    CURRENT_NAME, CURRENT_LINE, argv[2]);
37901e71e69Sespie 			close(fd);
380bb34cd6cSespie 			pbstr(temp);
381bb34cd6cSespie 			free(temp);
38201e71e69Sespie 		}
383df930be7Sderaadt 		break;
384df930be7Sderaadt 
385df930be7Sderaadt 	case TRNLTYPE:
386df930be7Sderaadt 	/*
387df930be7Sderaadt 	 * dotranslit - replace all characters in
388df930be7Sderaadt 	 * the source string that appears in the
389df930be7Sderaadt 	 * "from" string with the corresponding
390df930be7Sderaadt 	 * characters in the "to" string.
391df930be7Sderaadt 	 */
392df930be7Sderaadt 		if (argc > 3) {
39328728804Sespie 			char *temp;
39428728804Sespie 
39528728804Sespie 			temp = xalloc(strlen(argv[2])+1);
396df930be7Sderaadt 			if (argc > 4)
397df930be7Sderaadt 				map(temp, argv[2], argv[3], argv[4]);
398df930be7Sderaadt 			else
399df930be7Sderaadt 				map(temp, argv[2], argv[3], null);
400df930be7Sderaadt 			pbstr(temp);
40128728804Sespie 			free(temp);
4027d3e0b6bSderaadt 		} else if (argc > 2)
403df930be7Sderaadt 			pbstr(argv[2]);
404df930be7Sderaadt 		break;
405df930be7Sderaadt 
406df930be7Sderaadt 	case INDXTYPE:
407df930be7Sderaadt 	/*
408df930be7Sderaadt 	 * doindex - find the index of the second
409df930be7Sderaadt 	 * argument string in the first argument
410df930be7Sderaadt 	 * string. -1 if not present.
411df930be7Sderaadt 	 */
412df930be7Sderaadt 		pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
413df930be7Sderaadt 		break;
414df930be7Sderaadt 
415df930be7Sderaadt 	case ERRPTYPE:
416df930be7Sderaadt 	/*
417df930be7Sderaadt 	 * doerrp - print the arguments to stderr
418df930be7Sderaadt 	 * file
419df930be7Sderaadt 	 */
420df930be7Sderaadt 		if (argc > 2) {
421df930be7Sderaadt 			for (n = 2; n < argc; n++)
422df930be7Sderaadt 				fprintf(stderr, "%s ", argv[n]);
423df930be7Sderaadt 			fprintf(stderr, "\n");
424df930be7Sderaadt 		}
425df930be7Sderaadt 		break;
426df930be7Sderaadt 
427df930be7Sderaadt 	case DNLNTYPE:
428df930be7Sderaadt 	/*
429df930be7Sderaadt 	 * dodnl - eat-up-to and including
430df930be7Sderaadt 	 * newline
431df930be7Sderaadt 	 */
432df930be7Sderaadt 		while ((c = gpbc()) != '\n' && c != EOF)
433df930be7Sderaadt 			;
434df930be7Sderaadt 		break;
435df930be7Sderaadt 
436df930be7Sderaadt 	case M4WRTYPE:
437df930be7Sderaadt 	/*
438df930be7Sderaadt 	 * dom4wrap - set up for
439df930be7Sderaadt 	 * wrap-up/wind-down activity
440df930be7Sderaadt 	 */
441df930be7Sderaadt 		m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
442df930be7Sderaadt 		break;
443df930be7Sderaadt 
444df930be7Sderaadt 	case EXITTYPE:
445df930be7Sderaadt 	/*
446df930be7Sderaadt 	 * doexit - immediate exit from m4.
447df930be7Sderaadt 	 */
448df930be7Sderaadt 		killdiv();
449df930be7Sderaadt 		exit((argc > 2) ? atoi(argv[2]) : 0);
450df930be7Sderaadt 		break;
451df930be7Sderaadt 
452df930be7Sderaadt 	case DEFNTYPE:
453df930be7Sderaadt 		if (argc > 2)
454df930be7Sderaadt 			for (n = 2; n < argc; n++)
455df930be7Sderaadt 				dodefn(argv[n]);
456df930be7Sderaadt 		break;
457df930be7Sderaadt 
458b8161682Sespie 	case INDIRTYPE:	/* Indirect call */
459b8161682Sespie 		if (argc > 2)
460b8161682Sespie 			doindir(argv, argc);
461b8161682Sespie 		break;
462b8161682Sespie 
463b8161682Sespie 	case BUILTINTYPE: /* Builtins only */
464b8161682Sespie 		if (argc > 2)
465b8161682Sespie 			dobuiltin(argv, argc);
466b8161682Sespie 		break;
467b8161682Sespie 
468b8161682Sespie 	case PATSTYPE:
469b8161682Sespie 		if (argc > 2)
470b8161682Sespie 			dopatsubst(argv, argc);
471b8161682Sespie 		break;
472b8161682Sespie 	case REGEXPTYPE:
473b8161682Sespie 		if (argc > 2)
474b8161682Sespie 			doregexp(argv, argc);
475b8161682Sespie 		break;
476b8161682Sespie 	case LINETYPE:
477b8161682Sespie 		doprintlineno(infile+ilevel);
478b8161682Sespie 		break;
479b8161682Sespie 	case FILENAMETYPE:
480b8161682Sespie 		doprintfilename(infile+ilevel);
481b8161682Sespie 		break;
482423624b7Sespie 	case SELFTYPE:
483423624b7Sespie 		pbstr(rquote);
484423624b7Sespie 		pbstr(argv[1]);
485423624b7Sespie 		pbstr(lquote);
486423624b7Sespie 		break;
487df930be7Sderaadt 	default:
4880d3ffe1dSespie 		errx(1, "%s at line %lu: eval: major botch.",
4890d3ffe1dSespie 			CURRENT_NAME, CURRENT_LINE);
490df930be7Sderaadt 		break;
491df930be7Sderaadt 	}
492df930be7Sderaadt }
493df930be7Sderaadt 
494df930be7Sderaadt /*
49508f7f207Sespie  * expand_macro - user-defined macro expansion
496df930be7Sderaadt  */
497df930be7Sderaadt void
498*8e061d4bSespie expand_macro(const char *argv[], int argc)
499df930be7Sderaadt {
500bb34cd6cSespie 	const char *t;
501bb34cd6cSespie 	const char *p;
50253f6f6bfSespie 	int n;
50353f6f6bfSespie 	int argno;
504df930be7Sderaadt 
505df930be7Sderaadt 	t = argv[0];		       /* defn string as a whole */
506df930be7Sderaadt 	p = t;
507df930be7Sderaadt 	while (*p)
508df930be7Sderaadt 		p++;
509df930be7Sderaadt 	p--;			       /* last character of defn */
510df930be7Sderaadt 	while (p > t) {
511df930be7Sderaadt 		if (*(p - 1) != ARGFLAG)
512dd84b4a6Sespie 			PUTBACK(*p);
513df930be7Sderaadt 		else {
514df930be7Sderaadt 			switch (*p) {
515df930be7Sderaadt 
516df930be7Sderaadt 			case '#':
517df930be7Sderaadt 				pbnum(argc - 2);
518df930be7Sderaadt 				break;
519df930be7Sderaadt 			case '0':
520df930be7Sderaadt 			case '1':
521df930be7Sderaadt 			case '2':
522df930be7Sderaadt 			case '3':
523df930be7Sderaadt 			case '4':
524df930be7Sderaadt 			case '5':
525df930be7Sderaadt 			case '6':
526df930be7Sderaadt 			case '7':
527df930be7Sderaadt 			case '8':
528df930be7Sderaadt 			case '9':
529df930be7Sderaadt 				if ((argno = *p - '0') < argc - 1)
530df930be7Sderaadt 					pbstr(argv[argno + 1]);
531df930be7Sderaadt 				break;
532df930be7Sderaadt 			case '*':
533faa30e49Sespie 				if (argc > 2) {
534df930be7Sderaadt 					for (n = argc - 1; n > 2; n--) {
535df930be7Sderaadt 						pbstr(argv[n]);
536aa676ce1Smillert 						putback(COMMA);
537df930be7Sderaadt 					}
538df930be7Sderaadt 					pbstr(argv[2]);
539faa30e49Sespie 			    	}
540df930be7Sderaadt 				break;
541aa676ce1Smillert                         case '@':
542faa30e49Sespie 				if (argc > 2) {
543aa676ce1Smillert 					for (n = argc - 1; n > 2; n--) {
544aa676ce1Smillert 						pbstr(rquote);
545aa676ce1Smillert 						pbstr(argv[n]);
546aa676ce1Smillert 						pbstr(lquote);
547aa676ce1Smillert 						putback(COMMA);
548aa676ce1Smillert 					}
549aa676ce1Smillert 					pbstr(rquote);
550aa676ce1Smillert 					pbstr(argv[2]);
551aa676ce1Smillert 					pbstr(lquote);
552faa30e49Sespie 				}
553aa676ce1Smillert                                 break;
554df930be7Sderaadt 			default:
555dd84b4a6Sespie 				PUTBACK(*p);
556dd84b4a6Sespie 				PUTBACK('$');
557df930be7Sderaadt 				break;
558df930be7Sderaadt 			}
559df930be7Sderaadt 			p--;
560df930be7Sderaadt 		}
561df930be7Sderaadt 		p--;
562df930be7Sderaadt 	}
563df930be7Sderaadt 	if (p == t)		       /* do last character */
564dd84b4a6Sespie 		PUTBACK(*p);
565df930be7Sderaadt }
566df930be7Sderaadt 
567df930be7Sderaadt /*
568df930be7Sderaadt  * dodefine - install definition in the table
569df930be7Sderaadt  */
570df930be7Sderaadt void
571*8e061d4bSespie dodefine(const char *name, const char *defn)
572df930be7Sderaadt {
57353f6f6bfSespie 	ndptr p;
574f8b42d48Sespie 	int n;
575df930be7Sderaadt 
576df930be7Sderaadt 	if (!*name)
5770d3ffe1dSespie 		errx(1, "%s at line %lu: null definition.", CURRENT_NAME,
5780d3ffe1dSespie 		    CURRENT_LINE);
579df930be7Sderaadt 	if ((p = lookup(name)) == nil)
580df930be7Sderaadt 		p = addent(name);
581df930be7Sderaadt 	else if (p->defn != null)
582df930be7Sderaadt 		free((char *) p->defn);
583f8b42d48Sespie 	if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0) {
584f8b42d48Sespie 		n = builtin_type(defn+sizeof(BUILTIN_MARKER)-1);
585f8b42d48Sespie 		if (n != -1) {
58655d92b07Sespie 			p->type = n & TYPEMASK;
58755d92b07Sespie 			if ((n & NOARGS) == 0)
58855d92b07Sespie 				p->type |= NEEDARGS;
589f8b42d48Sespie 			p->defn = null;
590f8b42d48Sespie 			return;
591f8b42d48Sespie 		}
592f8b42d48Sespie 	}
593df930be7Sderaadt 	if (!*defn)
594df930be7Sderaadt 		p->defn = null;
595df930be7Sderaadt 	else
596df930be7Sderaadt 		p->defn = xstrdup(defn);
597df930be7Sderaadt 	p->type = MACRTYPE;
598718b194dSespie 	if (STREQ(name, defn))
599718b194dSespie 		p->type |= RECDEF;
600df930be7Sderaadt }
601df930be7Sderaadt 
602df930be7Sderaadt /*
603df930be7Sderaadt  * dodefn - push back a quoted definition of
604df930be7Sderaadt  *      the given name.
605df930be7Sderaadt  */
606bb34cd6cSespie static void
607*8e061d4bSespie dodefn(const char *name)
608df930be7Sderaadt {
60953f6f6bfSespie 	ndptr p;
610f8b42d48Sespie 	char *real;
611df930be7Sderaadt 
612f8b42d48Sespie 	if ((p = lookup(name)) != nil) {
613f8b42d48Sespie 		if (p->defn != null) {
6143a73db8cSderaadt 			pbstr(rquote);
615df930be7Sderaadt 			pbstr(p->defn);
6163a73db8cSderaadt 			pbstr(lquote);
617f8b42d48Sespie 		} else if ((real = builtin_realname(p->type)) != NULL) {
618f8b42d48Sespie 			pbstr(real);
619f8b42d48Sespie 			pbstr(BUILTIN_MARKER);
620f8b42d48Sespie 		}
621df930be7Sderaadt 	}
622df930be7Sderaadt }
623df930be7Sderaadt 
624df930be7Sderaadt /*
625df930be7Sderaadt  * dopushdef - install a definition in the hash table
626df930be7Sderaadt  *      without removing a previous definition. Since
627df930be7Sderaadt  *      each new entry is entered in *front* of the
628df930be7Sderaadt  *      hash bucket, it hides a previous definition from
629df930be7Sderaadt  *      lookup.
630df930be7Sderaadt  */
631bb34cd6cSespie static void
632*8e061d4bSespie dopushdef(const char *name, const char *defn)
633df930be7Sderaadt {
63453f6f6bfSespie 	ndptr p;
635df930be7Sderaadt 
636df930be7Sderaadt 	if (!*name)
6370d3ffe1dSespie 		errx(1, "%s at line %lu: null definition", CURRENT_NAME,
6380d3ffe1dSespie 		    CURRENT_LINE);
639df930be7Sderaadt 	p = addent(name);
640df930be7Sderaadt 	if (!*defn)
641df930be7Sderaadt 		p->defn = null;
642df930be7Sderaadt 	else
643df930be7Sderaadt 		p->defn = xstrdup(defn);
644df930be7Sderaadt 	p->type = MACRTYPE;
645718b194dSespie 	if (STREQ(name, defn))
646718b194dSespie 		p->type |= RECDEF;
647df930be7Sderaadt }
648df930be7Sderaadt 
649df930be7Sderaadt /*
650dd0682a2Sespie  * dump_one_def - dump the specified definition.
651dd0682a2Sespie  */
652dd0682a2Sespie static void
653*8e061d4bSespie dump_one_def(ndptr p)
654dd0682a2Sespie {
6555191fa0aSespie 	char *real;
6565191fa0aSespie 
6575191fa0aSespie 	if (mimic_gnu) {
6585191fa0aSespie 		if ((p->type & TYPEMASK) == MACRTYPE)
6594dac505dSespie 			fprintf(traceout, "%s:\t%s\n", p->name, p->defn);
6605191fa0aSespie 		else {
6615191fa0aSespie 			real = builtin_realname(p->type);
6625191fa0aSespie 			if (real == NULL)
6635191fa0aSespie 				real = null;
6644dac505dSespie 			fprintf(traceout, "%s:\t<%s>\n", p->name, real);
6655191fa0aSespie 	    	}
6665191fa0aSespie 	} else
6674dac505dSespie 		fprintf(traceout, "`%s'\t`%s'\n", p->name, p->defn);
668dd0682a2Sespie }
669dd0682a2Sespie 
670dd0682a2Sespie /*
671df930be7Sderaadt  * dodumpdef - dump the specified definitions in the hash
672df930be7Sderaadt  *      table to stderr. If nothing is specified, the entire
673df930be7Sderaadt  *      hash table is dumped.
674df930be7Sderaadt  */
675bb34cd6cSespie static void
676*8e061d4bSespie dodump(const char *argv[], int argc)
677df930be7Sderaadt {
67853f6f6bfSespie 	int n;
679df930be7Sderaadt 	ndptr p;
680df930be7Sderaadt 
681df930be7Sderaadt 	if (argc > 2) {
682df930be7Sderaadt 		for (n = 2; n < argc; n++)
683df930be7Sderaadt 			if ((p = lookup(argv[n])) != nil)
684dd0682a2Sespie 				dump_one_def(p);
6857d3e0b6bSderaadt 	} else {
686df930be7Sderaadt 		for (n = 0; n < HASHSIZE; n++)
687df930be7Sderaadt 			for (p = hashtab[n]; p != nil; p = p->nxtptr)
688dd0682a2Sespie 				dump_one_def(p);
689df930be7Sderaadt 	}
690df930be7Sderaadt }
691df930be7Sderaadt 
692df930be7Sderaadt /*
69334970243Sespie  * dotrace - mark some macros as traced/untraced depending upon on.
69434970243Sespie  */
69534970243Sespie static void
696*8e061d4bSespie dotrace(const char *argv[], int argc, int on)
69734970243Sespie {
69834970243Sespie 	int n;
69934970243Sespie 
70034970243Sespie 	if (argc > 2) {
70134970243Sespie 		for (n = 2; n < argc; n++)
70234970243Sespie 			mark_traced(argv[n], on);
70334970243Sespie 	} else
70434970243Sespie 		mark_traced(NULL, on);
70534970243Sespie }
70634970243Sespie 
70734970243Sespie /*
708df930be7Sderaadt  * doifelse - select one of two alternatives - loop.
709df930be7Sderaadt  */
710bb34cd6cSespie static void
711*8e061d4bSespie doifelse(const char *argv[], int argc)
712df930be7Sderaadt {
713df930be7Sderaadt 	cycle {
714df930be7Sderaadt 		if (STREQ(argv[2], argv[3]))
715df930be7Sderaadt 			pbstr(argv[4]);
716df930be7Sderaadt 		else if (argc == 6)
717df930be7Sderaadt 			pbstr(argv[5]);
718df930be7Sderaadt 		else if (argc > 6) {
719df930be7Sderaadt 			argv += 3;
720df930be7Sderaadt 			argc -= 3;
721df930be7Sderaadt 			continue;
722df930be7Sderaadt 		}
723df930be7Sderaadt 		break;
724df930be7Sderaadt 	}
725df930be7Sderaadt }
726df930be7Sderaadt 
727df930be7Sderaadt /*
728df930be7Sderaadt  * doinclude - include a given file.
729df930be7Sderaadt  */
730bb34cd6cSespie static int
731*8e061d4bSespie doincl(const char *ifile)
732df930be7Sderaadt {
733df930be7Sderaadt 	if (ilevel + 1 == MAXINP)
7340d3ffe1dSespie 		errx(1, "%s at line %lu: too many include files.",
7350d3ffe1dSespie 		    CURRENT_NAME, CURRENT_LINE);
7360d3ffe1dSespie 	if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
737df930be7Sderaadt 		ilevel++;
738df930be7Sderaadt 		bbase[ilevel] = bufbase = bp;
739df930be7Sderaadt 		return (1);
7407d3e0b6bSderaadt 	} else
741df930be7Sderaadt 		return (0);
742df930be7Sderaadt }
743df930be7Sderaadt 
744df930be7Sderaadt #ifdef EXTENDED
745df930be7Sderaadt /*
746df930be7Sderaadt  * dopaste - include a given file without any
747df930be7Sderaadt  *           macro processing.
748df930be7Sderaadt  */
749bb34cd6cSespie static int
750*8e061d4bSespie dopaste(const char *pfile)
751df930be7Sderaadt {
752df930be7Sderaadt 	FILE *pf;
75353f6f6bfSespie 	int c;
754df930be7Sderaadt 
755df930be7Sderaadt 	if ((pf = fopen(pfile, "r")) != NULL) {
756df930be7Sderaadt 		while ((c = getc(pf)) != EOF)
757df930be7Sderaadt 			putc(c, active);
758df930be7Sderaadt 		(void) fclose(pf);
759df930be7Sderaadt 		return (1);
7607d3e0b6bSderaadt 	} else
761df930be7Sderaadt 		return (0);
762df930be7Sderaadt }
763df930be7Sderaadt #endif
764df930be7Sderaadt 
765054026c0Sespie static void
766*8e061d4bSespie gnu_dochq(const char *argv[], int ac)
767054026c0Sespie {
768054026c0Sespie 	/* In gnu-m4 mode, the only way to restore quotes is to have no
769054026c0Sespie 	 * arguments at all. */
770054026c0Sespie 	if (ac == 2) {
771054026c0Sespie 		lquote[0] = LQUOTE, lquote[1] = EOS;
772054026c0Sespie 		rquote[0] = RQUOTE, rquote[1] = EOS;
773054026c0Sespie 	} else {
774054026c0Sespie 		strlcpy(lquote, argv[2], sizeof(lquote));
775054026c0Sespie 		if(ac > 3)
776054026c0Sespie 			strlcpy(rquote, argv[3], sizeof(rquote));
777054026c0Sespie 		else
778054026c0Sespie 			rquote[0] = EOS;
779054026c0Sespie 	}
780054026c0Sespie }
781054026c0Sespie 
782df930be7Sderaadt /*
783df930be7Sderaadt  * dochq - change quote characters
784df930be7Sderaadt  */
785bb34cd6cSespie static void
786*8e061d4bSespie dochq(const char *argv[], int argc)
787df930be7Sderaadt {
788df930be7Sderaadt 	if (argc > 2) {
78918a1973bSderaadt 		if (*argv[2])
790b81b15b2Sespie 			strlcpy(lquote, argv[2], sizeof(lquote));
79118a1973bSderaadt 		else {
79218a1973bSderaadt 			lquote[0] = LQUOTE;
793f0484631Sespie 			lquote[1] = EOS;
79418a1973bSderaadt 		}
795df930be7Sderaadt 		if (argc > 3) {
796df930be7Sderaadt 			if (*argv[3])
797b81b15b2Sespie 				strlcpy(rquote, argv[3], sizeof(rquote));
7987d3e0b6bSderaadt 		} else
79929a0bfdcSderaadt 			strcpy(rquote, lquote);
8007d3e0b6bSderaadt 	} else {
801f0484631Sespie 		lquote[0] = LQUOTE, lquote[1] = EOS;
802f0484631Sespie 		rquote[0] = RQUOTE, rquote[1] = EOS;
803df930be7Sderaadt 	}
804df930be7Sderaadt }
805df930be7Sderaadt 
806054026c0Sespie static void
807*8e061d4bSespie gnu_dochc(const char *argv[], int ac)
808054026c0Sespie {
809054026c0Sespie 	/* In gnu-m4 mode, no arguments mean no comment
810054026c0Sespie 	 * arguments at all. */
811054026c0Sespie 	if (ac == 2) {
812054026c0Sespie 		scommt[0] = EOS;
813054026c0Sespie 		ecommt[0] = EOS;
814054026c0Sespie 	} else {
815054026c0Sespie 		if (*argv[2])
816054026c0Sespie 			strlcpy(scommt, argv[2], sizeof(scommt));
817054026c0Sespie 		else
818054026c0Sespie 			scommt[0] = SCOMMT, scommt[1] = EOS;
819054026c0Sespie 		if(ac > 3 && *argv[3])
820054026c0Sespie 			strlcpy(ecommt, argv[3], sizeof(ecommt));
821054026c0Sespie 		else
822054026c0Sespie 			ecommt[0] = ECOMMT, ecommt[1] = EOS;
823054026c0Sespie 	}
824054026c0Sespie }
825df930be7Sderaadt /*
826df930be7Sderaadt  * dochc - change comment characters
827df930be7Sderaadt  */
828bb34cd6cSespie static void
829*8e061d4bSespie dochc(const char *argv[], int argc)
830df930be7Sderaadt {
831df930be7Sderaadt 	if (argc > 2) {
832df930be7Sderaadt 		if (*argv[2])
833b81b15b2Sespie 			strlcpy(scommt, argv[2], sizeof(scommt));
834df930be7Sderaadt 		if (argc > 3) {
835df930be7Sderaadt 			if (*argv[3])
836b81b15b2Sespie 				strlcpy(ecommt, argv[3], sizeof(ecommt));
837df930be7Sderaadt 		}
838df930be7Sderaadt 		else
839f0484631Sespie 			ecommt[0] = ECOMMT, ecommt[1] = EOS;
840df930be7Sderaadt 	}
841df930be7Sderaadt 	else {
842f0484631Sespie 		scommt[0] = SCOMMT, scommt[1] = EOS;
843f0484631Sespie 		ecommt[0] = ECOMMT, ecommt[1] = EOS;
844df930be7Sderaadt 	}
845df930be7Sderaadt }
846df930be7Sderaadt 
847df930be7Sderaadt /*
848df930be7Sderaadt  * dodivert - divert the output to a temporary file
849df930be7Sderaadt  */
850bb34cd6cSespie static void
851*8e061d4bSespie dodiv(int n)
852df930be7Sderaadt {
853445b77f7Smillert 	int fd;
854445b77f7Smillert 
8557d3e0b6bSderaadt 	oindex = n;
85625afcddbSespie 	if (n >= maxout) {
85725afcddbSespie 		if (mimic_gnu)
85825afcddbSespie 			resizedivs(n + 10);
85925afcddbSespie 		else
86025afcddbSespie 			n = 0;		/* bitbucket */
86125afcddbSespie     	}
86225afcddbSespie 
86325afcddbSespie 	if (n < 0)
864df930be7Sderaadt 		n = 0;		       /* bitbucket */
865df930be7Sderaadt 	if (outfile[n] == NULL) {
8663f42598dSespie 		char fname[] = _PATH_DIVNAME;
8673f42598dSespie 
8683f42598dSespie 		if ((fd = mkstemp(fname)) < 0 ||
8693f42598dSespie 			(outfile[n] = fdopen(fd, "w+")) == NULL)
8703f42598dSespie 				err(1, "%s: cannot divert", fname);
8713f42598dSespie 		if (unlink(fname) == -1)
8723f42598dSespie 			err(1, "%s: cannot unlink", fname);
873df930be7Sderaadt 	}
874df930be7Sderaadt 	active = outfile[n];
875df930be7Sderaadt }
876df930be7Sderaadt 
877df930be7Sderaadt /*
878df930be7Sderaadt  * doundivert - undivert a specified output, or all
879df930be7Sderaadt  *              other outputs, in numerical order.
880df930be7Sderaadt  */
881bb34cd6cSespie static void
882*8e061d4bSespie doundiv(const char *argv[], int argc)
883df930be7Sderaadt {
88453f6f6bfSespie 	int ind;
88553f6f6bfSespie 	int n;
886df930be7Sderaadt 
887df930be7Sderaadt 	if (argc > 2) {
888df930be7Sderaadt 		for (ind = 2; ind < argc; ind++) {
889df930be7Sderaadt 			n = atoi(argv[ind]);
89025afcddbSespie 			if (n > 0 && n < maxout && outfile[n] != NULL)
891df930be7Sderaadt 				getdiv(n);
892df930be7Sderaadt 
893df930be7Sderaadt 		}
894df930be7Sderaadt 	}
895df930be7Sderaadt 	else
89625afcddbSespie 		for (n = 1; n < maxout; n++)
897df930be7Sderaadt 			if (outfile[n] != NULL)
898df930be7Sderaadt 				getdiv(n);
899df930be7Sderaadt }
900df930be7Sderaadt 
901df930be7Sderaadt /*
902df930be7Sderaadt  * dosub - select substring
903df930be7Sderaadt  */
904bb34cd6cSespie static void
905*8e061d4bSespie dosub(const char *argv[], int argc)
906df930be7Sderaadt {
907bb34cd6cSespie 	const char *ap, *fc, *k;
90853f6f6bfSespie 	int nc;
909df930be7Sderaadt 
910df930be7Sderaadt 	ap = argv[2];		       /* target string */
911df930be7Sderaadt #ifdef EXPR
912df930be7Sderaadt 	fc = ap + expr(argv[3]);       /* first char */
913df930be7Sderaadt #else
914df930be7Sderaadt 	fc = ap + atoi(argv[3]);       /* first char */
915df930be7Sderaadt #endif
916bee8364eSespie 	nc = strlen(fc);
917bee8364eSespie 	if (argc >= 5)
918bee8364eSespie #ifdef EXPR
919bee8364eSespie 		nc = min(nc, expr(argv[4]));
920bee8364eSespie #else
921bee8364eSespie 		nc = min(nc, atoi(argv[4]));
922bee8364eSespie #endif
923df930be7Sderaadt 	if (fc >= ap && fc < ap + strlen(ap))
924bee8364eSespie 		for (k = fc + nc - 1; k >= fc; k--)
925df930be7Sderaadt 			putback(*k);
926df930be7Sderaadt }
927df930be7Sderaadt 
928df930be7Sderaadt /*
929df930be7Sderaadt  * map:
930df930be7Sderaadt  * map every character of s1 that is specified in from
931df930be7Sderaadt  * into s3 and replace in s. (source s1 remains untouched)
932df930be7Sderaadt  *
933df930be7Sderaadt  * This is a standard implementation of map(s,from,to) function of ICON
934df930be7Sderaadt  * language. Within mapvec, we replace every character of "from" with
935df930be7Sderaadt  * the corresponding character in "to". If "to" is shorter than "from",
936df930be7Sderaadt  * than the corresponding entries are null, which means that those
937df930be7Sderaadt  * characters dissapear altogether. Furthermore, imagine
938df930be7Sderaadt  * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
939df930be7Sderaadt  * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
940df930be7Sderaadt  * ultimately maps to `*'. In order to achieve this effect in an efficient
941df930be7Sderaadt  * manner (i.e. without multiple passes over the destination string), we
942df930be7Sderaadt  * loop over mapvec, starting with the initial source character. if the
943df930be7Sderaadt  * character value (dch) in this location is different than the source
944df930be7Sderaadt  * character (sch), sch becomes dch, once again to index into mapvec, until
945df930be7Sderaadt  * the character value stabilizes (i.e. sch = dch, in other words
946df930be7Sderaadt  * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
947df930be7Sderaadt  * character, it will stabilize, since mapvec[0] == 0 at all times. At the
948df930be7Sderaadt  * end, we restore mapvec* back to normal where mapvec[n] == n for
949df930be7Sderaadt  * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
950df930be7Sderaadt  * about 5 times faster than any algorithm that makes multiple passes over
951df930be7Sderaadt  * destination string.
952df930be7Sderaadt  */
953bb34cd6cSespie static void
954*8e061d4bSespie map(char *dest, const char *src, const char *from, const char *to)
955df930be7Sderaadt {
956bb34cd6cSespie 	const char *tmp;
957ee3599c7Sespie 	unsigned char sch, dch;
95887c5c065Sespie 	static char frombis[257];
95987c5c065Sespie 	static char tobis[257];
960ee3599c7Sespie 	static unsigned char mapvec[256] = {
961ee3599c7Sespie 	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
962ee3599c7Sespie 	    19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
963ee3599c7Sespie 	    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
964ee3599c7Sespie 	    53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
965ee3599c7Sespie 	    70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
966ee3599c7Sespie 	    87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
967ee3599c7Sespie 	    103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
968ee3599c7Sespie 	    116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
969ee3599c7Sespie 	    129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
970ee3599c7Sespie 	    142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
971ee3599c7Sespie 	    155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
972ee3599c7Sespie 	    168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
973ee3599c7Sespie 	    181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
974ee3599c7Sespie 	    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
975ee3599c7Sespie 	    207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
976ee3599c7Sespie 	    220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
977ee3599c7Sespie 	    233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
978ee3599c7Sespie 	    246, 247, 248, 249, 250, 251, 252, 253, 254, 255
979df930be7Sderaadt 	};
980df930be7Sderaadt 
981df930be7Sderaadt 	if (*src) {
98287c5c065Sespie 		if (mimic_gnu) {
98387c5c065Sespie 			/*
98487c5c065Sespie 			 * expand character ranges on the fly
98587c5c065Sespie 			 */
98687c5c065Sespie 			from = handledash(frombis, frombis + 256, from);
98787c5c065Sespie 			to = handledash(tobis, tobis + 256, to);
98887c5c065Sespie 		}
989df930be7Sderaadt 		tmp = from;
990df930be7Sderaadt 	/*
991df930be7Sderaadt 	 * create a mapping between "from" and
992df930be7Sderaadt 	 * "to"
993df930be7Sderaadt 	 */
994df930be7Sderaadt 		while (*from)
995ee3599c7Sespie 			mapvec[(unsigned char)(*from++)] = (*to) ?
996ee3599c7Sespie 				(unsigned char)(*to++) : 0;
997df930be7Sderaadt 
998df930be7Sderaadt 		while (*src) {
999ee3599c7Sespie 			sch = (unsigned char)(*src++);
1000df930be7Sderaadt 			dch = mapvec[sch];
1001df930be7Sderaadt 			while (dch != sch) {
1002df930be7Sderaadt 				sch = dch;
1003df930be7Sderaadt 				dch = mapvec[sch];
1004df930be7Sderaadt 			}
1005ee3599c7Sespie 			if ((*dest = (char)dch))
1006df930be7Sderaadt 				dest++;
1007df930be7Sderaadt 		}
1008df930be7Sderaadt 	/*
1009df930be7Sderaadt 	 * restore all the changed characters
1010df930be7Sderaadt 	 */
1011df930be7Sderaadt 		while (*tmp) {
1012ee3599c7Sespie 			mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
1013df930be7Sderaadt 			tmp++;
1014df930be7Sderaadt 		}
1015df930be7Sderaadt 	}
1016ee3599c7Sespie 	*dest = '\0';
1017df930be7Sderaadt }
101887c5c065Sespie 
101987c5c065Sespie 
102087c5c065Sespie /*
102187c5c065Sespie  * handledash:
102287c5c065Sespie  *  use buffer to copy the src string, expanding character ranges
102387c5c065Sespie  * on the way.
102487c5c065Sespie  */
102587c5c065Sespie static const char *
1026*8e061d4bSespie handledash(char *buffer, char *end, const char *src)
102787c5c065Sespie {
102887c5c065Sespie 	char *p;
102987c5c065Sespie 
103087c5c065Sespie 	p = buffer;
103187c5c065Sespie 	while(*src) {
103287c5c065Sespie 		if (src[1] == '-' && src[2]) {
103387c5c065Sespie 			unsigned char i;
103487c5c065Sespie 			for (i = (unsigned char)src[0];
103587c5c065Sespie 			    i <= (unsigned char)src[2]; i++) {
103687c5c065Sespie 				*p++ = i;
103787c5c065Sespie 				if (p == end) {
103887c5c065Sespie 					*p = '\0';
103987c5c065Sespie 					return buffer;
104087c5c065Sespie 				}
104187c5c065Sespie 			}
104287c5c065Sespie 			src += 3;
104387c5c065Sespie 		} else
104487c5c065Sespie 			*p++ = *src++;
104587c5c065Sespie 		if (p == end)
104687c5c065Sespie 			break;
104787c5c065Sespie 	}
104887c5c065Sespie 	*p = '\0';
104987c5c065Sespie 	return buffer;
105087c5c065Sespie }
1051