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