1 /* $OpenBSD: eval.c,v 1.66 2008/08/21 21:01:47 espie Exp $ */ 2 /* $NetBSD: eval.c,v 1.21 2011/03/05 16:38:25 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.21 2011/03/05 16:38:25 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 <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 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%s", 433 mimic_gnu && n == 2 ? "" : " ", 434 argv[n]); 435 if (!mimic_gnu) 436 fprintf(stderr, "\n"); 437 } 438 break; 439 440 case DNLNTYPE: 441 /* 442 * dodnl - eat-up-to and including 443 * newline 444 */ 445 while ((c = gpbc()) != '\n' && c != EOF) 446 ; 447 break; 448 449 case M4WRTYPE: 450 /* 451 * dom4wrap - set up for 452 * wrap-up/wind-down activity 453 */ 454 if (argc > 2) 455 dom4wrap(argv[2]); 456 break; 457 458 case EXITTYPE: 459 /* 460 * doexit - immediate exit from m4. 461 */ 462 killdiv(); 463 exit((argc > 2) ? atoi(argv[2]) : 0); 464 break; 465 466 case DEFNTYPE: 467 if (argc > 2) 468 for (n = 2; n < argc; n++) 469 dodefn(argv[n]); 470 break; 471 472 case INDIRTYPE: /* Indirect call */ 473 if (argc > 2) 474 doindir(argv, argc); 475 break; 476 477 case BUILTINTYPE: /* Builtins only */ 478 if (argc > 2) 479 dobuiltin(argv, argc); 480 break; 481 482 case PATSTYPE: 483 if (argc > 2) 484 dopatsubst(argv, argc); 485 break; 486 case REGEXPTYPE: 487 if (argc > 2) 488 doregexp(argv, argc); 489 break; 490 case LINETYPE: 491 doprintlineno(infile+ilevel); 492 break; 493 case FILENAMETYPE: 494 doprintfilename(infile+ilevel); 495 break; 496 case SELFTYPE: 497 pbstr(rquote); 498 pbstr(argv[1]); 499 pbstr(lquote); 500 break; 501 default: 502 m4errx(1, "eval: major botch."); 503 break; 504 } 505 } 506 507 /* 508 * expand_macro - user-defined macro expansion 509 */ 510 void 511 expand_macro(const char *argv[], int argc) 512 { 513 const char *t; 514 const char *p; 515 int n; 516 int argno; 517 518 t = argv[0]; /* defn string as a whole */ 519 p = t; 520 while (*p) 521 p++; 522 p--; /* last character of defn */ 523 while (p > t) { 524 if (*(p - 1) != ARGFLAG) 525 PUSHBACK(*p); 526 else { 527 switch (*p) { 528 529 case '#': 530 pbnum(argc - 2); 531 break; 532 case '0': 533 case '1': 534 case '2': 535 case '3': 536 case '4': 537 case '5': 538 case '6': 539 case '7': 540 case '8': 541 case '9': 542 if ((argno = *p - '0') < argc - 1) 543 pbstr(argv[argno + 1]); 544 break; 545 case '*': 546 if (argc > 2) { 547 for (n = argc - 1; n > 2; n--) { 548 pbstr(argv[n]); 549 pushback(COMMA); 550 } 551 pbstr(argv[2]); 552 } 553 break; 554 case '@': 555 if (argc > 2) { 556 for (n = argc - 1; n > 2; n--) { 557 pbstr(rquote); 558 pbstr(argv[n]); 559 pbstr(lquote); 560 pushback(COMMA); 561 } 562 pbstr(rquote); 563 pbstr(argv[2]); 564 pbstr(lquote); 565 } 566 break; 567 default: 568 PUSHBACK(*p); 569 PUSHBACK('$'); 570 break; 571 } 572 p--; 573 } 574 p--; 575 } 576 if (p == t) /* do last character */ 577 PUSHBACK(*p); 578 } 579 580 581 /* 582 * dodefine - install definition in the table 583 */ 584 void 585 dodefine(const char *name, const char *defn) 586 { 587 if (!*name && !mimic_gnu) 588 m4errx(1, "null definition."); 589 else 590 macro_define(name, defn); 591 } 592 593 /* 594 * dodefn - push back a quoted definition of 595 * the given name. 596 */ 597 static void 598 dodefn(const char *name) 599 { 600 struct macro_definition *p; 601 602 if ((p = lookup_macro_definition(name)) != NULL) { 603 if ((p->type & TYPEMASK) == MACRTYPE) { 604 pbstr(rquote); 605 pbstr(p->defn); 606 pbstr(lquote); 607 } else { 608 pbstr(p->defn); 609 pbstr(BUILTIN_MARKER); 610 } 611 } 612 } 613 614 /* 615 * dopushdef - install a definition in the hash table 616 * without removing a previous definition. Since 617 * each new entry is entered in *front* of the 618 * hash bucket, it hides a previous definition from 619 * lookup. 620 */ 621 static void 622 dopushdef(const char *name, const char *defn) 623 { 624 if (!*name && !mimic_gnu) 625 m4errx(1, "null definition."); 626 else 627 macro_pushdef(name, defn); 628 } 629 630 /* 631 * dump_one_def - dump the specified definition. 632 */ 633 static void 634 dump_one_def(const char *name, struct macro_definition *p) 635 { 636 if (!traceout) 637 traceout = stderr; 638 if (mimic_gnu) { 639 if ((p->type & TYPEMASK) == MACRTYPE) 640 fprintf(traceout, "%s:\t%s\n", name, p->defn); 641 else { 642 fprintf(traceout, "%s:\t<%s>\n", name, p->defn); 643 } 644 } else 645 fprintf(traceout, "`%s'\t`%s'\n", name, p->defn); 646 } 647 648 /* 649 * dodumpdef - dump the specified definitions in the hash 650 * table to stderr. If nothing is specified, the entire 651 * hash table is dumped. 652 */ 653 static void 654 dodump(const char *argv[], int argc) 655 { 656 int n; 657 struct macro_definition *p; 658 659 if (argc > 2) { 660 for (n = 2; n < argc; n++) 661 if ((p = lookup_macro_definition(argv[n])) != NULL) 662 dump_one_def(argv[n], p); 663 } else 664 macro_for_all(dump_one_def); 665 } 666 667 /* 668 * dotrace - mark some macros as traced/untraced depending upon on. 669 */ 670 static void 671 dotrace(const char *argv[], int argc, int on) 672 { 673 int n; 674 675 if (argc > 2) { 676 for (n = 2; n < argc; n++) 677 mark_traced(argv[n], on); 678 } else 679 mark_traced(NULL, on); 680 } 681 682 /* 683 * doifelse - select one of two alternatives - loop. 684 */ 685 static void 686 doifelse(const char *argv[], int argc) 687 { 688 cycle { 689 if (STREQ(argv[2], argv[3])) 690 pbstr(argv[4]); 691 else if (argc == 6) 692 pbstr(argv[5]); 693 else if (argc > 6) { 694 argv += 3; 695 argc -= 3; 696 continue; 697 } 698 break; 699 } 700 } 701 702 /* 703 * doinclude - include a given file. 704 */ 705 static int 706 doincl(const char *ifile) 707 { 708 if (ilevel + 1 == MAXINP) 709 m4errx(1, "too many include files."); 710 if (fopen_trypath(infile+ilevel+1, ifile) != NULL) { 711 ilevel++; 712 bbase[ilevel] = bufbase = bp; 713 return (1); 714 } else 715 return (0); 716 } 717 718 #ifdef EXTENDED 719 /* 720 * dopaste - include a given file without any 721 * macro processing. 722 */ 723 static int 724 dopaste(const char *pfile) 725 { 726 FILE *pf; 727 int c; 728 729 if ((pf = fopen(pfile, "r")) != NULL) { 730 if (synch_lines) 731 fprintf(active, "#line 1 \"%s\"\n", pfile); 732 while ((c = getc(pf)) != EOF) 733 putc(c, active); 734 (void) fclose(pf); 735 emit_synchline(); 736 return (1); 737 } else 738 return (0); 739 } 740 #endif 741 742 /* 743 * dochq - change quote characters 744 */ 745 static void 746 dochq(const char *argv[], int ac) 747 { 748 if (ac == 2) { 749 lquote[0] = LQUOTE; lquote[1] = EOS; 750 rquote[0] = RQUOTE; rquote[1] = EOS; 751 } else { 752 strlcpy(lquote, argv[2], sizeof(lquote)); 753 if (ac > 3) { 754 strlcpy(rquote, argv[3], sizeof(rquote)); 755 } else { 756 rquote[0] = ECOMMT; rquote[1] = EOS; 757 } 758 } 759 } 760 761 /* 762 * dochc - change comment characters 763 */ 764 static void 765 dochc(const char *argv[], int argc) 766 { 767 /* XXX Note that there is no difference between no argument and a single 768 * empty argument. 769 */ 770 if (argc == 2) { 771 scommt[0] = EOS; 772 ecommt[0] = EOS; 773 } else { 774 strlcpy(scommt, argv[2], sizeof(scommt)); 775 if (argc == 3) { 776 ecommt[0] = ECOMMT; ecommt[1] = EOS; 777 } else { 778 strlcpy(ecommt, argv[3], sizeof(ecommt)); 779 } 780 } 781 } 782 783 /* 784 * dom4wrap - expand text at EOF 785 */ 786 static void 787 dom4wrap(const char *text) 788 { 789 if (wrapindex >= maxwraps) { 790 if (maxwraps == 0) 791 maxwraps = 16; 792 else 793 maxwraps *= 2; 794 m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps), 795 "too many m4wraps"); 796 } 797 m4wraps[wrapindex++] = xstrdup(text); 798 } 799 800 /* 801 * dodivert - divert the output to a temporary file 802 */ 803 static void 804 dodiv(int n) 805 { 806 int fd; 807 808 oindex = n; 809 if (n >= maxout) { 810 if (mimic_gnu) 811 resizedivs(n + 10); 812 else 813 n = 0; /* bitbucket */ 814 } 815 816 if (n < 0) 817 n = 0; /* bitbucket */ 818 if (outfile[n] == NULL) { 819 char fname[] = _PATH_DIVNAME; 820 821 if ((fd = mkstemp(fname)) < 0 || 822 (outfile[n] = fdopen(fd, "w+")) == NULL) 823 err(1, "%s: cannot divert", fname); 824 if (unlink(fname) == -1) 825 err(1, "%s: cannot unlink", fname); 826 } 827 active = outfile[n]; 828 } 829 830 /* 831 * doundivert - undivert a specified output, or all 832 * other outputs, in numerical order. 833 */ 834 static void 835 doundiv(const char *argv[], int argc) 836 { 837 int ind; 838 int n; 839 840 if (argc > 2) { 841 for (ind = 2; ind < argc; ind++) { 842 const char *errstr; 843 n = strtonum(argv[ind], 1, INT_MAX, &errstr); 844 if (errstr) { 845 if (errno == EINVAL && mimic_gnu) 846 getdivfile(argv[ind]); 847 } else { 848 if (n < maxout && outfile[n] != NULL) 849 getdiv(n); 850 } 851 } 852 } 853 else 854 for (n = 1; n < maxout; n++) 855 if (outfile[n] != NULL) 856 getdiv(n); 857 } 858 859 /* 860 * dosub - select substring 861 */ 862 static void 863 dosub(const char *argv[], int argc) 864 { 865 const char *ap, *fc, *k; 866 int nc; 867 868 ap = argv[2]; /* target string */ 869 #ifdef EXPR 870 fc = ap + expr(argv[3]); /* first char */ 871 #else 872 fc = ap + atoi(argv[3]); /* first char */ 873 #endif 874 nc = strlen(fc); 875 if (argc >= 5) 876 #ifdef EXPR 877 nc = min(nc, expr(argv[4])); 878 #else 879 nc = min(nc, atoi(argv[4])); 880 #endif 881 if (fc >= ap && fc < ap + strlen(ap)) 882 for (k = fc + nc - 1; k >= fc; k--) 883 pushback(*k); 884 } 885 886 /* 887 * map: 888 * map every character of s1 that is specified in from 889 * into s3 and replace in s. (source s1 remains untouched) 890 * 891 * This is a standard implementation of map(s,from,to) function of ICON 892 * language. Within mapvec, we replace every character of "from" with 893 * the corresponding character in "to". If "to" is shorter than "from", 894 * than the corresponding entries are null, which means that those 895 * characters dissapear altogether. Furthermore, imagine 896 * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, 897 * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' 898 * ultimately maps to `*'. In order to achieve this effect in an efficient 899 * manner (i.e. without multiple passes over the destination string), we 900 * loop over mapvec, starting with the initial source character. if the 901 * character value (dch) in this location is different than the source 902 * character (sch), sch becomes dch, once again to index into mapvec, until 903 * the character value stabilizes (i.e. sch = dch, in other words 904 * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary 905 * character, it will stabilize, since mapvec[0] == 0 at all times. At the 906 * end, we restore mapvec* back to normal where mapvec[n] == n for 907 * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is 908 * about 5 times faster than any algorithm that makes multiple passes over 909 * destination string. 910 */ 911 static void 912 map(char *dest, const char *src, const char *from, const char *to) 913 { 914 const char *tmp; 915 unsigned char sch, dch; 916 static char frombis[257]; 917 static char tobis[257]; 918 static unsigned char mapvec[256] = { 919 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 920 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 921 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 922 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 923 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 924 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 925 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 926 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 927 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 928 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 929 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 930 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 931 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 932 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 933 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 934 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 935 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 936 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 937 }; 938 939 if (*src) { 940 if (mimic_gnu) { 941 /* 942 * expand character ranges on the fly 943 */ 944 from = handledash(frombis, frombis + 256, from); 945 to = handledash(tobis, tobis + 256, to); 946 } 947 tmp = from; 948 /* 949 * create a mapping between "from" and 950 * "to" 951 */ 952 while (*from) 953 mapvec[(unsigned char)(*from++)] = (*to) ? 954 (unsigned char)(*to++) : 0; 955 956 while (*src) { 957 sch = (unsigned char)(*src++); 958 dch = mapvec[sch]; 959 while (dch != sch) { 960 sch = dch; 961 dch = mapvec[sch]; 962 } 963 if ((*dest = (char)dch)) 964 dest++; 965 } 966 /* 967 * restore all the changed characters 968 */ 969 while (*tmp) { 970 mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp); 971 tmp++; 972 } 973 } 974 *dest = '\0'; 975 } 976 977 978 /* 979 * handledash: 980 * use buffer to copy the src string, expanding character ranges 981 * on the way. 982 */ 983 static const char * 984 handledash(char *buffer, char *end, const char *src) 985 { 986 char *p; 987 988 p = buffer; 989 while(*src) { 990 if (src[1] == '-' && src[2]) { 991 unsigned char i; 992 if ((unsigned char)src[0] <= (unsigned char)src[2]) { 993 for (i = (unsigned char)src[0]; 994 i <= (unsigned char)src[2]; i++) { 995 *p++ = i; 996 if (p == end) { 997 *p = '\0'; 998 return buffer; 999 } 1000 } 1001 } else { 1002 for (i = (unsigned char)src[0]; 1003 i >= (unsigned char)src[2]; i--) { 1004 *p++ = i; 1005 if (p == end) { 1006 *p = '\0'; 1007 return buffer; 1008 } 1009 } 1010 } 1011 src += 3; 1012 } else 1013 *p++ = *src++; 1014 if (p == end) 1015 break; 1016 } 1017 *p = '\0'; 1018 return buffer; 1019 } 1020