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