1 /* $OpenBSD: eval.c,v 1.50 2003/06/30 21:47:21 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 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)eval.c 8.2 (Berkeley) 4/27/95"; 39 #else 40 static char rcsid[] = "$OpenBSD: eval.c,v 1.50 2003/06/30 21:47:21 espie Exp $"; 41 #endif 42 #endif /* not lint */ 43 44 /* 45 * eval.c 46 * Facility: m4 macro processor 47 * by: oz 48 */ 49 50 #include <sys/types.h> 51 #include <errno.h> 52 #include <unistd.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <stddef.h> 56 #include <string.h> 57 #include <fcntl.h> 58 #include <err.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 gnu_dochq(const char *[], int); 72 static void dochq(const char *[], int); 73 static void gnu_dochc(const char *[], int); 74 static void dochc(const char *[], int); 75 static void dodiv(int); 76 static void doundiv(const char *[], int); 77 static void dosub(const char *[], int); 78 static void map(char *, const char *, const char *, const char *); 79 static const char *handledash(char *, char *, const char *); 80 static void expand_builtin(const char *[], int, int); 81 static void expand_macro(const char *[], int); 82 static void dump_one_def(const char *, struct macro_definition *); 83 84 unsigned long expansion_id; 85 86 /* 87 * eval - eval all macros and builtins calls 88 * argc - number of elements in argv. 89 * argv - element vector : 90 * argv[0] = definition of a user 91 * macro or NULL if built-in. 92 * argv[1] = name of the macro or 93 * built-in. 94 * argv[2] = parameters to user-defined 95 * . macro or built-in. 96 * . 97 * 98 * A call in the form of macro-or-builtin() will result in: 99 * argv[0] = nullstr 100 * argv[1] = macro-or-builtin 101 * argv[2] = nullstr 102 * 103 * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin 104 */ 105 void 106 eval(const char *argv[], int argc, int td, int is_traced) 107 { 108 ssize_t mark = -1; 109 110 expansion_id++; 111 if (td & RECDEF) 112 errx(1, "%s at line %lu: expanding recursive definition for %s", 113 CURRENT_NAME, CURRENT_LINE, argv[1]); 114 if (is_traced) 115 mark = trace(argv, argc, infile+ilevel); 116 if (td == MACRTYPE) 117 expand_macro(argv, argc); 118 else 119 expand_builtin(argv, argc, td); 120 if (mark != -1) 121 finish_trace(mark); 122 } 123 124 /* 125 * expand_builtin - evaluate built-in macros. 126 */ 127 void 128 expand_builtin(const char *argv[], int argc, int td) 129 { 130 int c, n; 131 int ac; 132 static int sysval = 0; 133 134 #ifdef DEBUG 135 printf("argc = %d\n", argc); 136 for (n = 0; n < argc; n++) 137 printf("argv[%d] = %s\n", n, argv[n]); 138 fflush(stdout); 139 #endif 140 141 /* 142 * if argc == 3 and argv[2] is null, then we 143 * have macro-or-builtin() type call. We adjust 144 * argc to avoid further checking.. 145 */ 146 ac = argc; 147 148 if (argc == 3 && !*(argv[2])) 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 if (argc > 2) 181 pbnum(expr(argv[2])); 182 break; 183 184 case IFELTYPE: 185 if (argc > 4) 186 doifelse(argv, argc); 187 break; 188 189 case IFDFTYPE: 190 /* 191 * doifdef - select one of two 192 * alternatives based on the existence of 193 * another definition 194 */ 195 if (argc > 3) { 196 if (lookup_macro_definition(argv[2]) != NULL) 197 pbstr(argv[3]); 198 else if (argc > 4) 199 pbstr(argv[4]); 200 } 201 break; 202 203 case LENGTYPE: 204 /* 205 * dolen - find the length of the 206 * argument 207 */ 208 pbnum((argc > 2) ? strlen(argv[2]) : 0); 209 break; 210 211 case INCRTYPE: 212 /* 213 * doincr - increment the value of the 214 * argument 215 */ 216 if (argc > 2) 217 pbnum(atoi(argv[2]) + 1); 218 break; 219 220 case DECRTYPE: 221 /* 222 * dodecr - decrement the value of the 223 * argument 224 */ 225 if (argc > 2) 226 pbnum(atoi(argv[2]) - 1); 227 break; 228 229 case SYSCTYPE: 230 /* 231 * dosys - execute system command 232 */ 233 if (argc > 2) 234 sysval = system(argv[2]); 235 break; 236 237 case SYSVTYPE: 238 /* 239 * dosysval - return value of the last 240 * system call. 241 * 242 */ 243 pbnum(sysval); 244 break; 245 246 case ESYSCMDTYPE: 247 if (argc > 2) 248 doesyscmd(argv[2]); 249 break; 250 case INCLTYPE: 251 if (argc > 2) 252 if (!doincl(argv[2])) 253 err(1, "%s at line %lu: include(%s)", 254 CURRENT_NAME, CURRENT_LINE, argv[2]); 255 break; 256 257 case SINCTYPE: 258 if (argc > 2) 259 (void) doincl(argv[2]); 260 break; 261 #ifdef EXTENDED 262 case PASTTYPE: 263 if (argc > 2) 264 if (!dopaste(argv[2])) 265 err(1, "%s at line %lu: paste(%s)", 266 CURRENT_NAME, CURRENT_LINE, argv[2]); 267 break; 268 269 case SPASTYPE: 270 if (argc > 2) 271 (void) dopaste(argv[2]); 272 break; 273 #endif 274 case CHNQTYPE: 275 if (mimic_gnu) 276 gnu_dochq(argv, ac); 277 else 278 dochq(argv, argc); 279 break; 280 281 case CHNCTYPE: 282 if (mimic_gnu) 283 gnu_dochc(argv, ac); 284 else 285 dochc(argv, argc); 286 break; 287 288 case SUBSTYPE: 289 /* 290 * dosub - select substring 291 * 292 */ 293 if (argc > 3) 294 dosub(argv, argc); 295 break; 296 297 case SHIFTYPE: 298 /* 299 * doshift - push back all arguments 300 * except the first one (i.e. skip 301 * argv[2]) 302 */ 303 if (argc > 3) { 304 for (n = argc - 1; n > 3; n--) { 305 pbstr(rquote); 306 pbstr(argv[n]); 307 pbstr(lquote); 308 putback(COMMA); 309 } 310 pbstr(rquote); 311 pbstr(argv[3]); 312 pbstr(lquote); 313 } 314 break; 315 316 case DIVRTYPE: 317 if (argc > 2 && (n = atoi(argv[2])) != 0) 318 dodiv(n); 319 else { 320 active = stdout; 321 oindex = 0; 322 } 323 break; 324 325 case UNDVTYPE: 326 doundiv(argv, argc); 327 break; 328 329 case DIVNTYPE: 330 /* 331 * dodivnum - return the number of 332 * current output diversion 333 */ 334 pbnum(oindex); 335 break; 336 337 case UNDFTYPE: 338 /* 339 * doundefine - undefine a previously 340 * defined macro(s) or m4 keyword(s). 341 */ 342 if (argc > 2) 343 for (n = 2; n < argc; n++) 344 macro_undefine(argv[n]); 345 break; 346 347 case POPDTYPE: 348 /* 349 * dopopdef - remove the topmost 350 * definitions of macro(s) or m4 351 * keyword(s). 352 */ 353 if (argc > 2) 354 for (n = 2; n < argc; n++) 355 macro_popdef(argv[n]); 356 break; 357 358 case MKTMTYPE: 359 /* 360 * dotemp - create a temporary file 361 */ 362 if (argc > 2) { 363 int fd; 364 char *temp; 365 366 temp = xstrdup(argv[2]); 367 368 fd = mkstemp(temp); 369 if (fd == -1) 370 err(1, 371 "%s at line %lu: couldn't make temp file %s", 372 CURRENT_NAME, CURRENT_LINE, argv[2]); 373 close(fd); 374 pbstr(temp); 375 free(temp); 376 } 377 break; 378 379 case TRNLTYPE: 380 /* 381 * dotranslit - replace all characters in 382 * the source string that appears in the 383 * "from" string with the corresponding 384 * characters in the "to" string. 385 */ 386 if (argc > 3) { 387 char *temp; 388 389 temp = xalloc(strlen(argv[2])+1); 390 if (argc > 4) 391 map(temp, argv[2], argv[3], argv[4]); 392 else 393 map(temp, argv[2], argv[3], null); 394 pbstr(temp); 395 free(temp); 396 } else if (argc > 2) 397 pbstr(argv[2]); 398 break; 399 400 case INDXTYPE: 401 /* 402 * doindex - find the index of the second 403 * argument string in the first argument 404 * string. -1 if not present. 405 */ 406 pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); 407 break; 408 409 case ERRPTYPE: 410 /* 411 * doerrp - print the arguments to stderr 412 * file 413 */ 414 if (argc > 2) { 415 for (n = 2; n < argc; n++) 416 fprintf(stderr, "%s ", argv[n]); 417 fprintf(stderr, "\n"); 418 } 419 break; 420 421 case DNLNTYPE: 422 /* 423 * dodnl - eat-up-to and including 424 * newline 425 */ 426 while ((c = gpbc()) != '\n' && c != EOF) 427 ; 428 break; 429 430 case M4WRTYPE: 431 /* 432 * dom4wrap - set up for 433 * wrap-up/wind-down activity 434 */ 435 m4wraps = (argc > 2) ? xstrdup(argv[2]) : null; 436 break; 437 438 case EXITTYPE: 439 /* 440 * doexit - immediate exit from m4. 441 */ 442 killdiv(); 443 exit((argc > 2) ? atoi(argv[2]) : 0); 444 break; 445 446 case DEFNTYPE: 447 if (argc > 2) 448 for (n = 2; n < argc; n++) 449 dodefn(argv[n]); 450 break; 451 452 case INDIRTYPE: /* Indirect call */ 453 if (argc > 2) 454 doindir(argv, argc); 455 break; 456 457 case BUILTINTYPE: /* Builtins only */ 458 if (argc > 2) 459 dobuiltin(argv, argc); 460 break; 461 462 case PATSTYPE: 463 if (argc > 2) 464 dopatsubst(argv, argc); 465 break; 466 case REGEXPTYPE: 467 if (argc > 2) 468 doregexp(argv, argc); 469 break; 470 case LINETYPE: 471 doprintlineno(infile+ilevel); 472 break; 473 case FILENAMETYPE: 474 doprintfilename(infile+ilevel); 475 break; 476 case SELFTYPE: 477 pbstr(rquote); 478 pbstr(argv[1]); 479 pbstr(lquote); 480 break; 481 default: 482 errx(1, "%s at line %lu: eval: major botch.", 483 CURRENT_NAME, CURRENT_LINE); 484 break; 485 } 486 } 487 488 /* 489 * expand_macro - user-defined macro expansion 490 */ 491 void 492 expand_macro(const char *argv[], int argc) 493 { 494 const char *t; 495 const char *p; 496 int n; 497 int argno; 498 499 t = argv[0]; /* defn string as a whole */ 500 p = t; 501 while (*p) 502 p++; 503 p--; /* last character of defn */ 504 while (p > t) { 505 if (*(p - 1) != ARGFLAG) 506 PUTBACK(*p); 507 else { 508 switch (*p) { 509 510 case '#': 511 pbnum(argc - 2); 512 break; 513 case '0': 514 case '1': 515 case '2': 516 case '3': 517 case '4': 518 case '5': 519 case '6': 520 case '7': 521 case '8': 522 case '9': 523 if ((argno = *p - '0') < argc - 1) 524 pbstr(argv[argno + 1]); 525 break; 526 case '*': 527 if (argc > 2) { 528 for (n = argc - 1; n > 2; n--) { 529 pbstr(argv[n]); 530 putback(COMMA); 531 } 532 pbstr(argv[2]); 533 } 534 break; 535 case '@': 536 if (argc > 2) { 537 for (n = argc - 1; n > 2; n--) { 538 pbstr(rquote); 539 pbstr(argv[n]); 540 pbstr(lquote); 541 putback(COMMA); 542 } 543 pbstr(rquote); 544 pbstr(argv[2]); 545 pbstr(lquote); 546 } 547 break; 548 default: 549 PUTBACK(*p); 550 PUTBACK('$'); 551 break; 552 } 553 p--; 554 } 555 p--; 556 } 557 if (p == t) /* do last character */ 558 PUTBACK(*p); 559 } 560 561 562 /* 563 * dodefine - install definition in the table 564 */ 565 void 566 dodefine(const char *name, const char *defn) 567 { 568 if (!*name) 569 errx(1, "%s at line %lu: null definition.", CURRENT_NAME, 570 CURRENT_LINE); 571 macro_define(name, defn); 572 } 573 574 /* 575 * dodefn - push back a quoted definition of 576 * the given name. 577 */ 578 static void 579 dodefn(const char *name) 580 { 581 struct macro_definition *p; 582 char *real; 583 584 if ((p = lookup_macro_definition(name)) != NULL) { 585 if ((p->type & TYPEMASK) == MACRTYPE) { 586 pbstr(rquote); 587 pbstr(p->defn); 588 pbstr(lquote); 589 } else { 590 pbstr(p->defn); 591 pbstr(BUILTIN_MARKER); 592 } 593 } 594 } 595 596 /* 597 * dopushdef - install a definition in the hash table 598 * without removing a previous definition. Since 599 * each new entry is entered in *front* of the 600 * hash bucket, it hides a previous definition from 601 * lookup. 602 */ 603 static void 604 dopushdef(const char *name, const char *defn) 605 { 606 if (!*name) 607 errx(1, "%s at line %lu: null definition", CURRENT_NAME, 608 CURRENT_LINE); 609 macro_pushdef(name, defn); 610 } 611 612 /* 613 * dump_one_def - dump the specified definition. 614 */ 615 static void 616 dump_one_def(const char *name, struct macro_definition *p) 617 { 618 if (mimic_gnu) { 619 if ((p->type & TYPEMASK) == MACRTYPE) 620 fprintf(traceout, "%s:\t%s\n", name, p->defn); 621 else { 622 fprintf(traceout, "%s:\t<%s>\n", name, p->defn); 623 } 624 } else 625 fprintf(traceout, "`%s'\t`%s'\n", name, p->defn); 626 } 627 628 /* 629 * dodumpdef - dump the specified definitions in the hash 630 * table to stderr. If nothing is specified, the entire 631 * hash table is dumped. 632 */ 633 static void 634 dodump(const char *argv[], int argc) 635 { 636 int n; 637 struct macro_definition *p; 638 639 if (argc > 2) { 640 for (n = 2; n < argc; n++) 641 if ((p = lookup_macro_definition(argv[n])) != NULL) 642 dump_one_def(argv[n], p); 643 } else 644 macro_for_all(dump_one_def); 645 } 646 647 /* 648 * dotrace - mark some macros as traced/untraced depending upon on. 649 */ 650 static void 651 dotrace(const char *argv[], int argc, int on) 652 { 653 int n; 654 655 if (argc > 2) { 656 for (n = 2; n < argc; n++) 657 mark_traced(argv[n], on); 658 } else 659 mark_traced(NULL, on); 660 } 661 662 /* 663 * doifelse - select one of two alternatives - loop. 664 */ 665 static void 666 doifelse(const char *argv[], int argc) 667 { 668 cycle { 669 if (STREQ(argv[2], argv[3])) 670 pbstr(argv[4]); 671 else if (argc == 6) 672 pbstr(argv[5]); 673 else if (argc > 6) { 674 argv += 3; 675 argc -= 3; 676 continue; 677 } 678 break; 679 } 680 } 681 682 /* 683 * doinclude - include a given file. 684 */ 685 static int 686 doincl(const char *ifile) 687 { 688 if (ilevel + 1 == MAXINP) 689 errx(1, "%s at line %lu: too many include files.", 690 CURRENT_NAME, CURRENT_LINE); 691 if (fopen_trypath(infile+ilevel+1, ifile) != NULL) { 692 ilevel++; 693 bbase[ilevel] = bufbase = bp; 694 return (1); 695 } else 696 return (0); 697 } 698 699 #ifdef EXTENDED 700 /* 701 * dopaste - include a given file without any 702 * macro processing. 703 */ 704 static int 705 dopaste(const char *pfile) 706 { 707 FILE *pf; 708 int c; 709 710 if ((pf = fopen(pfile, "r")) != NULL) { 711 if (synch_lines) 712 fprintf(active, "#line 1 \"%s\"\n", pfile); 713 while ((c = getc(pf)) != EOF) 714 putc(c, active); 715 (void) fclose(pf); 716 emit_synchline(); 717 return (1); 718 } else 719 return (0); 720 } 721 #endif 722 723 static void 724 gnu_dochq(const char *argv[], int ac) 725 { 726 /* In gnu-m4 mode, the only way to restore quotes is to have no 727 * arguments at all. */ 728 if (ac == 2) { 729 lquote[0] = LQUOTE, lquote[1] = EOS; 730 rquote[0] = RQUOTE, rquote[1] = EOS; 731 } else { 732 strlcpy(lquote, argv[2], sizeof(lquote)); 733 if(ac > 3) 734 strlcpy(rquote, argv[3], sizeof(rquote)); 735 else 736 rquote[0] = EOS; 737 } 738 } 739 740 /* 741 * dochq - change quote characters 742 */ 743 static void 744 dochq(const char *argv[], int argc) 745 { 746 if (argc > 2) { 747 if (*argv[2]) 748 strlcpy(lquote, argv[2], sizeof(lquote)); 749 else { 750 lquote[0] = LQUOTE; 751 lquote[1] = EOS; 752 } 753 if (argc > 3) { 754 if (*argv[3]) 755 strlcpy(rquote, argv[3], sizeof(rquote)); 756 } else 757 strlcpy(rquote, lquote, sizeof(rquote)); 758 } else { 759 lquote[0] = LQUOTE, lquote[1] = EOS; 760 rquote[0] = RQUOTE, rquote[1] = EOS; 761 } 762 } 763 764 static void 765 gnu_dochc(const char *argv[], int ac) 766 { 767 /* In gnu-m4 mode, no arguments mean no comment 768 * arguments at all. */ 769 if (ac == 2) { 770 scommt[0] = EOS; 771 ecommt[0] = EOS; 772 } else { 773 if (*argv[2]) 774 strlcpy(scommt, argv[2], sizeof(scommt)); 775 else 776 scommt[0] = SCOMMT, scommt[1] = EOS; 777 if(ac > 3 && *argv[3]) 778 strlcpy(ecommt, argv[3], sizeof(ecommt)); 779 else 780 ecommt[0] = ECOMMT, ecommt[1] = EOS; 781 } 782 } 783 /* 784 * dochc - change comment characters 785 */ 786 static void 787 dochc(const char *argv[], int argc) 788 { 789 if (argc > 2) { 790 if (*argv[2]) 791 strlcpy(scommt, argv[2], sizeof(scommt)); 792 if (argc > 3) { 793 if (*argv[3]) 794 strlcpy(ecommt, argv[3], sizeof(ecommt)); 795 } 796 else 797 ecommt[0] = ECOMMT, ecommt[1] = EOS; 798 } 799 else { 800 scommt[0] = SCOMMT, scommt[1] = EOS; 801 ecommt[0] = ECOMMT, ecommt[1] = EOS; 802 } 803 } 804 805 /* 806 * dodivert - divert the output to a temporary file 807 */ 808 static void 809 dodiv(int n) 810 { 811 int fd; 812 813 oindex = n; 814 if (n >= maxout) { 815 if (mimic_gnu) 816 resizedivs(n + 10); 817 else 818 n = 0; /* bitbucket */ 819 } 820 821 if (n < 0) 822 n = 0; /* bitbucket */ 823 if (outfile[n] == NULL) { 824 char fname[] = _PATH_DIVNAME; 825 826 if ((fd = mkstemp(fname)) < 0 || 827 (outfile[n] = fdopen(fd, "w+")) == NULL) 828 err(1, "%s: cannot divert", fname); 829 if (unlink(fname) == -1) 830 err(1, "%s: cannot unlink", fname); 831 } 832 active = outfile[n]; 833 } 834 835 /* 836 * doundivert - undivert a specified output, or all 837 * other outputs, in numerical order. 838 */ 839 static void 840 doundiv(const char *argv[], int argc) 841 { 842 int ind; 843 int n; 844 845 if (argc > 2) { 846 for (ind = 2; ind < argc; ind++) { 847 n = atoi(argv[ind]); 848 if (n > 0 && 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 putback(*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 for (i = (unsigned char)src[0]; 993 i <= (unsigned char)src[2]; i++) { 994 *p++ = i; 995 if (p == end) { 996 *p = '\0'; 997 return buffer; 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