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