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 dodumpdef(const char *[], int); 64 static void dotrace(const char *[], int, int); 65 static void doifelse(const char *[], int); 66 static int doinclude(const char *); 67 static int dopaste(const char *); 68 static void dochangequote(const char *[], int); 69 static void dochangecom(const char *[], int); 70 static void dom4wrap(const char *); 71 static void dodivert(int); 72 static void doundivert(const char *[], int); 73 static void dosubstr(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 == MACROTYPE) 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 DEFINETYPE: 153 if (argc > 2) 154 dodefine(argv[2], (argc > 3) ? argv[3] : null); 155 break; 156 157 case PUSHDEFTYPE: 158 if (argc > 2) 159 dopushdef(argv[2], (argc > 3) ? argv[3] : null); 160 break; 161 162 case DUMPDEFTYPE: 163 dodumpdef(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 EVALTYPE: 175 /* 176 * doeval - evaluate arithmetic expression 177 */ 178 { 179 int base = 10; 180 int mindigits = 0; 181 const char *errstr; 182 183 if (argc > 3 && *argv[3] != '\0') { 184 base = strtonum(argv[3], 2, 36, &errstr); 185 if (errstr) { 186 m4errx(1, "eval: base is %s: %s.", 187 errstr, argv[3]); 188 } 189 } 190 if (argc > 4) { 191 mindigits = strtonum(argv[4], 0, INT_MAX, &errstr); 192 if (errstr) { 193 m4errx(1, "eval: mindigits is %s: %s.", 194 errstr, argv[4]); 195 } 196 } 197 if (argc > 2) 198 pbnumbase(expr(argv[2]), base, mindigits); 199 break; 200 } 201 202 case IFELSETYPE: 203 doifelse(argv, argc); 204 break; 205 206 case IFDEFTYPE: 207 /* 208 * doifdef - select one of two alternatives based 209 * on the existence of another definition 210 */ 211 if (argc > 3) { 212 if (lookup_macro_definition(argv[2]) != NULL) 213 pbstr(argv[3]); 214 else if (argc > 4) 215 pbstr(argv[4]); 216 } 217 break; 218 219 case LENTYPE: 220 /* 221 * dolen - find the length of the argument 222 */ 223 pbnum((argc > 2) ? strlen(argv[2]) : 0); 224 break; 225 226 case INCRTYPE: 227 /* 228 * doincr - increment the value of the argument 229 */ 230 if (argc > 2) { 231 n = strtonum(argv[2], INT_MIN, INT_MAX-1, &errstr); 232 if (errstr != NULL) 233 m4errx(1, "incr: argument is %s: %s.", 234 errstr, argv[2]); 235 pbnum(n + 1); 236 } 237 break; 238 239 case DECRTYPE: 240 /* 241 * dodecr - decrement the value of the argument 242 */ 243 if (argc > 2) { 244 n = strtonum(argv[2], INT_MIN+1, INT_MAX, &errstr); 245 if (errstr) 246 m4errx(1, "decr: argument is %s: %s.", 247 errstr, argv[2]); 248 pbnum(n - 1); 249 } 250 break; 251 252 case SYSCMDTYPE: 253 /* 254 * dosyscmd - execute system command 255 */ 256 if (argc > 2) { 257 fflush(stdout); 258 sysval = system(argv[2]); 259 } 260 break; 261 262 case SYSVALTYPE: 263 /* 264 * dosysval - return value of the last system call. 265 * 266 */ 267 pbnum(sysval); 268 break; 269 270 case ESYSCMDTYPE: 271 if (argc > 2) 272 doesyscmd(argv[2]); 273 break; 274 case INCLUDETYPE: 275 if (argc > 2) { 276 if (!doinclude(argv[2])) { 277 if (mimic_gnu) { 278 warn("%s at line %lu: include(%s)", 279 CURRENT_NAME, CURRENT_LINE, argv[2]); 280 exit_code = 1; 281 if (fatal_warns) { 282 killdiv(); 283 exit(exit_code); 284 } 285 } else 286 err(1, "%s at line %lu: include(%s)", 287 CURRENT_NAME, CURRENT_LINE, argv[2]); 288 } 289 } 290 break; 291 292 case SINCLUDETYPE: 293 /* like include, but don't error out if file not found */ 294 if (argc > 2) 295 (void) doinclude(argv[2]); 296 break; 297 #ifdef EXTENDED 298 case PASTETYPE: 299 if (argc > 2) 300 if (!dopaste(argv[2])) 301 err(1, "%s at line %lu: paste(%s)", 302 CURRENT_NAME, CURRENT_LINE, argv[2]); 303 break; 304 305 case SPASTETYPE: 306 if (argc > 2) 307 (void) dopaste(argv[2]); 308 break; 309 case FORMATTYPE: 310 doformat(argv, argc); 311 break; 312 #endif 313 case CHANGEQUOTETYPE: 314 dochangequote(argv, ac); 315 break; 316 317 case CHANGECOMTYPE: 318 dochangecom(argv, argc); 319 break; 320 321 case SUBSTRTYPE: 322 /* 323 * dosubstr - select substring 324 * 325 */ 326 if (argc > 3) 327 dosubstr(argv, argc); 328 break; 329 330 case SHIFTTYPE: 331 /* 332 * doshift - push back all arguments except the first one 333 * (i.e. skip argv[2]) 334 */ 335 if (argc > 3) { 336 for (n = argc - 1; n > 3; n--) { 337 pbstr(rquote); 338 pbstr(argv[n]); 339 pbstr(lquote); 340 pushback(COMMA); 341 } 342 pbstr(rquote); 343 pbstr(argv[3]); 344 pbstr(lquote); 345 } 346 break; 347 348 case DIVERTTYPE: 349 if (argc > 2) { 350 n = strtonum(argv[2], INT_MIN, INT_MAX, &errstr); 351 if (errstr) 352 m4errx(1, "divert: argument is %s: %s.", 353 errstr, argv[2]); 354 if (n != 0) { 355 dodivert(n); 356 break; 357 } 358 } 359 active = stdout; 360 oindex = 0; 361 break; 362 363 case UNDIVERTTYPE: 364 doundivert(argv, argc); 365 break; 366 367 case DIVNUMTYPE: 368 /* 369 * dodivnum - return the number of current output diversion 370 */ 371 pbnum(oindex); 372 break; 373 374 case UNDEFINETYPE: 375 /* 376 * doundefine - undefine a previously defined macro(s) or m4 377 * keyword(s). 378 */ 379 if (argc > 2) 380 for (n = 2; n < argc; n++) 381 macro_undefine(argv[n]); 382 break; 383 384 case POPDEFTYPE: 385 /* 386 * dopopdef - remove the topmost definitions of macro(s) 387 * or m4 keyword(s). 388 */ 389 if (argc > 2) 390 for (n = 2; n < argc; n++) 391 macro_popdef(argv[n]); 392 break; 393 394 case MKSTEMPTYPE: 395 /* 396 * domkstemp - safely create a temporary file 397 */ 398 if (argc > 2) { 399 int fd; 400 char *temp; 401 402 temp = xstrdup(argv[2]); 403 404 fd = mkstemp(temp); 405 if (fd == -1) 406 err(1, 407 "%s at line %lu: couldn't make temp file %s", 408 CURRENT_NAME, CURRENT_LINE, argv[2]); 409 close(fd); 410 pbstr(temp); 411 free(temp); 412 } 413 break; 414 415 case TRANSLITTYPE: 416 /* 417 * dotranslit - replace all characters in the source string 418 * that appear in the "from" string with the corresponding 419 * characters in the "to" string. 420 */ 421 if (argc > 3) { 422 char *temp; 423 424 temp = xalloc(strlen(argv[2])+1, NULL); 425 if (argc > 4) 426 map(temp, argv[2], argv[3], argv[4]); 427 else 428 map(temp, argv[2], argv[3], null); 429 pbstr(temp); 430 free(temp); 431 } else if (argc > 2) 432 pbstr(argv[2]); 433 break; 434 435 case INDEXTYPE: 436 /* 437 * doindex - find the index of the second argument string 438 * in the first argument string. -1 if not present. 439 */ 440 pbnum((argc > 3) ? doindex(argv[2], argv[3]) : -1); 441 break; 442 443 case ERRPRINTTYPE: 444 /* 445 * doerrprint - print the arguments to stderr 446 */ 447 if (argc > 2) { 448 for (n = 2; n < argc; n++) 449 fprintf(stderr, "%s ", argv[n]); 450 fprintf(stderr, "\n"); 451 } 452 break; 453 454 case DNLTYPE: 455 /* 456 * dodnl - eat-up-to and including newline 457 */ 458 while ((c = gpbc()) != '\n' && c != EOF) 459 ; 460 break; 461 462 case M4WRAPTYPE: 463 /* 464 * dom4wrap - set up for 465 * wrap-up/wind-down activity 466 */ 467 if (argc > 2) 468 dom4wrap(argv[2]); 469 break; 470 471 case M4EXITTYPE: 472 /* 473 * dom4exit - immediate exit from m4. 474 */ 475 killdiv(); 476 exit((argc > 2) ? atoi(argv[2]) : 0); 477 break; 478 479 case DEFNTYPE: 480 if (argc > 2) 481 for (n = argc - 1; n >= 2; n--) 482 dodefn(argv[n]); 483 break; 484 485 case INDIRTYPE: /* Indirect call */ 486 if (argc > 2) 487 doindir(argv, argc); 488 break; 489 490 case BUILTINTYPE: /* Builtins only */ 491 if (argc > 2) 492 dobuiltin(argv, argc); 493 break; 494 495 case PATSUBSTTYPE: 496 if (argc > 2) 497 dopatsubst(argv, argc); 498 break; 499 case REGEXPTYPE: 500 if (argc > 2) 501 doregexp(argv, argc); 502 break; 503 case LINETYPE: 504 doprintlineno(infile+ilevel); 505 break; 506 case FILENAMETYPE: 507 doprintfilename(infile+ilevel); 508 break; 509 case SELFTYPE: 510 pbstr(rquote); 511 pbstr(argv[1]); 512 pbstr(lquote); 513 break; 514 default: 515 m4errx(1, "eval: major botch."); 516 break; 517 } 518 } 519 520 /* 521 * expand_macro - user-defined macro expansion 522 */ 523 void 524 expand_macro(const char *argv[], int argc) 525 { 526 const char *t; 527 const char *p; 528 int n; 529 int argno; 530 531 t = argv[0]; /* defn string as a whole */ 532 p = t; 533 while (*p) 534 p++; 535 p--; /* last character of defn */ 536 while (p > t) { 537 if (*(p - 1) != ARGFLAG) 538 PUSHBACK(*p); 539 else { 540 switch (*p) { 541 542 case '#': 543 pbnum(argc - 2); 544 break; 545 case '0': 546 case '1': 547 case '2': 548 case '3': 549 case '4': 550 case '5': 551 case '6': 552 case '7': 553 case '8': 554 case '9': 555 if ((argno = *p - '0') < argc - 1) 556 pbstr(argv[argno + 1]); 557 break; 558 case '*': 559 if (argc > 2) { 560 for (n = argc - 1; n > 2; n--) { 561 pbstr(argv[n]); 562 pushback(COMMA); 563 } 564 pbstr(argv[2]); 565 } 566 break; 567 case '@': 568 if (argc > 2) { 569 for (n = argc - 1; n > 2; n--) { 570 pbstr(rquote); 571 pbstr(argv[n]); 572 pbstr(lquote); 573 pushback(COMMA); 574 } 575 pbstr(rquote); 576 pbstr(argv[2]); 577 pbstr(lquote); 578 } 579 break; 580 default: 581 PUSHBACK(*p); 582 PUSHBACK('$'); 583 break; 584 } 585 p--; 586 } 587 p--; 588 } 589 if (p == t) /* do last character */ 590 PUSHBACK(*p); 591 } 592 593 594 /* 595 * dodefine - install definition in the table 596 */ 597 void 598 dodefine(const char *name, const char *defn) 599 { 600 if (!*name && !mimic_gnu) 601 m4errx(1, "null definition."); 602 else 603 macro_define(name, defn); 604 } 605 606 /* 607 * dodefn - push back a quoted definition of 608 * the given name. 609 */ 610 static void 611 dodefn(const char *name) 612 { 613 struct macro_definition *p; 614 615 if ((p = lookup_macro_definition(name)) != NULL) { 616 if ((p->type & TYPEMASK) == MACROTYPE) { 617 pbstr(rquote); 618 pbstr(p->defn); 619 pbstr(lquote); 620 } else { 621 pbstr(p->defn); 622 pbstr(BUILTIN_MARKER); 623 } 624 } 625 } 626 627 /* 628 * dopushdef - install a definition in the hash table 629 * without removing a previous definition. Since 630 * each new entry is entered in *front* of the 631 * hash bucket, it hides a previous definition from 632 * lookup. 633 */ 634 static void 635 dopushdef(const char *name, const char *defn) 636 { 637 if (!*name && !mimic_gnu) 638 m4errx(1, "null definition."); 639 else 640 macro_pushdef(name, defn); 641 } 642 643 /* 644 * dump_one_def - dump the specified definition. 645 */ 646 static void 647 dump_one_def(const char *name, struct macro_definition *p) 648 { 649 if (!traceout) 650 traceout = stderr; 651 if (mimic_gnu) { 652 if ((p->type & TYPEMASK) == MACROTYPE) 653 fprintf(traceout, "%s:\t%s\n", name, p->defn); 654 else { 655 fprintf(traceout, "%s:\t<%s>\n", name, p->defn); 656 } 657 } else 658 fprintf(traceout, "`%s'\t`%s'\n", name, p->defn); 659 } 660 661 /* 662 * dodumpdef - dump the specified definitions in the hash 663 * table to stderr. If nothing is specified, the entire 664 * hash table is dumped. 665 */ 666 static void 667 dodumpdef(const char *argv[], int argc) 668 { 669 int n; 670 struct macro_definition *p; 671 672 if (argc > 2) { 673 for (n = 2; n < argc; n++) 674 if ((p = lookup_macro_definition(argv[n])) != NULL) 675 dump_one_def(argv[n], p); 676 } else 677 macro_for_all(dump_one_def); 678 } 679 680 /* 681 * dotrace - mark some macros as traced/untraced depending upon on. 682 */ 683 static void 684 dotrace(const char *argv[], int argc, int on) 685 { 686 int n; 687 688 if (argc > 2) { 689 for (n = 2; n < argc; n++) 690 mark_traced(argv[n], on); 691 } else 692 mark_traced(NULL, on); 693 } 694 695 /* 696 * doifelse - select one of two alternatives - loop. 697 */ 698 static void 699 doifelse(const char *argv[], int argc) 700 { 701 while (argc > 4) { 702 if (STREQ(argv[2], argv[3])) { 703 pbstr(argv[4]); 704 break; 705 } else if (argc == 6) { 706 pbstr(argv[5]); 707 break; 708 } else { 709 argv += 3; 710 argc -= 3; 711 } 712 } 713 } 714 715 /* 716 * doinclude - include a given file. 717 */ 718 static int 719 doinclude(const char *ifile) 720 { 721 if (ilevel + 1 == MAXINP) 722 m4errx(1, "too many include files."); 723 if (fopen_trypath(infile+ilevel+1, ifile) != NULL) { 724 ilevel++; 725 bbase[ilevel] = bufbase = bp; 726 return (1); 727 } else 728 return (0); 729 } 730 731 #ifdef EXTENDED 732 /* 733 * dopaste - include a given file without any 734 * macro processing. 735 */ 736 static int 737 dopaste(const char *pfile) 738 { 739 FILE *pf; 740 int c; 741 742 if ((pf = fopen(pfile, "r")) != NULL) { 743 if (synch_lines) 744 fprintf(active, "#line 1 \"%s\"\n", pfile); 745 while ((c = getc(pf)) != EOF) 746 putc(c, active); 747 (void) fclose(pf); 748 emit_synchline(); 749 return (1); 750 } else 751 return (0); 752 } 753 #endif 754 755 /* 756 * dochangequote - change quote characters 757 */ 758 static void 759 dochangequote(const char *argv[], int ac) 760 { 761 if (ac == 2) { 762 lquote[0] = LQUOTE; lquote[1] = EOS; 763 rquote[0] = RQUOTE; rquote[1] = EOS; 764 } else { 765 strlcpy(lquote, argv[2], sizeof(lquote)); 766 if (ac > 3) { 767 strlcpy(rquote, argv[3], sizeof(rquote)); 768 } else { 769 rquote[0] = ECOMMT; rquote[1] = EOS; 770 } 771 } 772 } 773 774 /* 775 * dochangecom - change comment characters 776 */ 777 static void 778 dochangecom(const char *argv[], int argc) 779 { 780 /* XXX Note that there is no difference between no argument and a single 781 * empty argument. 782 */ 783 if (argc == 2) { 784 scommt[0] = EOS; 785 ecommt[0] = EOS; 786 } else { 787 strlcpy(scommt, argv[2], sizeof(scommt)); 788 if (argc == 3) { 789 ecommt[0] = ECOMMT; ecommt[1] = EOS; 790 } else { 791 strlcpy(ecommt, argv[3], sizeof(ecommt)); 792 } 793 } 794 } 795 796 /* 797 * dom4wrap - expand text at EOF 798 */ 799 static void 800 dom4wrap(const char *text) 801 { 802 if (wrapindex >= maxwraps) { 803 if (maxwraps == 0) 804 maxwraps = 16; 805 else 806 maxwraps *= 2; 807 m4wraps = xreallocarray(m4wraps, maxwraps, sizeof(*m4wraps), 808 "too many m4wraps"); 809 } 810 m4wraps[wrapindex++] = xstrdup(text); 811 } 812 813 /* 814 * dodivert - divert the output to a temporary file 815 */ 816 static void 817 dodivert(int n) 818 { 819 int fd; 820 821 oindex = n; 822 if (n >= maxout) { 823 if (mimic_gnu) 824 resizedivs(n + 10); 825 else 826 n = 0; /* bitbucket */ 827 } 828 829 if (n < 0) 830 n = 0; /* bitbucket */ 831 if (outfile[n] == NULL) { 832 char fname[] = _PATH_DIVNAME; 833 834 if ((fd = mkstemp(fname)) == -1 || 835 unlink(fname) == -1 || 836 (outfile[n] = fdopen(fd, "w+")) == NULL) 837 err(1, "%s: cannot divert", fname); 838 } 839 active = outfile[n]; 840 } 841 842 /* 843 * doundivert - undivert a specified output, or all 844 * other outputs, in numerical order. 845 */ 846 static void 847 doundivert(const char *argv[], int argc) 848 { 849 int ind; 850 int n; 851 852 if (argc > 2) { 853 for (ind = 2; ind < argc; ind++) { 854 const char *errstr; 855 n = strtonum(argv[ind], 1, INT_MAX, &errstr); 856 if (errstr) { 857 if (errno == EINVAL && mimic_gnu) 858 getdivfile(argv[ind]); 859 } else { 860 if (n < maxout && outfile[n] != NULL) 861 getdiv(n); 862 } 863 } 864 } 865 else 866 for (n = 1; n < maxout; n++) 867 if (outfile[n] != NULL) 868 getdiv(n); 869 } 870 871 /* 872 * dosubstr - select substring 873 */ 874 static void 875 dosubstr(const char *argv[], int argc) 876 { 877 const char *ap, *fc, *k; 878 int nc; 879 880 ap = argv[2]; /* target string */ 881 #ifdef EXPR 882 fc = ap + expr(argv[3]); /* first char */ 883 #else 884 fc = ap + atoi(argv[3]); /* first char */ 885 #endif 886 nc = strlen(fc); 887 if (argc >= 5) 888 #ifdef EXPR 889 nc = min(nc, expr(argv[4])); 890 #else 891 nc = min(nc, atoi(argv[4])); 892 #endif 893 if (fc >= ap && fc < ap + strlen(ap)) 894 for (k = fc + nc - 1; k >= fc; k--) 895 pushback(*k); 896 } 897 898 /* 899 * map: 900 * map every character of s1 that is specified in from 901 * into s3 and replace in s. (source s1 remains untouched) 902 * 903 * This is derived from the a standard implementation of map(s,from,to) 904 * function of ICON language. Within mapvec, we replace every character 905 * of "from" with the corresponding character in "to". 906 * If "to" is shorter than "from", than the corresponding entries are null, 907 * which means that those characters disappear altogether. 908 */ 909 static void 910 map(char *dest, const char *src, const char *from, const char *to) 911 { 912 const char *tmp; 913 unsigned char sch, dch; 914 static char frombis[257]; 915 static char tobis[257]; 916 int i; 917 char seen[256]; 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 for (i = 0; i < 256; i++) 953 seen[i] = 0; 954 while (*from) { 955 if (!seen[(unsigned char)(*from)]) { 956 mapvec[(unsigned char)(*from)] = (unsigned char)(*to); 957 seen[(unsigned char)(*from)] = 1; 958 } 959 from++; 960 if (*to) 961 to++; 962 } 963 964 while (*src) { 965 sch = (unsigned char)(*src++); 966 dch = mapvec[sch]; 967 if ((*dest = (char)dch)) 968 dest++; 969 } 970 /* 971 * restore all the changed characters 972 */ 973 while (*tmp) { 974 mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp); 975 tmp++; 976 } 977 } 978 *dest = '\0'; 979 } 980 981 982 /* 983 * handledash: 984 * use buffer to copy the src string, expanding character ranges 985 * on the way. 986 */ 987 static const char * 988 handledash(char *buffer, char *end, const char *src) 989 { 990 char *p; 991 992 p = buffer; 993 while(*src) { 994 if (src[1] == '-' && src[2]) { 995 unsigned char i; 996 if ((unsigned char)src[0] <= (unsigned char)src[2]) { 997 for (i = (unsigned char)src[0]; 998 i <= (unsigned char)src[2]; i++) { 999 *p++ = i; 1000 if (p == end) { 1001 *p = '\0'; 1002 return buffer; 1003 } 1004 } 1005 } else { 1006 for (i = (unsigned char)src[0]; 1007 i >= (unsigned char)src[2]; i--) { 1008 *p++ = i; 1009 if (p == end) { 1010 *p = '\0'; 1011 return buffer; 1012 } 1013 } 1014 } 1015 src += 3; 1016 } else 1017 *p++ = *src++; 1018 if (p == end) 1019 break; 1020 } 1021 *p = '\0'; 1022 return buffer; 1023 } 1024