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