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