Stop building multiple versions of telnet.
[dragonfly.git] / usr.bin / m4 / eval.c
blob09c9c1bd1a888c0697a79b1d89f8affb1ce6ca55
1 /* $OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $ */
2 /* $NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $ */
4 /*
5 * Copyright (c) 1989, 1993
6 * The Regents of the University of California. All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Ozan Yigit at York University.
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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 * @(#)eval.c 8.2 (Berkeley) 4/27/95
40 * $OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $
41 * $FreeBSD: src/usr.bin/m4/eval.c,v 1.22 2004/08/16 14:18:21 tjr Exp $
42 * $DragonFly: src/usr.bin/m4/eval.c,v 1.3 2006/12/27 21:29:02 pavalos Exp $
46 * eval.c
47 * Facility: m4 macro processor
48 * by: oz
51 #include <sys/types.h>
52 #include <errno.h>
53 #include <unistd.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <stddef.h>
57 #include <string.h>
58 #include <fcntl.h>
59 #include <err.h>
60 #include "mdef.h"
61 #include "stdd.h"
62 #include "extern.h"
63 #include "pathnames.h"
65 #define BUILTIN_MARKER "__builtin_"
67 static void dodefn(const char *);
68 static void dopushdef(const char *, const char *);
69 static void dodump(const char *[], int);
70 static void dotrace(const char *[], int, int);
71 static void doifelse(const char *[], int);
72 static int doincl(const char *);
73 static int dopaste(const char *);
74 static void gnu_dochq(const char *[], int);
75 static void dochq(const char *[], int);
76 static void gnu_dochc(const char *[], int);
77 static void dochc(const char *[], int);
78 static void dodiv(int);
79 static void doundiv(const char *[], int);
80 static void dosub(const char *[], int);
81 static void map(char *, const char *, const char *, const char *);
82 static const char *handledash(char *, char *, const char *);
83 static void expand_builtin(const char *[], int, int);
84 static void expand_macro(const char *[], int);
85 static void dump_one_def(ndptr);
87 unsigned long expansion_id;
90 * eval - eval all macros and builtins calls
91 * argc - number of elements in argv.
92 * argv - element vector :
93 * argv[0] = definition of a user
94 * macro or nil if built-in.
95 * argv[1] = name of the macro or
96 * built-in.
97 * argv[2] = parameters to user-defined
98 * . macro or built-in.
99 * .
101 * A call in the form of macro-or-builtin() will result in:
102 * argv[0] = nullstr
103 * argv[1] = macro-or-builtin
104 * argv[2] = nullstr
106 * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
108 void
109 eval(const char *argv[], int argc, int td)
111 ssize_t mark = -1;
113 expansion_id++;
114 if (td & RECDEF)
115 errx(1, "%s at line %lu: expanding recursive definition for %s",
116 CURRENT_NAME, CURRENT_LINE, argv[1]);
117 if (traced_macros && is_traced(argv[1]))
118 mark = trace(argv, argc, infile+ilevel);
119 if (td == MACRTYPE)
120 expand_macro(argv, argc);
121 else
122 expand_builtin(argv, argc, td);
123 if (mark != -1)
124 finish_trace(mark);
128 * expand_builtin - evaluate built-in macros.
130 void
131 expand_builtin(const char *argv[], int argc, int td)
133 int c, n;
134 int ac;
135 static int sysval = 0;
137 #ifdef DEBUG
138 printf("argc = %d\n", argc);
139 for (n = 0; n < argc; n++)
140 printf("argv[%d] = %s\n", n, argv[n]);
141 fflush(stdout);
142 #endif
145 * if argc == 3 and argv[2] is null, then we
146 * have macro-or-builtin() type call. We adjust
147 * argc to avoid further checking..
149 ac = argc;
151 if (argc == 3 && !*(argv[2]))
152 argc--;
154 switch (td & TYPEMASK) {
156 case DEFITYPE:
157 if (argc > 2)
158 dodefine(argv[2], (argc > 3) ? argv[3] : null);
159 break;
161 case PUSDTYPE:
162 if (argc > 2)
163 dopushdef(argv[2], (argc > 3) ? argv[3] : null);
164 break;
166 case DUMPTYPE:
167 dodump(argv, argc);
168 break;
170 case TRACEONTYPE:
171 dotrace(argv, argc, 1);
172 break;
174 case TRACEOFFTYPE:
175 dotrace(argv, argc, 0);
176 break;
178 case EXPRTYPE:
180 * doexpr - evaluate arithmetic
181 * expression
183 if (argc > 2)
184 pbnum(expr(argv[2]));
185 break;
187 case IFELTYPE:
188 if (argc > 4)
189 doifelse(argv, argc);
190 break;
192 case IFDFTYPE:
194 * doifdef - select one of two
195 * alternatives based on the existence of
196 * another definition
198 if (argc > 3) {
199 if (lookup(argv[2]) != nil)
200 pbstr(argv[3]);
201 else if (argc > 4)
202 pbstr(argv[4]);
204 break;
206 case LENGTYPE:
208 * dolen - find the length of the
209 * argument
211 pbnum((argc > 2) ? strlen(argv[2]) : 0);
212 break;
214 case INCRTYPE:
216 * doincr - increment the value of the
217 * argument
219 if (argc > 2)
220 pbnum(atoi(argv[2]) + 1);
221 break;
223 case DECRTYPE:
225 * dodecr - decrement the value of the
226 * argument
228 if (argc > 2)
229 pbnum(atoi(argv[2]) - 1);
230 break;
232 case SYSCTYPE:
234 * dosys - execute system command
236 if (argc > 2) {
237 fflush(NULL);
238 sysval = system(argv[2]);
240 break;
242 case SYSVTYPE:
244 * dosysval - return value of the last
245 * system call.
248 pbnum(sysval);
249 break;
251 case ESYSCMDTYPE:
252 if (argc > 2)
253 doesyscmd(argv[2]);
254 break;
255 case INCLTYPE:
256 if (argc > 2)
257 if (!doincl(argv[2]))
258 err(1, "%s at line %lu: include(%s)",
259 CURRENT_NAME, CURRENT_LINE, argv[2]);
260 break;
262 case SINCTYPE:
263 if (argc > 2)
264 (void) doincl(argv[2]);
265 break;
266 #ifdef EXTENDED
267 case PASTTYPE:
268 if (argc > 2)
269 if (!dopaste(argv[2]))
270 err(1, "%s at line %lu: paste(%s)",
271 CURRENT_NAME, CURRENT_LINE, argv[2]);
272 break;
274 case SPASTYPE:
275 if (argc > 2)
276 (void) dopaste(argv[2]);
277 break;
278 #endif
279 case CHNQTYPE:
280 if (mimic_gnu)
281 gnu_dochq(argv, ac);
282 else
283 dochq(argv, argc);
284 break;
286 case CHNCTYPE:
287 if (mimic_gnu)
288 gnu_dochc(argv, ac);
289 else
290 dochc(argv, argc);
291 break;
293 case SUBSTYPE:
295 * dosub - select substring
298 if (argc > 3)
299 dosub(argv, argc);
300 break;
302 case SHIFTYPE:
304 * doshift - push back all arguments
305 * except the first one (i.e. skip
306 * argv[2])
308 if (argc > 3) {
309 for (n = argc - 1; n > 3; n--) {
310 pbstr(rquote);
311 pbstr(argv[n]);
312 pbstr(lquote);
313 putback(COMMA);
315 pbstr(rquote);
316 pbstr(argv[3]);
317 pbstr(lquote);
319 break;
321 case DIVRTYPE:
322 if (argc > 2 && (n = atoi(argv[2])) != 0)
323 dodiv(n);
324 else {
325 active = stdout;
326 oindex = 0;
328 break;
330 case UNDVTYPE:
331 doundiv(argv, argc);
332 break;
334 case DIVNTYPE:
336 * dodivnum - return the number of
337 * current output diversion
339 pbnum(oindex);
340 break;
342 case UNDFTYPE:
344 * doundefine - undefine a previously
345 * defined macro(s) or m4 keyword(s).
347 if (argc > 2)
348 for (n = 2; n < argc; n++)
349 remhash(argv[n], ALL);
350 break;
352 case POPDTYPE:
354 * dopopdef - remove the topmost
355 * definitions of macro(s) or m4
356 * keyword(s).
358 if (argc > 2)
359 for (n = 2; n < argc; n++)
360 remhash(argv[n], TOP);
361 break;
363 case MKTMTYPE:
365 * dotemp - create a temporary file
367 if (argc > 2) {
368 int fd;
369 char *temp;
371 temp = xstrdup(argv[2]);
373 fd = mkstemp(temp);
374 if (fd == -1)
375 err(1,
376 "%s at line %lu: couldn't make temp file %s",
377 CURRENT_NAME, CURRENT_LINE, argv[2]);
378 close(fd);
379 pbstr(temp);
380 free(temp);
382 break;
384 case TRNLTYPE:
386 * dotranslit - replace all characters in
387 * the source string that appears in the
388 * "from" string with the corresponding
389 * characters in the "to" string.
391 if (argc > 3) {
392 char *temp;
394 temp = xalloc(strlen(argv[2])+1);
395 if (argc > 4)
396 map(temp, argv[2], argv[3], argv[4]);
397 else
398 map(temp, argv[2], argv[3], null);
399 pbstr(temp);
400 free(temp);
401 } else if (argc > 2)
402 pbstr(argv[2]);
403 break;
405 case INDXTYPE:
407 * doindex - find the index of the second
408 * argument string in the first argument
409 * string. -1 if not present.
411 pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
412 break;
414 case ERRPTYPE:
416 * doerrp - print the arguments to stderr
417 * file
419 if (argc > 2) {
420 for (n = 2; n < argc; n++)
421 fprintf(stderr, "%s ", argv[n]);
422 fprintf(stderr, "\n");
424 break;
426 case DNLNTYPE:
428 * dodnl - eat-up-to and including
429 * newline
431 while ((c = gpbc()) != '\n' && c != EOF)
433 break;
435 case M4WRTYPE:
437 * dom4wrap - set up for
438 * wrap-up/wind-down activity
440 m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
441 break;
443 case EXITTYPE:
445 * doexit - immediate exit from m4.
447 killdiv();
448 exit((argc > 2) ? atoi(argv[2]) : 0);
449 break;
451 case DEFNTYPE:
452 if (argc > 2)
453 for (n = 2; n < argc; n++)
454 dodefn(argv[n]);
455 break;
457 case INDIRTYPE: /* Indirect call */
458 if (argc > 2)
459 doindir(argv, argc);
460 break;
462 case BUILTINTYPE: /* Builtins only */
463 if (argc > 2)
464 dobuiltin(argv, argc);
465 break;
467 case PATSTYPE:
468 if (argc > 2)
469 dopatsubst(argv, argc);
470 break;
471 case REGEXPTYPE:
472 if (argc > 2)
473 doregexp(argv, argc);
474 break;
475 case LINETYPE:
476 doprintlineno(infile+ilevel);
477 break;
478 case FILENAMETYPE:
479 doprintfilename(infile+ilevel);
480 break;
481 case SELFTYPE:
482 pbstr(rquote);
483 pbstr(argv[1]);
484 pbstr(lquote);
485 break;
486 default:
487 errx(1, "%s at line %lu: eval: major botch.",
488 CURRENT_NAME, CURRENT_LINE);
489 break;
494 * expand_macro - user-defined macro expansion
496 void
497 expand_macro(const char *argv[], int argc)
499 const char *t;
500 const char *p;
501 int n;
502 int argno;
504 t = argv[0]; /* defn string as a whole */
505 p = t;
506 while (*p)
507 p++;
508 p--; /* last character of defn */
509 while (p > t) {
510 if (*(p - 1) != ARGFLAG)
511 PUTBACK(*p);
512 else {
513 switch (*p) {
515 case '#':
516 pbnum(argc - 2);
517 break;
518 case '0':
519 case '1':
520 case '2':
521 case '3':
522 case '4':
523 case '5':
524 case '6':
525 case '7':
526 case '8':
527 case '9':
528 if ((argno = *p - '0') < argc - 1)
529 pbstr(argv[argno + 1]);
530 break;
531 case '*':
532 if (argc > 2) {
533 for (n = argc - 1; n > 2; n--) {
534 pbstr(argv[n]);
535 putback(COMMA);
537 pbstr(argv[2]);
539 break;
540 case '@':
541 if (argc > 2) {
542 for (n = argc - 1; n > 2; n--) {
543 pbstr(rquote);
544 pbstr(argv[n]);
545 pbstr(lquote);
546 putback(COMMA);
548 pbstr(rquote);
549 pbstr(argv[2]);
550 pbstr(lquote);
552 break;
553 default:
554 PUTBACK(*p);
555 PUTBACK('$');
556 break;
558 p--;
560 p--;
562 if (p == t) /* do last character */
563 PUTBACK(*p);
567 * dodefine - install definition in the table
569 void
570 dodefine(const char *name, const char *defn)
572 ndptr p;
573 int n;
575 if (!*name)
576 errx(1, "%s at line %lu: null definition.", CURRENT_NAME,
577 CURRENT_LINE);
578 if ((p = lookup(name)) == nil)
579 p = addent(name);
580 else if (p->defn != null)
581 free((char *) p->defn);
582 if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0) {
583 n = builtin_type(defn+sizeof(BUILTIN_MARKER)-1);
584 if (n != -1) {
585 p->type = n & TYPEMASK;
586 if ((n & NOARGS) == 0)
587 p->type |= NEEDARGS;
588 p->defn = null;
589 return;
592 if (!*defn)
593 p->defn = null;
594 else
595 p->defn = xstrdup(defn);
596 p->type = MACRTYPE;
597 if (STREQ(name, defn))
598 p->type |= RECDEF;
602 * dodefn - push back a quoted definition of
603 * the given name.
605 static void
606 dodefn(const char *name)
608 ndptr p;
609 const char *real;
611 if ((p = lookup(name)) != nil) {
612 if (p->defn != null) {
613 pbstr(rquote);
614 pbstr(p->defn);
615 pbstr(lquote);
616 } else if ((real = builtin_realname(p->type)) != NULL) {
617 pbstr(real);
618 pbstr(BUILTIN_MARKER);
624 * dopushdef - install a definition in the hash table
625 * without removing a previous definition. Since
626 * each new entry is entered in *front* of the
627 * hash bucket, it hides a previous definition from
628 * lookup.
630 static void
631 dopushdef(const char *name, const char *defn)
633 ndptr p;
635 if (!*name)
636 errx(1, "%s at line %lu: null definition", CURRENT_NAME,
637 CURRENT_LINE);
638 p = addent(name);
639 if (!*defn)
640 p->defn = null;
641 else
642 p->defn = xstrdup(defn);
643 p->type = MACRTYPE;
644 if (STREQ(name, defn))
645 p->type |= RECDEF;
649 * dump_one_def - dump the specified definition.
651 static void
652 dump_one_def(ndptr p)
654 const char *real;
656 if (mimic_gnu) {
657 if ((p->type & TYPEMASK) == MACRTYPE)
658 fprintf(traceout, "%s:\t%s\n", p->name, p->defn);
659 else {
660 real = builtin_realname(p->type);
661 if (real == NULL)
662 real = null;
663 fprintf(traceout, "%s:\t<%s>\n", p->name, real);
665 } else
666 fprintf(traceout, "`%s'\t`%s'\n", p->name, p->defn);
670 * dodumpdef - dump the specified definitions in the hash
671 * table to stderr. If nothing is specified, the entire
672 * hash table is dumped.
674 static void
675 dodump(const char *argv[], int argc)
677 int n;
678 ndptr p;
680 if (argc > 2) {
681 for (n = 2; n < argc; n++)
682 if ((p = lookup(argv[n])) != nil)
683 dump_one_def(p);
684 } else {
685 for (n = 0; n < HASHSIZE; n++)
686 for (p = hashtab[n]; p != nil; p = p->nxtptr)
687 dump_one_def(p);
692 * dotrace - mark some macros as traced/untraced depending upon on.
694 static void
695 dotrace(const char *argv[], int argc, int on)
697 int n;
699 if (argc > 2) {
700 for (n = 2; n < argc; n++)
701 mark_traced(argv[n], on);
702 } else
703 mark_traced(NULL, on);
707 * doifelse - select one of two alternatives - loop.
709 static void
710 doifelse(const char *argv[], int argc)
712 cycle {
713 if (STREQ(argv[2], argv[3]))
714 pbstr(argv[4]);
715 else if (argc == 6)
716 pbstr(argv[5]);
717 else if (argc > 6) {
718 argv += 3;
719 argc -= 3;
720 continue;
722 break;
727 * doinclude - include a given file.
729 static int
730 doincl(const char *ifile)
732 if (ilevel + 1 == MAXINP)
733 errx(1, "%s at line %lu: too many include files.",
734 CURRENT_NAME, CURRENT_LINE);
735 if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
736 ilevel++;
737 if ((inname[ilevel] = strdup(ifile)) == NULL)
738 err(1, NULL);
739 inlineno[ilevel] = 1;
740 bbase[ilevel] = bufbase = bp;
741 emitline();
742 return (1);
743 } else
744 return (0);
747 #ifdef EXTENDED
749 * dopaste - include a given file without any
750 * macro processing.
752 static int
753 dopaste(const char *pfile)
755 FILE *pf;
756 int c;
758 if ((pf = fopen(pfile, "r")) != NULL) {
759 fprintf(active, "#line 1 \"%s\"\n", pfile);
760 while ((c = getc(pf)) != EOF)
761 putc(c, active);
762 (void) fclose(pf);
763 emitline();
764 return (1);
765 } else
766 return (0);
768 #endif
770 static void
771 gnu_dochq(const char *argv[], int ac)
773 /* In gnu-m4 mode, the only way to restore quotes is to have no
774 * arguments at all. */
775 if (ac == 2) {
776 lquote[0] = LQUOTE, lquote[1] = EOS;
777 rquote[0] = RQUOTE, rquote[1] = EOS;
778 } else {
779 strlcpy(lquote, argv[2], sizeof(lquote));
780 if(ac > 3)
781 strlcpy(rquote, argv[3], sizeof(rquote));
782 else
783 rquote[0] = EOS;
788 * dochq - change quote characters
790 static void
791 dochq(const char *argv[], int argc)
793 if (argc > 2) {
794 if (*argv[2])
795 strlcpy(lquote, argv[2], sizeof(lquote));
796 else {
797 lquote[0] = LQUOTE;
798 lquote[1] = EOS;
800 if (argc > 3) {
801 if (*argv[3])
802 strlcpy(rquote, argv[3], sizeof(rquote));
803 } else
804 strcpy(rquote, lquote);
805 } else {
806 lquote[0] = LQUOTE, lquote[1] = EOS;
807 rquote[0] = RQUOTE, rquote[1] = EOS;
811 static void
812 gnu_dochc(const char *argv[], int ac)
814 /* In gnu-m4 mode, no arguments mean no comment
815 * arguments at all. */
816 if (ac == 2) {
817 scommt[0] = EOS;
818 ecommt[0] = EOS;
819 } else {
820 if (*argv[2])
821 strlcpy(scommt, argv[2], sizeof(scommt));
822 else
823 scommt[0] = SCOMMT, scommt[1] = EOS;
824 if(ac > 3 && *argv[3])
825 strlcpy(ecommt, argv[3], sizeof(ecommt));
826 else
827 ecommt[0] = ECOMMT, ecommt[1] = EOS;
831 * dochc - change comment characters
833 static void
834 dochc(const char *argv[], int argc)
836 if (argc > 2) {
837 if (*argv[2])
838 strlcpy(scommt, argv[2], sizeof(scommt));
839 if (argc > 3) {
840 if (*argv[3])
841 strlcpy(ecommt, argv[3], sizeof(ecommt));
843 else
844 ecommt[0] = ECOMMT, ecommt[1] = EOS;
846 else {
847 scommt[0] = SCOMMT, scommt[1] = EOS;
848 ecommt[0] = ECOMMT, ecommt[1] = EOS;
853 * dodivert - divert the output to a temporary file
855 static void
856 dodiv(int n)
858 int fd;
860 oindex = n;
861 if (n >= maxout) {
862 if (mimic_gnu)
863 resizedivs(n + 10);
864 else
865 n = 0; /* bitbucket */
868 if (n < 0)
869 n = 0; /* bitbucket */
870 if (outfile[n] == NULL) {
871 char fname[] = _PATH_DIVNAME;
873 if ((fd = mkstemp(fname)) < 0 ||
874 (outfile[n] = fdopen(fd, "w+")) == NULL)
875 err(1, "%s: cannot divert", fname);
876 if (unlink(fname) == -1)
877 err(1, "%s: cannot unlink", fname);
879 active = outfile[n];
883 * doundivert - undivert a specified output, or all
884 * other outputs, in numerical order.
886 static void
887 doundiv(const char *argv[], int argc)
889 int ind;
890 int n;
892 if (argc > 2) {
893 for (ind = 2; ind < argc; ind++) {
894 n = atoi(argv[ind]);
895 if (n > 0 && n < maxout && outfile[n] != NULL)
896 getdiv(n);
900 else
901 for (n = 1; n < maxout; n++)
902 if (outfile[n] != NULL)
903 getdiv(n);
907 * dosub - select substring
909 static void
910 dosub(const char *argv[], int argc)
912 const char *ap, *fc, *k;
913 int nc;
915 ap = argv[2]; /* target string */
916 #ifdef EXPR
917 fc = ap + expr(argv[3]); /* first char */
918 #else
919 fc = ap + atoi(argv[3]); /* first char */
920 #endif
921 nc = strlen(fc);
922 if (argc >= 5)
923 #ifdef EXPR
924 nc = min(nc, expr(argv[4]));
925 #else
926 nc = min(nc, atoi(argv[4]));
927 #endif
928 if (fc >= ap && fc < ap + strlen(ap))
929 for (k = fc + nc - 1; k >= fc; k--)
930 putback(*k);
934 * map:
935 * map every character of s1 that is specified in from
936 * into s3 and replace in s. (source s1 remains untouched)
938 * This is a standard implementation of map(s,from,to) function of ICON
939 * language. Within mapvec, we replace every character of "from" with
940 * the corresponding character in "to". If "to" is shorter than "from",
941 * than the corresponding entries are null, which means that those
942 * characters dissapear altogether. Furthermore, imagine
943 * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
944 * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
945 * ultimately maps to `*'. In order to achieve this effect in an efficient
946 * manner (i.e. without multiple passes over the destination string), we
947 * loop over mapvec, starting with the initial source character. if the
948 * character value (dch) in this location is different than the source
949 * character (sch), sch becomes dch, once again to index into mapvec, until
950 * the character value stabilizes (i.e. sch = dch, in other words
951 * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
952 * character, it will stabilize, since mapvec[0] == 0 at all times. At the
953 * end, we restore mapvec* back to normal where mapvec[n] == n for
954 * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
955 * about 5 times faster than any algorithm that makes multiple passes over
956 * destination string.
958 static void
959 map(char *dest, const char *src, const char *from, const char *to)
961 const char *tmp;
962 unsigned char sch, dch;
963 static char frombis[257];
964 static char tobis[257];
965 static unsigned char mapvec[256] = {
966 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
967 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
968 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
969 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
970 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
971 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
972 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
973 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
974 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
975 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
976 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
977 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
978 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
979 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
980 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
981 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
982 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
983 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
986 if (*src) {
987 if (mimic_gnu) {
989 * expand character ranges on the fly
991 from = handledash(frombis, frombis + 256, from);
992 to = handledash(tobis, tobis + 256, to);
994 tmp = from;
996 * create a mapping between "from" and
997 * "to"
999 while (*from)
1000 mapvec[(unsigned char)(*from++)] = (*to) ?
1001 (unsigned char)(*to++) : 0;
1003 while (*src) {
1004 sch = (unsigned char)(*src++);
1005 dch = mapvec[sch];
1006 while (dch != sch) {
1007 sch = dch;
1008 dch = mapvec[sch];
1010 if ((*dest = (char)dch))
1011 dest++;
1014 * restore all the changed characters
1016 while (*tmp) {
1017 mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
1018 tmp++;
1021 *dest = '\0';
1026 * handledash:
1027 * use buffer to copy the src string, expanding character ranges
1028 * on the way.
1030 static const char *
1031 handledash(char *buffer, char *end, const char *src)
1033 char *p;
1035 p = buffer;
1036 while(*src) {
1037 if (src[1] == '-' && src[2]) {
1038 unsigned char i;
1039 for (i = (unsigned char)src[0];
1040 i <= (unsigned char)src[2]; i++) {
1041 *p++ = i;
1042 if (p == end) {
1043 *p = '\0';
1044 return buffer;
1047 src += 3;
1048 } else
1049 *p++ = *src++;
1050 if (p == end)
1051 break;
1053 *p = '\0';
1054 return buffer;