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 36 #include <sys/cdefs.h> 37 __FBSDID("$FreeBSD$"); 38 39 40 /* 41 * eval.c 42 * Facility: m4 macro processor 43 * by: oz 44 */ 45 46 #include <sys/types.h> 47 #include <err.h> 48 #include <errno.h> 49 #include <limits.h> 50 #include <unistd.h> 51 #include <stdint.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <stddef.h> 55 #include <string.h> 56 #include <fcntl.h> 57 #include "mdef.h" 58 #include "stdd.h" 59 #include "extern.h" 60 #include "pathnames.h" 61 62 static void dodefn(const char *); 63 static void dopushdef(const char *, const char *); 64 static void dodump(const char *[], int); 65 static void dotrace(const char *[], int, int); 66 static void doifelse(const char *[], int); 67 static int doincl(const char *); 68 static int dopaste(const char *); 69 static void dochq(const char *[], int); 70 static void dochc(const char *[], int); 71 static void dom4wrap(const char *); 72 static void dodiv(int); 73 static void doundiv(const char *[], int); 74 static void dosub(const char *[], int); 75 static void map(char *, const char *, const char *, const char *); 76 static const char *handledash(char *, char *, const char *); 77 static void expand_builtin(const char *[], int, int); 78 static void expand_macro(const char *[], int); 79 static void dump_one_def(const char *, struct macro_definition *); 80 81 unsigned long expansion_id; 82 83 /* 84 * eval - eval all macros and builtins calls 85 * argc - number of elements in argv. 86 * argv - element vector : 87 * argv[0] = definition of a user 88 * macro or NULL if built-in. 89 * argv[1] = name of the macro or 90 * built-in. 91 * argv[2] = parameters to user-defined 92 * . macro or built-in. 93 * . 94 * 95 * A call in the form of macro-or-builtin() will result in: 96 * argv[0] = nullstr 97 * argv[1] = macro-or-builtin 98 * argv[2] = nullstr 99 * 100 * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin 101 */ 102 void 103 eval(const char *argv[], int argc, int td, int is_traced) 104 { 105 size_t mark = SIZE_MAX; 106 107 expansion_id++; 108 if (td & RECDEF) 109 m4errx(1, "expanding recursive definition for %s.", argv[1]); 110 if (is_traced) 111 mark = trace(argv, argc, infile+ilevel); 112 if (td == MACRTYPE) 113 expand_macro(argv, argc); 114 else 115 expand_builtin(argv, argc, td); 116 if (mark != SIZE_MAX) 117 finish_trace(mark); 118 } 119 120 /* 121 * expand_builtin - evaluate built-in macros. 122 */ 123 void 124 expand_builtin(const char *argv[], int argc, int td) 125 { 126 int c, n; 127 int ac; 128 static int sysval = 0; 129 130 #ifdef DEBUG 131 printf("argc = %d\n", argc); 132 for (n = 0; n < argc; n++) 133 printf("argv[%d] = %s\n", n, argv[n]); 134 fflush(stdout); 135 #endif 136 137 /* 138 * if argc == 3 and argv[2] is null, then we 139 * have macro-or-builtin() type call. We adjust 140 * argc to avoid further checking.. 141 */ 142 /* we keep the initial value for those built-ins that differentiate 143 * between builtin() and builtin. 144 */ 145 ac = argc; 146 147 if (argc == 3 && !*(argv[2]) && !mimic_gnu) 148 argc--; 149 150 switch (td & TYPEMASK) { 151 152 case DEFITYPE: 153 if (argc > 2) 154 dodefine(argv[2], (argc > 3) ? argv[3] : null); 155 break; 156 157 case PUSDTYPE: 158 if (argc > 2) 159 dopushdef(argv[2], (argc > 3) ? argv[3] : null); 160 break; 161 162 case DUMPTYPE: 163 dodump(argv, argc); 164 break; 165 166 case TRACEONTYPE: 167 dotrace(argv, argc, 1); 168 break; 169 170 case TRACEOFFTYPE: 171 dotrace(argv, argc, 0); 172 break; 173 174 case EXPRTYPE: 175 /* 176 * doexpr - evaluate arithmetic 177 * expression 178 */ 179 { 180 int base = 10; 181 int maxdigits = 0; 182 const char *errstr; 183 184 if (argc > 3) { 185 base = strtonum(argv[3], 2, 36, &errstr); 186 if (errstr) { 187 m4errx(1, "expr: base %s invalid.", argv[3]); 188 } 189 } 190 if (argc > 4) { 191 maxdigits = strtonum(argv[4], 0, INT_MAX, &errstr); 192 if (errstr) { 193 m4errx(1, "expr: maxdigits %s invalid.", argv[4]); 194 } 195 } 196 if (argc > 2) 197 pbnumbase(expr(argv[2]), base, maxdigits); 198 break; 199 } 200 201 case IFELTYPE: 202 if (argc > 4) 203 doifelse(argv, argc); 204 break; 205 206 case IFDFTYPE: 207 /* 208 * doifdef - select one of two 209 * alternatives based on the existence of 210 * another definition 211 */ 212 if (argc > 3) { 213 if (lookup_macro_definition(argv[2]) != NULL) 214 pbstr(argv[3]); 215 else if (argc > 4) 216 pbstr(argv[4]); 217 } 218 break; 219 220 case LENGTYPE: 221 /* 222 * dolen - find the length of the 223 * argument 224 */ 225 pbnum((argc > 2) ? strlen(argv[2]) : 0); 226 break; 227 228 case INCRTYPE: 229 /* 230 * doincr - increment the value of the 231 * argument 232 */ 233 if (argc > 2) 234 pbnum(atoi(argv[2]) + 1); 235 break; 236 237 case DECRTYPE: 238 /* 239 * dodecr - decrement the value of the 240 * argument 241 */ 242 if (argc > 2) 243 pbnum(atoi(argv[2]) - 1); 244 break; 245 246 case SYSCTYPE: 247 /* 248 * dosys - execute system command 249 */ 250 if (argc > 2) { 251 fflush(stdout); 252 sysval = system(argv[2]); 253 } 254 break; 255 256 case SYSVTYPE: 257 /* 258 * dosysval - return value of the last 259 * system call. 260 * 261 */ 262 pbnum(sysval); 263 break; 264 265 case ESYSCMDTYPE: 266 if (argc > 2) 267 doesyscmd(argv[2]); 268 break; 269 case INCLTYPE: 270 if (argc > 2) 271 if (!doincl(argv[2])) { 272 if (mimic_gnu) 273 warn("%s at line %lu: include(%s)", 274 CURRENT_NAME, CURRENT_LINE, argv[2]); 275 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 (void) 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 (void) 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 */ 512 void 513 expand_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 */ 586 void 587 dodefine(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 */ 599 static void 600 dodefn(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 */ 623 static void 624 dopushdef(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 */ 635 static void 636 dump_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 } 646 } else 647 fprintf(traceout, "`%s'\t`%s'\n", name, p->defn); 648 } 649 650 /* 651 * dodumpdef - dump the specified definitions in the hash 652 * table to stderr. If nothing is specified, the entire 653 * hash table is dumped. 654 */ 655 static void 656 dodump(const char *argv[], int argc) 657 { 658 int n; 659 struct macro_definition *p; 660 661 if (argc > 2) { 662 for (n = 2; n < argc; n++) 663 if ((p = lookup_macro_definition(argv[n])) != NULL) 664 dump_one_def(argv[n], p); 665 } else 666 macro_for_all(dump_one_def); 667 } 668 669 /* 670 * dotrace - mark some macros as traced/untraced depending upon on. 671 */ 672 static void 673 dotrace(const char *argv[], int argc, int on) 674 { 675 int n; 676 677 if (argc > 2) { 678 for (n = 2; n < argc; n++) 679 mark_traced(argv[n], on); 680 } else 681 mark_traced(NULL, on); 682 } 683 684 /* 685 * doifelse - select one of two alternatives - loop. 686 */ 687 static void 688 doifelse(const char *argv[], int argc) 689 { 690 cycle { 691 if (STREQ(argv[2], argv[3])) 692 pbstr(argv[4]); 693 else if (argc == 6) 694 pbstr(argv[5]); 695 else if (argc > 6) { 696 argv += 3; 697 argc -= 3; 698 continue; 699 } 700 break; 701 } 702 } 703 704 /* 705 * doinclude - include a given file. 706 */ 707 static int 708 doincl(const char *ifile) 709 { 710 if (ilevel + 1 == MAXINP) 711 m4errx(1, "too many include files."); 712 if (fopen_trypath(infile+ilevel+1, ifile) != NULL) { 713 ilevel++; 714 bbase[ilevel] = bufbase = bp; 715 return (1); 716 } else 717 return (0); 718 } 719 720 #ifdef EXTENDED 721 /* 722 * dopaste - include a given file without any 723 * macro processing. 724 */ 725 static int 726 dopaste(const char *pfile) 727 { 728 FILE *pf; 729 int c; 730 731 if ((pf = fopen(pfile, "r")) != NULL) { 732 if (synch_lines) 733 fprintf(active, "#line 1 \"%s\"\n", pfile); 734 while ((c = getc(pf)) != EOF) 735 putc(c, active); 736 (void) fclose(pf); 737 emit_synchline(); 738 return (1); 739 } else 740 return (0); 741 } 742 #endif 743 744 /* 745 * dochq - change quote characters 746 */ 747 static void 748 dochq(const char *argv[], int ac) 749 { 750 if (ac == 2) { 751 lquote[0] = LQUOTE; lquote[1] = EOS; 752 rquote[0] = RQUOTE; rquote[1] = EOS; 753 } else { 754 strlcpy(lquote, argv[2], sizeof(lquote)); 755 if (ac > 3) { 756 strlcpy(rquote, argv[3], sizeof(rquote)); 757 } else { 758 rquote[0] = ECOMMT; rquote[1] = EOS; 759 } 760 } 761 } 762 763 /* 764 * dochc - change comment characters 765 */ 766 static void 767 dochc(const char *argv[], int argc) 768 { 769 /* XXX Note that there is no difference between no argument and a single 770 * empty argument. 771 */ 772 if (argc == 2) { 773 scommt[0] = EOS; 774 ecommt[0] = EOS; 775 } else { 776 strlcpy(scommt, argv[2], sizeof(scommt)); 777 if (argc == 3) { 778 ecommt[0] = ECOMMT; ecommt[1] = EOS; 779 } else { 780 strlcpy(ecommt, argv[3], sizeof(ecommt)); 781 } 782 } 783 } 784 785 /* 786 * dom4wrap - expand text at EOF 787 */ 788 static void 789 dom4wrap(const char *text) 790 { 791 if (wrapindex >= maxwraps) { 792 if (maxwraps == 0) 793 maxwraps = 16; 794 else 795 maxwraps *= 2; 796 m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps), 797 "too many m4wraps"); 798 } 799 m4wraps[wrapindex++] = xstrdup(text); 800 } 801 802 /* 803 * dodivert - divert the output to a temporary file 804 */ 805 static void 806 dodiv(int n) 807 { 808 int fd; 809 810 oindex = n; 811 if (n >= maxout) { 812 if (mimic_gnu) 813 resizedivs(n + 10); 814 else 815 n = 0; /* bitbucket */ 816 } 817 818 if (n < 0) 819 n = 0; /* bitbucket */ 820 if (outfile[n] == NULL) { 821 char fname[] = _PATH_DIVNAME; 822 823 if ((fd = mkstemp(fname)) < 0 || 824 (outfile[n] = fdopen(fd, "w+")) == NULL) 825 err(1, "%s: cannot divert", fname); 826 if (unlink(fname) == -1) 827 err(1, "%s: cannot unlink", fname); 828 } 829 active = outfile[n]; 830 } 831 832 /* 833 * doundivert - undivert a specified output, or all 834 * other outputs, in numerical order. 835 */ 836 static void 837 doundiv(const char *argv[], int argc) 838 { 839 int ind; 840 int n; 841 842 if (argc > 2) { 843 for (ind = 2; ind < argc; ind++) { 844 const char *errstr; 845 n = strtonum(argv[ind], 1, INT_MAX, &errstr); 846 if (errstr) { 847 if (errno == EINVAL && mimic_gnu) 848 getdivfile(argv[ind]); 849 } else { 850 if (n < maxout && outfile[n] != NULL) 851 getdiv(n); 852 } 853 } 854 } 855 else 856 for (n = 1; n < maxout; n++) 857 if (outfile[n] != NULL) 858 getdiv(n); 859 } 860 861 /* 862 * dosub - select substring 863 */ 864 static void 865 dosub(const char *argv[], int argc) 866 { 867 const char *ap, *fc, *k; 868 int nc; 869 870 ap = argv[2]; /* target string */ 871 #ifdef EXPR 872 fc = ap + expr(argv[3]); /* first char */ 873 #else 874 fc = ap + atoi(argv[3]); /* first char */ 875 #endif 876 nc = strlen(fc); 877 if (argc >= 5) 878 #ifdef EXPR 879 nc = min(nc, expr(argv[4])); 880 #else 881 nc = min(nc, atoi(argv[4])); 882 #endif 883 if (fc >= ap && fc < ap + strlen(ap)) 884 for (k = fc + nc - 1; k >= fc; k--) 885 pushback(*k); 886 } 887 888 /* 889 * map: 890 * map every character of s1 that is specified in from 891 * into s3 and replace in s. (source s1 remains untouched) 892 * 893 * This is derived from the a standard implementation of map(s,from,to) 894 * function of ICON language. Within mapvec, we replace every character 895 * of "from" with the corresponding character in "to". 896 * If "to" is shorter than "from", than the corresponding entries are null, 897 * which means that those characters dissapear altogether. 898 */ 899 static void 900 map(char *dest, const char *src, const char *from, const char *to) 901 { 902 const char *tmp; 903 unsigned char sch, dch; 904 static char frombis[257]; 905 static char tobis[257]; 906 int i; 907 char seen[256]; 908 static unsigned char mapvec[256] = { 909 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 910 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 911 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 912 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 913 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 914 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 915 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 916 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 917 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 918 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 919 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 920 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 921 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 922 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 923 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 924 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 925 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 926 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 927 }; 928 929 if (*src) { 930 if (mimic_gnu) { 931 /* 932 * expand character ranges on the fly 933 */ 934 from = handledash(frombis, frombis + 256, from); 935 to = handledash(tobis, tobis + 256, to); 936 } 937 tmp = from; 938 /* 939 * create a mapping between "from" and 940 * "to" 941 */ 942 for (i = 0; i < 256; i++) 943 seen[i] = 0; 944 while (*from) { 945 if (!seen[(unsigned char)(*from)]) { 946 mapvec[(unsigned char)(*from)] = (unsigned char)(*to); 947 seen[(unsigned char)(*from)] = 1; 948 } 949 from++; 950 if (*to) 951 to++; 952 } 953 954 while (*src) { 955 sch = (unsigned char)(*src++); 956 dch = mapvec[sch]; 957 if ((*dest = (char)dch)) 958 dest++; 959 } 960 /* 961 * restore all the changed characters 962 */ 963 while (*tmp) { 964 mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp); 965 tmp++; 966 } 967 } 968 *dest = '\0'; 969 } 970 971 972 /* 973 * handledash: 974 * use buffer to copy the src string, expanding character ranges 975 * on the way. 976 */ 977 static const char * 978 handledash(char *buffer, char *end, const char *src) 979 { 980 char *p; 981 982 p = buffer; 983 while(*src) { 984 if (src[1] == '-' && src[2]) { 985 unsigned char i; 986 if ((unsigned char)src[0] <= (unsigned char)src[2]) { 987 for (i = (unsigned char)src[0]; 988 i <= (unsigned char)src[2]; i++) { 989 *p++ = i; 990 if (p == end) { 991 *p = '\0'; 992 return buffer; 993 } 994 } 995 } else { 996 for (i = (unsigned char)src[0]; 997 i >= (unsigned char)src[2]; i--) { 998 *p++ = i; 999 if (p == end) { 1000 *p = '\0'; 1001 return buffer; 1002 } 1003 } 1004 } 1005 src += 3; 1006 } else 1007 *p++ = *src++; 1008 if (p == end) 1009 break; 1010 } 1011 *p = '\0'; 1012 return buffer; 1013 } 1014