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