1 /* $OpenBSD: eval.c,v 1.66 2008/08/21 21:01:47 espie Exp $ */ 2 /* $NetBSD: eval.c,v 1.23 2015/01/29 19:26:20 christos 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 #if HAVE_NBTOOL_CONFIG_H 42 #include "nbtool_config.h" 43 #endif 44 #include <sys/cdefs.h> 45 __RCSID("$NetBSD: eval.c,v 1.23 2015/01/29 19:26:20 christos Exp $"); 46 47 #include <sys/types.h> 48 #include <err.h> 49 #include <errno.h> 50 #include <limits.h> 51 #include <unistd.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <stddef.h> 55 #include <stdint.h> 56 #include <string.h> 57 #include <inttypes.h> 58 #include <fcntl.h> 59 #include "mdef.h" 60 #include "stdd.h" 61 #include "extern.h" 62 #include "pathnames.h" 63 64 static void dodefn(const char *); 65 static void dopushdef(const char *, const char *); 66 static void dodump(const char *[], int); 67 static void dotrace(const char *[], int, int); 68 static void doifelse(const char *[], int); 69 static int doincl(const char *); 70 static int dopaste(const char *); 71 static void dochq(const char *[], int); 72 static void dochc(const char *[], int); 73 static void dom4wrap(const char *); 74 static void dodiv(int); 75 static void doundiv(const char *[], int); 76 static void dosub(const char *[], int); 77 static void map(char *, const char *, const char *, const char *); 78 static const char *handledash(char *, char *, const char *); 79 static void expand_builtin(const char *[], int, int); 80 static void expand_macro(const char *[], int); 81 static void dump_one_def(const char *, struct macro_definition *); 82 83 unsigned long expansion_id; 84 85 /* 86 * eval - eval all macros and builtins calls 87 * argc - number of elements in argv. 88 * argv - element vector : 89 * argv[0] = definition of a user 90 * macro or NULL if built-in. 91 * argv[1] = name of the macro or 92 * built-in. 93 * argv[2] = parameters to user-defined 94 * . macro or built-in. 95 * . 96 * 97 * A call in the form of macro-or-builtin() will result in: 98 * argv[0] = nullstr 99 * argv[1] = macro-or-builtin 100 * argv[2] = nullstr 101 * 102 * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin 103 */ 104 void 105 eval(const char *argv[], int argc, int td, int is_traced) 106 { 107 size_t mark = SIZE_MAX; 108 109 expansion_id++; 110 if (td & RECDEF) 111 m4errx(1, "expanding recursive definition for %s.", argv[1]); 112 if (is_traced) 113 mark = trace(argv, argc, infile+ilevel); 114 if (td == MACRTYPE) 115 expand_macro(argv, argc); 116 else 117 expand_builtin(argv, argc, td); 118 if (mark != SIZE_MAX) 119 finish_trace(mark); 120 } 121 122 /* 123 * expand_builtin - evaluate built-in macros. 124 */ 125 void 126 expand_builtin(const char *argv[], int argc, int td) 127 { 128 int c, n; 129 int ac; 130 static int sysval = 0; 131 132 #ifdef DEBUG 133 printf("argc = %d\n", argc); 134 for (n = 0; n < argc; n++) 135 printf("argv[%d] = %s\n", n, argv[n]); 136 fflush(stdout); 137 #endif 138 139 /* 140 * if argc == 3 and argv[2] is null, then we 141 * have macro-or-builtin() type call. We adjust 142 * argc to avoid further checking.. 143 */ 144 /* we keep the initial value for those built-ins that differentiate 145 * between builtin() and builtin. 146 */ 147 ac = argc; 148 149 if (argc == 3 && !*(argv[2]) && !mimic_gnu) 150 argc--; 151 152 switch (td & TYPEMASK) { 153 154 case DEFITYPE: 155 if (argc > 2) 156 dodefine(argv[2], (argc > 3) ? argv[3] : null); 157 break; 158 159 case PUSDTYPE: 160 if (argc > 2) 161 dopushdef(argv[2], (argc > 3) ? argv[3] : null); 162 break; 163 164 case DUMPTYPE: 165 dodump(argv, argc); 166 break; 167 168 case TRACEONTYPE: 169 dotrace(argv, argc, 1); 170 break; 171 172 case TRACEOFFTYPE: 173 dotrace(argv, argc, 0); 174 break; 175 176 case EXPRTYPE: 177 /* 178 * doexpr - evaluate arithmetic 179 * expression 180 */ 181 { 182 int base = 10; 183 int maxdigits = 0; 184 int e; 185 186 if (argc > 3) { 187 base = strtoi(argv[3], NULL, 0, 2, 36, &e); 188 if (e) { 189 m4errx(1, "expr: base %s invalid.", argv[3]); 190 } 191 } 192 if (argc > 4) { 193 maxdigits = strtoi(argv[4], NULL, 0, 0, INT_MAX, &e); 194 if (e) { 195 m4errx(1, "expr: maxdigits %s invalid.", argv[4]); 196 } 197 } 198 if (argc > 2) 199 pbnumbase(expr(argv[2]), base, maxdigits); 200 break; 201 } 202 203 case IFELTYPE: 204 if (argc > 4) 205 doifelse(argv, argc); 206 break; 207 208 case IFDFTYPE: 209 /* 210 * doifdef - select one of two 211 * alternatives based on the existence of 212 * another definition 213 */ 214 if (argc > 3) { 215 if (lookup_macro_definition(argv[2]) != NULL) 216 pbstr(argv[3]); 217 else if (argc > 4) 218 pbstr(argv[4]); 219 } 220 break; 221 222 case LENGTYPE: 223 /* 224 * dolen - find the length of the 225 * argument 226 */ 227 pbnum((argc > 2) ? strlen(argv[2]) : 0); 228 break; 229 230 case INCRTYPE: 231 /* 232 * doincr - increment the value of the 233 * argument 234 */ 235 if (argc > 2) 236 pbnum(atoi(argv[2]) + 1); 237 break; 238 239 case DECRTYPE: 240 /* 241 * dodecr - decrement the value of the 242 * argument 243 */ 244 if (argc > 2) 245 pbnum(atoi(argv[2]) - 1); 246 break; 247 248 case SYSCTYPE: 249 /* 250 * dosys - execute system command 251 */ 252 if (argc > 2) { 253 fflush(stdout); 254 sysval = system(argv[2]); 255 } 256 break; 257 258 case SYSVTYPE: 259 /* 260 * dosysval - return value of the last 261 * system call. 262 * 263 */ 264 pbnum(sysval); 265 break; 266 267 case ESYSCMDTYPE: 268 if (argc > 2) 269 doesyscmd(argv[2]); 270 break; 271 case INCLTYPE: 272 if (argc > 2) 273 if (!doincl(argv[2])) 274 err(1, "%s at line %lu: include(%s)", 275 CURRENT_NAME, CURRENT_LINE, argv[2]); 276 break; 277 278 case SINCTYPE: 279 if (argc > 2) 280 (void) doincl(argv[2]); 281 break; 282 #ifdef EXTENDED 283 case PASTTYPE: 284 if (argc > 2) 285 if (!dopaste(argv[2])) 286 err(1, "%s at line %lu: paste(%s)", 287 CURRENT_NAME, CURRENT_LINE, argv[2]); 288 break; 289 290 case SPASTYPE: 291 if (argc > 2) 292 (void) dopaste(argv[2]); 293 break; 294 case FORMATTYPE: 295 doformat(argv, argc); 296 break; 297 #endif 298 case CHNQTYPE: 299 dochq(argv, ac); 300 break; 301 302 case CHNCTYPE: 303 dochc(argv, argc); 304 break; 305 306 case SUBSTYPE: 307 /* 308 * dosub - select substring 309 * 310 */ 311 if (argc > 3) 312 dosub(argv, argc); 313 break; 314 315 case SHIFTYPE: 316 /* 317 * doshift - push back all arguments 318 * except the first one (i.e. skip 319 * argv[2]) 320 */ 321 if (argc > 3) { 322 for (n = argc - 1; n > 3; n--) { 323 pbstr(rquote); 324 pbstr(argv[n]); 325 pbstr(lquote); 326 pushback(COMMA); 327 } 328 pbstr(rquote); 329 pbstr(argv[3]); 330 pbstr(lquote); 331 } 332 break; 333 334 case DIVRTYPE: 335 if (argc > 2 && (n = atoi(argv[2])) != 0) 336 dodiv(n); 337 else { 338 active = stdout; 339 oindex = 0; 340 } 341 break; 342 343 case UNDVTYPE: 344 doundiv(argv, argc); 345 break; 346 347 case DIVNTYPE: 348 /* 349 * dodivnum - return the number of 350 * current output diversion 351 */ 352 pbnum(oindex); 353 break; 354 355 case UNDFTYPE: 356 /* 357 * doundefine - undefine a previously 358 * defined macro(s) or m4 keyword(s). 359 */ 360 if (argc > 2) 361 for (n = 2; n < argc; n++) 362 macro_undefine(argv[n]); 363 break; 364 365 case POPDTYPE: 366 /* 367 * dopopdef - remove the topmost 368 * definitions of macro(s) or m4 369 * keyword(s). 370 */ 371 if (argc > 2) 372 for (n = 2; n < argc; n++) 373 macro_popdef(argv[n]); 374 break; 375 376 case MKTMTYPE: 377 /* 378 * dotemp - create a temporary file 379 */ 380 if (argc > 2) { 381 int fd; 382 char *temp; 383 384 temp = xstrdup(argv[2]); 385 386 fd = mkstemp(temp); 387 if (fd == -1) 388 err(1, 389 "%s at line %lu: couldn't make temp file %s", 390 CURRENT_NAME, CURRENT_LINE, argv[2]); 391 close(fd); 392 pbstr(temp); 393 free(temp); 394 } 395 break; 396 397 case TRNLTYPE: 398 /* 399 * dotranslit - replace all characters in 400 * the source string that appears in the 401 * "from" string with the corresponding 402 * characters in the "to" string. 403 */ 404 if (argc > 3) { 405 char *temp; 406 407 temp = xalloc(strlen(argv[2])+1, NULL); 408 if (argc > 4) 409 map(temp, argv[2], argv[3], argv[4]); 410 else 411 map(temp, argv[2], argv[3], null); 412 pbstr(temp); 413 free(temp); 414 } else if (argc > 2) 415 pbstr(argv[2]); 416 break; 417 418 case INDXTYPE: 419 /* 420 * doindex - find the index of the second 421 * argument string in the first argument 422 * string. -1 if not present. 423 */ 424 pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); 425 break; 426 427 case ERRPTYPE: 428 /* 429 * doerrp - print the arguments to stderr 430 * file 431 */ 432 if (argc > 2) { 433 for (n = 2; n < argc; n++) 434 fprintf(stderr, "%s%s", 435 mimic_gnu && n == 2 ? "" : " ", 436 argv[n]); 437 if (!mimic_gnu) 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 int e; 845 n = strtoi(argv[ind], NULL, 0, 1, INT_MAX, &e); 846 if (e) { 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 a standard implementation of map(s,from,to) function of ICON 894 * language. Within mapvec, we replace every character of "from" with 895 * the corresponding character in "to". If "to" is shorter than "from", 896 * than the corresponding entries are null, which means that those 897 * characters dissapear altogether. Furthermore, imagine 898 * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, 899 * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' 900 * ultimately maps to `*'. In order to achieve this effect in an efficient 901 * manner (i.e. without multiple passes over the destination string), we 902 * loop over mapvec, starting with the initial source character. if the 903 * character value (dch) in this location is different than the source 904 * character (sch), sch becomes dch, once again to index into mapvec, until 905 * the character value stabilizes (i.e. sch = dch, in other words 906 * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary 907 * character, it will stabilize, since mapvec[0] == 0 at all times. At the 908 * end, we restore mapvec* back to normal where mapvec[n] == n for 909 * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is 910 * about 5 times faster than any algorithm that makes multiple passes over 911 * destination string. 912 */ 913 static void 914 map(char *dest, const char *src, const char *from, const char *to) 915 { 916 const char *tmp; 917 unsigned char sch, dch; 918 static char frombis[257]; 919 static char tobis[257]; 920 static unsigned char mapvec[256] = { 921 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 922 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 923 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 924 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 925 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 926 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 927 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 928 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 929 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 930 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 931 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 932 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 933 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 934 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 935 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 936 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 937 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 938 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 939 }; 940 941 if (*src) { 942 if (mimic_gnu) { 943 /* 944 * expand character ranges on the fly 945 */ 946 from = handledash(frombis, frombis + 256, from); 947 to = handledash(tobis, tobis + 256, to); 948 } 949 tmp = from; 950 /* 951 * create a mapping between "from" and 952 * "to" 953 */ 954 while (*from) 955 mapvec[(unsigned char)(*from++)] = (*to) ? 956 (unsigned char)(*to++) : 0; 957 958 while (*src) { 959 sch = (unsigned char)(*src++); 960 dch = mapvec[sch]; 961 while (dch != sch) { 962 sch = dch; 963 dch = mapvec[sch]; 964 } 965 if ((*dest = (char)dch)) 966 dest++; 967 } 968 /* 969 * restore all the changed characters 970 */ 971 while (*tmp) { 972 mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp); 973 tmp++; 974 } 975 } 976 *dest = '\0'; 977 } 978 979 980 /* 981 * handledash: 982 * use buffer to copy the src string, expanding character ranges 983 * on the way. 984 */ 985 static const char * 986 handledash(char *buffer, char *end, const char *src) 987 { 988 char *p; 989 990 p = buffer; 991 while(*src) { 992 if (src[1] == '-' && src[2]) { 993 unsigned char i; 994 if ((unsigned char)src[0] <= (unsigned char)src[2]) { 995 for (i = (unsigned char)src[0]; 996 i <= (unsigned char)src[2]; i++) { 997 *p++ = i; 998 if (p == end) { 999 *p = '\0'; 1000 return buffer; 1001 } 1002 } 1003 } else { 1004 for (i = (unsigned char)src[0]; 1005 i >= (unsigned char)src[2]; i--) { 1006 *p++ = i; 1007 if (p == end) { 1008 *p = '\0'; 1009 return buffer; 1010 } 1011 } 1012 } 1013 src += 3; 1014 } else 1015 *p++ = *src++; 1016 if (p == end) 1017 break; 1018 } 1019 *p = '\0'; 1020 return buffer; 1021 } 1022