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