1 /* $OpenBSD: eval.c,v 1.76 2017/10/23 15:21:19 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 /* 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 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 if (mimic_gnu) { 268 warn("%s at line %lu: include(%s)", 269 CURRENT_NAME, CURRENT_LINE, argv[2]); 270 exit_code = 1; 271 if (fatal_warns) { 272 killdiv(); 273 exit(exit_code); 274 } 275 } else 276 err(1, "%s at line %lu: include(%s)", 277 CURRENT_NAME, CURRENT_LINE, argv[2]); 278 } 279 } 280 break; 281 282 case SINCTYPE: 283 if (argc > 2) 284 (void) doincl(argv[2]); 285 break; 286 #ifdef EXTENDED 287 case PASTTYPE: 288 if (argc > 2) 289 if (!dopaste(argv[2])) 290 err(1, "%s at line %lu: paste(%s)", 291 CURRENT_NAME, CURRENT_LINE, argv[2]); 292 break; 293 294 case SPASTYPE: 295 if (argc > 2) 296 (void) dopaste(argv[2]); 297 break; 298 case FORMATTYPE: 299 doformat(argv, argc); 300 break; 301 #endif 302 case CHNQTYPE: 303 dochq(argv, ac); 304 break; 305 306 case CHNCTYPE: 307 dochc(argv, argc); 308 break; 309 310 case SUBSTYPE: 311 /* 312 * dosub - select substring 313 * 314 */ 315 if (argc > 3) 316 dosub(argv, argc); 317 break; 318 319 case SHIFTYPE: 320 /* 321 * doshift - push back all arguments 322 * except the first one (i.e. skip 323 * argv[2]) 324 */ 325 if (argc > 3) { 326 for (n = argc - 1; n > 3; n--) { 327 pbstr(rquote); 328 pbstr(argv[n]); 329 pbstr(lquote); 330 pushback(COMMA); 331 } 332 pbstr(rquote); 333 pbstr(argv[3]); 334 pbstr(lquote); 335 } 336 break; 337 338 case DIVRTYPE: 339 if (argc > 2 && (n = atoi(argv[2])) != 0) 340 dodiv(n); 341 else { 342 active = stdout; 343 oindex = 0; 344 } 345 break; 346 347 case UNDVTYPE: 348 doundiv(argv, argc); 349 break; 350 351 case DIVNTYPE: 352 /* 353 * dodivnum - return the number of 354 * current output diversion 355 */ 356 pbnum(oindex); 357 break; 358 359 case UNDFTYPE: 360 /* 361 * doundefine - undefine a previously 362 * defined macro(s) or m4 keyword(s). 363 */ 364 if (argc > 2) 365 for (n = 2; n < argc; n++) 366 macro_undefine(argv[n]); 367 break; 368 369 case POPDTYPE: 370 /* 371 * dopopdef - remove the topmost 372 * definitions of macro(s) or m4 373 * keyword(s). 374 */ 375 if (argc > 2) 376 for (n = 2; n < argc; n++) 377 macro_popdef(argv[n]); 378 break; 379 380 case MKTMTYPE: 381 /* 382 * dotemp - create a temporary file 383 */ 384 if (argc > 2) { 385 int fd; 386 char *temp; 387 388 temp = xstrdup(argv[2]); 389 390 fd = mkstemp(temp); 391 if (fd == -1) 392 err(1, 393 "%s at line %lu: couldn't make temp file %s", 394 CURRENT_NAME, CURRENT_LINE, argv[2]); 395 close(fd); 396 pbstr(temp); 397 free(temp); 398 } 399 break; 400 401 case TRNLTYPE: 402 /* 403 * dotranslit - replace all characters in 404 * the source string that appears in the 405 * "from" string with the corresponding 406 * characters in the "to" string. 407 */ 408 if (argc > 3) { 409 char *temp; 410 411 temp = xalloc(strlen(argv[2])+1, NULL); 412 if (argc > 4) 413 map(temp, argv[2], argv[3], argv[4]); 414 else 415 map(temp, argv[2], argv[3], null); 416 pbstr(temp); 417 free(temp); 418 } else if (argc > 2) 419 pbstr(argv[2]); 420 break; 421 422 case INDXTYPE: 423 /* 424 * doindex - find the index of the second 425 * argument string in the first argument 426 * string. -1 if not present. 427 */ 428 pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); 429 break; 430 431 case ERRPTYPE: 432 /* 433 * doerrp - print the arguments to stderr 434 * file 435 */ 436 if (argc > 2) { 437 for (n = 2; n < argc; n++) 438 fprintf(stderr, "%s ", argv[n]); 439 fprintf(stderr, "\n"); 440 } 441 break; 442 443 case DNLNTYPE: 444 /* 445 * dodnl - eat-up-to and including 446 * newline 447 */ 448 while ((c = gpbc()) != '\n' && c != EOF) 449 ; 450 break; 451 452 case M4WRTYPE: 453 /* 454 * dom4wrap - set up for 455 * wrap-up/wind-down activity 456 */ 457 if (argc > 2) 458 dom4wrap(argv[2]); 459 break; 460 461 case EXITTYPE: 462 /* 463 * doexit - immediate exit from m4. 464 */ 465 killdiv(); 466 exit((argc > 2) ? atoi(argv[2]) : 0); 467 break; 468 469 case DEFNTYPE: 470 if (argc > 2) 471 for (n = 2; n < argc; n++) 472 dodefn(argv[n]); 473 break; 474 475 case INDIRTYPE: /* Indirect call */ 476 if (argc > 2) 477 doindir(argv, argc); 478 break; 479 480 case BUILTINTYPE: /* Builtins only */ 481 if (argc > 2) 482 dobuiltin(argv, argc); 483 break; 484 485 case PATSTYPE: 486 if (argc > 2) 487 dopatsubst(argv, argc); 488 break; 489 case REGEXPTYPE: 490 if (argc > 2) 491 doregexp(argv, argc); 492 break; 493 case LINETYPE: 494 doprintlineno(infile+ilevel); 495 break; 496 case FILENAMETYPE: 497 doprintfilename(infile+ilevel); 498 break; 499 case SELFTYPE: 500 pbstr(rquote); 501 pbstr(argv[1]); 502 pbstr(lquote); 503 break; 504 default: 505 m4errx(1, "eval: major botch."); 506 break; 507 } 508 } 509 510 /* 511 * expand_macro - user-defined macro expansion 512 */ 513 void 514 expand_macro(const char *argv[], int argc) 515 { 516 const char *t; 517 const char *p; 518 int n; 519 int argno; 520 521 t = argv[0]; /* defn string as a whole */ 522 p = t; 523 while (*p) 524 p++; 525 p--; /* last character of defn */ 526 while (p > t) { 527 if (*(p - 1) != ARGFLAG) 528 PUSHBACK(*p); 529 else { 530 switch (*p) { 531 532 case '#': 533 pbnum(argc - 2); 534 break; 535 case '0': 536 case '1': 537 case '2': 538 case '3': 539 case '4': 540 case '5': 541 case '6': 542 case '7': 543 case '8': 544 case '9': 545 if ((argno = *p - '0') < argc - 1) 546 pbstr(argv[argno + 1]); 547 break; 548 case '*': 549 if (argc > 2) { 550 for (n = argc - 1; n > 2; n--) { 551 pbstr(argv[n]); 552 pushback(COMMA); 553 } 554 pbstr(argv[2]); 555 } 556 break; 557 case '@': 558 if (argc > 2) { 559 for (n = argc - 1; n > 2; n--) { 560 pbstr(rquote); 561 pbstr(argv[n]); 562 pbstr(lquote); 563 pushback(COMMA); 564 } 565 pbstr(rquote); 566 pbstr(argv[2]); 567 pbstr(lquote); 568 } 569 break; 570 default: 571 PUSHBACK(*p); 572 PUSHBACK('$'); 573 break; 574 } 575 p--; 576 } 577 p--; 578 } 579 if (p == t) /* do last character */ 580 PUSHBACK(*p); 581 } 582 583 584 /* 585 * dodefine - install definition in the table 586 */ 587 void 588 dodefine(const char *name, const char *defn) 589 { 590 if (!*name && !mimic_gnu) 591 m4errx(1, "null definition."); 592 else 593 macro_define(name, defn); 594 } 595 596 /* 597 * dodefn - push back a quoted definition of 598 * the given name. 599 */ 600 static void 601 dodefn(const char *name) 602 { 603 struct macro_definition *p; 604 605 if ((p = lookup_macro_definition(name)) != NULL) { 606 if ((p->type & TYPEMASK) == MACRTYPE) { 607 pbstr(rquote); 608 pbstr(p->defn); 609 pbstr(lquote); 610 } else { 611 pbstr(p->defn); 612 pbstr(BUILTIN_MARKER); 613 } 614 } 615 } 616 617 /* 618 * dopushdef - install a definition in the hash table 619 * without removing a previous definition. Since 620 * each new entry is entered in *front* of the 621 * hash bucket, it hides a previous definition from 622 * lookup. 623 */ 624 static void 625 dopushdef(const char *name, const char *defn) 626 { 627 if (!*name && !mimic_gnu) 628 m4errx(1, "null definition."); 629 else 630 macro_pushdef(name, defn); 631 } 632 633 /* 634 * dump_one_def - dump the specified definition. 635 */ 636 static void 637 dump_one_def(const char *name, struct macro_definition *p) 638 { 639 if (!traceout) 640 traceout = stderr; 641 if (mimic_gnu) { 642 if ((p->type & TYPEMASK) == MACRTYPE) 643 fprintf(traceout, "%s:\t%s\n", name, p->defn); 644 else { 645 fprintf(traceout, "%s:\t<%s>\n", name, p->defn); 646 } 647 } else 648 fprintf(traceout, "`%s'\t`%s'\n", name, p->defn); 649 } 650 651 /* 652 * dodumpdef - dump the specified definitions in the hash 653 * table to stderr. If nothing is specified, the entire 654 * hash table is dumped. 655 */ 656 static void 657 dodump(const char *argv[], int argc) 658 { 659 int n; 660 struct macro_definition *p; 661 662 if (argc > 2) { 663 for (n = 2; n < argc; n++) 664 if ((p = lookup_macro_definition(argv[n])) != NULL) 665 dump_one_def(argv[n], p); 666 } else 667 macro_for_all(dump_one_def); 668 } 669 670 /* 671 * dotrace - mark some macros as traced/untraced depending upon on. 672 */ 673 static void 674 dotrace(const char *argv[], int argc, int on) 675 { 676 int n; 677 678 if (argc > 2) { 679 for (n = 2; n < argc; n++) 680 mark_traced(argv[n], on); 681 } else 682 mark_traced(NULL, on); 683 } 684 685 /* 686 * doifelse - select one of two alternatives - loop. 687 */ 688 static void 689 doifelse(const char *argv[], int argc) 690 { 691 while (argc > 4) { 692 if (STREQ(argv[2], argv[3])) { 693 pbstr(argv[4]); 694 break; 695 } else if (argc == 6) { 696 pbstr(argv[5]); 697 break; 698 } else { 699 argv += 3; 700 argc -= 3; 701 } 702 } 703 } 704 705 /* 706 * doinclude - include a given file. 707 */ 708 static int 709 doincl(const char *ifile) 710 { 711 if (ilevel + 1 == MAXINP) 712 m4errx(1, "too many include files."); 713 if (fopen_trypath(infile+ilevel+1, ifile) != NULL) { 714 ilevel++; 715 bbase[ilevel] = bufbase = bp; 716 return (1); 717 } else 718 return (0); 719 } 720 721 #ifdef EXTENDED 722 /* 723 * dopaste - include a given file without any 724 * macro processing. 725 */ 726 static int 727 dopaste(const char *pfile) 728 { 729 FILE *pf; 730 int c; 731 732 if ((pf = fopen(pfile, "r")) != NULL) { 733 if (synch_lines) 734 fprintf(active, "#line 1 \"%s\"\n", pfile); 735 while ((c = getc(pf)) != EOF) 736 putc(c, active); 737 (void) fclose(pf); 738 emit_synchline(); 739 return (1); 740 } else 741 return (0); 742 } 743 #endif 744 745 /* 746 * dochq - change quote characters 747 */ 748 static void 749 dochq(const char *argv[], int ac) 750 { 751 if (ac == 2) { 752 lquote[0] = LQUOTE; lquote[1] = EOS; 753 rquote[0] = RQUOTE; rquote[1] = EOS; 754 } else { 755 strlcpy(lquote, argv[2], sizeof(lquote)); 756 if (ac > 3) { 757 strlcpy(rquote, argv[3], sizeof(rquote)); 758 } else { 759 rquote[0] = ECOMMT; rquote[1] = EOS; 760 } 761 } 762 } 763 764 /* 765 * dochc - change comment characters 766 */ 767 static void 768 dochc(const char *argv[], int argc) 769 { 770 /* XXX Note that there is no difference between no argument and a single 771 * empty argument. 772 */ 773 if (argc == 2) { 774 scommt[0] = EOS; 775 ecommt[0] = EOS; 776 } else { 777 strlcpy(scommt, argv[2], sizeof(scommt)); 778 if (argc == 3) { 779 ecommt[0] = ECOMMT; ecommt[1] = EOS; 780 } else { 781 strlcpy(ecommt, argv[3], sizeof(ecommt)); 782 } 783 } 784 } 785 786 /* 787 * dom4wrap - expand text at EOF 788 */ 789 static void 790 dom4wrap(const char *text) 791 { 792 if (wrapindex >= maxwraps) { 793 if (maxwraps == 0) 794 maxwraps = 16; 795 else 796 maxwraps *= 2; 797 m4wraps = xreallocarray(m4wraps, maxwraps, sizeof(*m4wraps), 798 "too many m4wraps"); 799 } 800 m4wraps[wrapindex++] = xstrdup(text); 801 } 802 803 /* 804 * dodivert - divert the output to a temporary file 805 */ 806 static void 807 dodiv(int n) 808 { 809 int fd; 810 811 oindex = n; 812 if (n >= maxout) { 813 if (mimic_gnu) 814 resizedivs(n + 10); 815 else 816 n = 0; /* bitbucket */ 817 } 818 819 if (n < 0) 820 n = 0; /* bitbucket */ 821 if (outfile[n] == NULL) { 822 char fname[] = _PATH_DIVNAME; 823 824 if ((fd = mkstemp(fname)) < 0 || 825 unlink(fname) == -1 || 826 (outfile[n] = fdopen(fd, "w+")) == NULL) 827 err(1, "%s: cannot divert", 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