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