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