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