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