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