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