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