1 /* $OpenBSD: eval.c,v 1.81 2026/02/25 05:37:25 op Exp $ */ 2 /* $NetBSD: eval.c,v 1.31 2026/06/10 22:25:02 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.31 2026/06/10 22:25:02 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 <stdint.h> 55 #include <stdlib.h> 56 #include <stddef.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 dodumpdef(const char *[], int); 68 static void dotrace(const char *[], int, int); 69 static void doifelse(const char *[], int); 70 static int doinclude(const char *); 71 static int dopaste(const char *); 72 static void dochangequote(const char *[], int); 73 static void dochangecom(const char *[], int); 74 static void dom4wrap(const char *); 75 static void dodivert(int); 76 static void doundivert(const char *[], int); 77 static void dosubstr(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 == MACROTYPE) 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, e; 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 DEFINETYPE: 156 if (argc > 2) 157 dodefine(argv[2], (argc > 3) ? argv[3] : null); 158 break; 159 160 case PUSHDEFTYPE: 161 if (argc > 2) 162 dopushdef(argv[2], (argc > 3) ? argv[3] : null); 163 break; 164 165 case DUMPDEFTYPE: 166 dodumpdef(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 EVALTYPE: 178 /* 179 * doeval - evaluate arithmetic expression 180 */ 181 { 182 int base = 10; 183 int maxdigits = 0; 184 185 if (argc > 3 && *argv[3] != '\0') { 186 base = (int)strtoi(argv[3], NULL, 0, 2, 36, &e); 187 if (e) { 188 m4errx(1, "expr: base %s invalid.", argv[3]); 189 } 190 } 191 if (argc > 4) { 192 maxdigits = (int)strtoi(argv[4], NULL, 0, 0, INT_MAX, &e); 193 if (e) { 194 m4errx(1, "expr: maxdigits %s invalid.", argv[4]); 195 } 196 } 197 if (argc > 2) 198 pbnumbase(expr(argv[2]), base, maxdigits); 199 break; 200 } 201 202 case IFELSETYPE: 203 if (argc > 4) 204 doifelse(argv, argc); 205 break; 206 207 case IFDEFTYPE: 208 /* 209 * doifdef - select one of two alternatives based 210 * on the existence of another definition 211 */ 212 if (argc > 3) { 213 if (lookup_macro_definition(argv[2]) != NULL) 214 pbstr(argv[3]); 215 else if (argc > 4) 216 pbstr(argv[4]); 217 } 218 break; 219 220 case LENTYPE: 221 /* 222 * dolen - find the length of the argument 223 */ 224 pbnum((argc > 2) ? strlen(argv[2]) : 0); 225 break; 226 227 case INCRTYPE: 228 /* 229 * doincr - increment the value of the argument 230 */ 231 if (argc > 2) { 232 n = (int)strtoi(argv[2], NULL, 0, INT_MIN, INT_MAX-1, &e); 233 if (e) { 234 m4errx(1, "incr: argument %s: %s,", argv[2], 235 strerror(e)); 236 } 237 pbnum(n + 1); 238 } 239 break; 240 241 case DECRTYPE: 242 /* 243 * dodecr - decrement the value of the argument 244 */ 245 if (argc > 2) { 246 n = (int)strtoi(argv[2], NULL, 0, INT_MIN, INT_MAX-1, &e); 247 if (e) { 248 m4errx(1, "decr: argument %s: %s,", argv[2], 249 strerror(e)); 250 } 251 pbnum(n - 1); 252 } 253 break; 254 255 case SYSCMDTYPE: 256 /* 257 * dosyscmd - execute system command 258 */ 259 if (argc > 2) { 260 fflush(stdout); 261 sysval = system(argv[2]); 262 } 263 break; 264 265 case SYSVALTYPE: 266 /* 267 * dosysval - return value of the last system call. 268 * 269 */ 270 pbnum(sysval); 271 break; 272 273 case ESYSCMDTYPE: 274 if (argc > 2) 275 doesyscmd(argv[2]); 276 break; 277 case INCLUDETYPE: 278 if (argc > 2) { 279 if (!doinclude(argv[2])) { 280 if (mimic_gnu) { 281 warn("%s at line %lu: include(%s)", 282 CURRENT_NAME, CURRENT_LINE, argv[2]); 283 exit_code = 1; 284 if (fatal_warns) { 285 killdiv(); 286 exit(exit_code); 287 } 288 } else 289 err(1, "%s at line %lu: include(%s)", 290 CURRENT_NAME, CURRENT_LINE, argv[2]); 291 } 292 } 293 break; 294 295 case SINCLUDETYPE: 296 /* like include, but don't error out if file not found */ 297 if (argc > 2) 298 (void) doinclude(argv[2]); 299 break; 300 #ifdef EXTENDED 301 case PASTETYPE: 302 if (argc > 2) 303 if (!dopaste(argv[2])) 304 err(1, "%s at line %lu: paste(%s)", 305 CURRENT_NAME, CURRENT_LINE, argv[2]); 306 break; 307 308 case SPASTETYPE: 309 if (argc > 2) 310 (void) dopaste(argv[2]); 311 break; 312 case FORMATTYPE: 313 doformat(argv, argc); 314 break; 315 #endif 316 case CHANGEQUOTETYPE: 317 dochangequote(argv, ac); 318 break; 319 320 case CHANGECOMTYPE: 321 dochangecom(argv, argc); 322 break; 323 324 case SUBSTRTYPE: 325 /* 326 * dosubstr - select substring 327 * 328 */ 329 if (argc > 3) 330 dosubstr(argv, argc); 331 break; 332 333 case SHIFTTYPE: 334 /* 335 * doshift - push back all arguments except the first one 336 * (i.e. skip argv[2]) 337 */ 338 if (argc > 3) { 339 for (n = argc - 1; n > 3; n--) { 340 pbstr(rquote); 341 pbstr(argv[n]); 342 pbstr(lquote); 343 pushback(COMMA); 344 } 345 pbstr(rquote); 346 pbstr(argv[3]); 347 pbstr(lquote); 348 } 349 break; 350 351 case DIVERTTYPE: 352 if (argc > 2) { 353 n = (int)strtoi(argv[2], NULL, 0, INT_MIN, INT_MAX, &e); 354 if (e) { 355 m4errx(1, "divert: argument %s: %s,", argv[2], 356 strerror(e)); 357 } 358 if (n != 0) { 359 dodivert(n); 360 break; 361 } 362 } 363 active = stdout; 364 oindex = 0; 365 break; 366 367 case UNDIVERTTYPE: 368 doundivert(argv, argc); 369 break; 370 371 case DIVNUMTYPE: 372 /* 373 * dodivnum - return the number of current output diversion 374 */ 375 pbnum(oindex); 376 break; 377 378 case UNDEFINETYPE: 379 /* 380 * doundefine - undefine a previously defined macro(s) or m4 keyword(s). 381 */ 382 if (argc > 2) 383 for (n = 2; n < argc; n++) 384 macro_undefine(argv[n]); 385 break; 386 387 case POPDEFTYPE: 388 /* 389 * dopopdef - remove the topmost definitions of macro(s) 390 * or m4 keyword(s). 391 */ 392 if (argc > 2) 393 for (n = 2; n < argc; n++) 394 macro_popdef(argv[n]); 395 break; 396 397 case MKSTEMPTYPE: 398 /* 399 * domkstemp - create a temporary file 400 */ 401 if (argc > 2) { 402 int fd; 403 char *temp; 404 405 temp = xstrdup(argv[2]); 406 407 fd = mkstemp(temp); 408 if (fd == -1) 409 err(1, 410 "%s at line %lu: couldn't make temp file %s", 411 CURRENT_NAME, CURRENT_LINE, argv[2]); 412 close(fd); 413 pbstr(temp); 414 free(temp); 415 } 416 break; 417 418 case TRANSLITTYPE: 419 /* 420 * dotranslit - replace all characters in the source string 421 * that appears in the "from" string with the corresponding 422 * characters in the "to" string. 423 */ 424 if (argc > 3) { 425 char *temp; 426 427 temp = xalloc(strlen(argv[2])+1, NULL); 428 if (argc > 4) 429 map(temp, argv[2], argv[3], argv[4]); 430 else 431 map(temp, argv[2], argv[3], null); 432 pbstr(temp); 433 free(temp); 434 } else if (argc > 2) 435 pbstr(argv[2]); 436 break; 437 438 case INDEXTYPE: 439 /* 440 * doindex - find the index of the second argument string 441 * in the first argument string. -1 if not present. 442 */ 443 pbnum((int)((argc > 3) ? doindex(argv[2], argv[3]) : -1)); 444 break; 445 446 case ERRPRINTTYPE: 447 /* 448 * doerrprint - print the arguments to stderr 449 */ 450 if (argc > 2) { 451 for (n = 2; n < argc; n++) 452 fprintf(stderr, "%s%s", 453 mimic_gnu && n == 2 ? "" : " ", 454 argv[n]); 455 if (!mimic_gnu) 456 fprintf(stderr, "\n"); 457 } 458 break; 459 460 case DNLTYPE: 461 /* 462 * dodnl - eat-up-to and including newline 463 */ 464 while ((c = gpbc()) != '\n' && c != EOF) 465 ; 466 break; 467 468 case M4WRAPTYPE: 469 /* 470 * dom4wrap - set up for wrap-up/wind-down activity 471 */ 472 if (argc > 2) 473 dom4wrap(argv[2]); 474 break; 475 476 case M4EXITTYPE: 477 /* 478 * dom4exit - immediate exit from m4. 479 */ 480 killdiv(); 481 exit((argc > 2) ? atoi(argv[2]) : 0); 482 483 case DEFNTYPE: 484 if (argc > 2) 485 for (n = 2; n < argc; n++) 486 dodefn(argv[n]); 487 break; 488 489 case INDIRTYPE: /* Indirect call */ 490 if (argc > 2) 491 doindir(argv, argc); 492 break; 493 494 case BUILTINTYPE: /* Builtins only */ 495 if (argc > 2) 496 dobuiltin(argv, argc); 497 break; 498 499 case PATSUBSTTYPE: 500 if (argc > 2) 501 dopatsubst(argv, argc); 502 break; 503 case REGEXPTYPE: 504 if (argc > 2) 505 doregexp(argv, argc); 506 break; 507 case LINETYPE: 508 doprintlineno(infile+ilevel); 509 break; 510 case FILENAMETYPE: 511 doprintfilename(infile+ilevel); 512 break; 513 case SELFTYPE: 514 pbstr(rquote); 515 pbstr(argv[1]); 516 pbstr(lquote); 517 break; 518 default: 519 m4errx(1, "eval: major botch."); 520 } 521 } 522 523 /* 524 * expand_macro - user-defined macro expansion 525 */ 526 void 527 expand_macro(const char *argv[], int argc) 528 { 529 const char *t; 530 const char *p; 531 int n; 532 int argno; 533 534 t = argv[0]; /* defn string as a whole */ 535 p = t; 536 while (*p) 537 p++; 538 p--; /* last character of defn */ 539 while (p > t) { 540 if (*(p - 1) != ARGFLAG) 541 PUSHBACK(*p); 542 else { 543 switch (*p) { 544 545 case '#': 546 pbnum(argc - 2); 547 break; 548 case '0': 549 case '1': 550 case '2': 551 case '3': 552 case '4': 553 case '5': 554 case '6': 555 case '7': 556 case '8': 557 case '9': 558 argno = *p - '0'; 559 if (mimic_gnu) { 560 const unsigned char *q = 561 (const unsigned char *)p; 562 while (isdigit(*++q)) { 563 bp--; 564 argno = argno * 10 + *q - '0'; 565 } 566 } 567 if (argno < 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, "define macro with empty name."); 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) == MACROTYPE) { 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, "pushdef macro with empty name."); 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) == MACROTYPE) 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 dodumpdef(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 (argc < 5) 715 m4errx(1, "wrong number of args for ifelse"); 716 if (STREQ(argv[2], argv[3])) { 717 pbstr(argv[4]); 718 break; 719 } else if (argc == 6) { 720 pbstr(argv[5]); 721 break; 722 } else { 723 argv += 3; 724 argc -= 3; 725 } 726 } 727 } 728 729 /* 730 * doinclude - include a given file. 731 */ 732 static int 733 doinclude(const char *ifile) 734 { 735 #ifndef REAL_FREEZE 736 if (thawing) 737 return 1; 738 #endif 739 if (ilevel + 1 == MAXINP) 740 m4errx(1, "too many include files."); 741 if (fopen_trypath(infile+ilevel+1, ifile) != NULL) { 742 ilevel++; 743 bbase[ilevel] = bufbase = bp; 744 return (1); 745 } else 746 return (0); 747 } 748 749 #ifdef EXTENDED 750 /* 751 * dopaste - include a given file without any 752 * macro processing. 753 */ 754 static int 755 dopaste(const char *pfile) 756 { 757 FILE *pf; 758 int c; 759 760 if ((pf = fopen(pfile, "r")) != NULL) { 761 if (synch_lines) 762 fprintf(active, "#line 1 \"%s\"\n", pfile); 763 while ((c = getc(pf)) != EOF) 764 putc(c, active); 765 (void) fclose(pf); 766 emit_synchline(); 767 return (1); 768 } else 769 return (0); 770 } 771 #endif 772 773 /* 774 * dochangequote - change quote characters 775 */ 776 static void 777 dochangequote(const char *argv[], int ac) 778 { 779 if (ac == 2) { 780 lquote[0] = LQUOTE; lquote[1] = EOS; 781 rquote[0] = RQUOTE; rquote[1] = EOS; 782 } else { 783 strlcpy(lquote, argv[2], sizeof(lquote)); 784 if (ac > 3) { 785 strlcpy(rquote, argv[3], sizeof(rquote)); 786 } else { 787 rquote[0] = ECOMMT; rquote[1] = EOS; 788 } 789 } 790 } 791 792 /* 793 * dochangecom - change comment characters 794 */ 795 static void 796 dochangecom(const char *argv[], int argc) 797 { 798 /* XXX Note that there is no difference between no argument and a single 799 * empty argument. 800 */ 801 if (argc == 2) { 802 scommt[0] = EOS; 803 ecommt[0] = EOS; 804 } else { 805 strlcpy(scommt, argv[2], sizeof(scommt)); 806 if (argc == 3) { 807 ecommt[0] = ECOMMT; ecommt[1] = EOS; 808 } else { 809 strlcpy(ecommt, argv[3], sizeof(ecommt)); 810 } 811 } 812 } 813 814 /* 815 * dom4wrap - expand text at EOF 816 */ 817 static void 818 dom4wrap(const char *text) 819 { 820 if (wrapindex >= maxwraps) { 821 if (maxwraps == 0) 822 maxwraps = 16; 823 else 824 maxwraps *= 2; 825 m4wraps = xreallocarray(m4wraps, maxwraps, sizeof(*m4wraps), 826 "too many m4wraps"); 827 } 828 m4wraps[wrapindex++] = xstrdup(text); 829 } 830 831 /* 832 * dodivert - divert the output to a temporary file 833 */ 834 static void 835 dodivert(int n) 836 { 837 int fd; 838 839 oindex = n; 840 if (n >= maxout) { 841 if (mimic_gnu) 842 resizedivs(n + 10); 843 else 844 n = 0; /* bitbucket */ 845 } 846 847 if (n < 0) 848 n = 0; /* bitbucket */ 849 if (outfile[n] == NULL) { 850 char fname[] = _PATH_DIVNAME; 851 852 if ((fd = mkstemp(fname)) == -1 || 853 unlink(fname) == -1 || 854 (outfile[n] = fdopen(fd, "w+")) == NULL) 855 err(1, "%s: cannot divert", fname); 856 } 857 active = outfile[n]; 858 } 859 860 /* 861 * doundivert - undivert a specified output, or all 862 * other outputs, in numerical order. 863 */ 864 static void 865 doundivert(const char *argv[], int argc) 866 { 867 int ind; 868 int n; 869 870 if (argc > 2) { 871 for (ind = 2; ind < argc; ind++) { 872 int e; 873 n = (int)strtoi(argv[ind], NULL, 0, 1, INT_MAX, &e); 874 if (e) { 875 if (errno == EINVAL && mimic_gnu) 876 getdivfile(argv[ind]); 877 } else { 878 if (n < maxout && outfile[n] != NULL) 879 getdiv(n); 880 } 881 } 882 } 883 else 884 for (n = 1; n < maxout; n++) 885 if (outfile[n] != NULL) 886 getdiv(n); 887 } 888 889 /* 890 * dosubstr - select substring 891 */ 892 static void 893 dosubstr(const char *argv[], int argc) 894 { 895 const char *ap, *fc, *k; 896 int nc; 897 898 ap = argv[2]; /* target string */ 899 #ifdef EXPR 900 fc = ap + expr(argv[3]); /* first char */ 901 #else 902 fc = ap + atoi(argv[3]); /* first char */ 903 #endif 904 nc = strlen(fc); 905 if (argc >= 5) 906 #ifdef EXPR 907 nc = min(nc, expr(argv[4])); 908 #else 909 nc = min(nc, atoi(argv[4])); 910 #endif 911 if (fc >= ap && fc < ap + strlen(ap)) 912 for (k = fc + nc - 1; k >= fc; k--) 913 pushback(*k); 914 } 915 916 /* 917 * map: 918 * map every character of s1 that is specified in from 919 * into s3 and replace in s. (source s1 remains untouched) 920 * 921 * This is a standard implementation of map(s,from,to) function of ICON 922 * language. Within mapvec, we replace every character of "from" with 923 * the corresponding character in "to". If "to" is shorter than "from", 924 * than the corresponding entries are null, which means that those 925 * characters disappear altogether. Furthermore, imagine 926 * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, 927 * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' 928 * ultimately maps to `*'. In order to achieve this effect in an efficient 929 * manner (i.e. without multiple passes over the destination string), we 930 * loop over mapvec, starting with the initial source character. if the 931 * character value (dch) in this location is different than the source 932 * character (sch), sch becomes dch, once again to index into mapvec, until 933 * the character value stabilizes (i.e. sch = dch, in other words 934 * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary 935 * character, it will stabilize, since mapvec[0] == 0 at all times. At the 936 * end, we restore mapvec* back to normal where mapvec[n] == n for 937 * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is 938 * about 5 times faster than any algorithm that makes multiple passes over 939 * destination string. 940 */ 941 static void 942 map(char *dest, const char *src, const char *from, const char *to) 943 { 944 const char *tmp; 945 unsigned char sch, dch; 946 unsigned char found[256]; 947 static char frombis[257]; 948 static char tobis[257]; 949 static unsigned char mapvec[256] = { 950 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 951 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 952 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 953 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 954 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 955 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 956 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 957 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 958 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 959 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 960 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 961 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 962 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 963 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 964 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 965 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 966 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 967 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 968 }; 969 970 if (*src) { 971 if (mimic_gnu) { 972 /* 973 * expand character ranges on the fly 974 */ 975 from = handledash(frombis, frombis + 256, from); 976 to = handledash(tobis, tobis + 256, to); 977 } 978 tmp = from; 979 /* 980 * create a mapping between "from" and 981 * "to" 982 */ 983 memset(found, 0, sizeof(found)); 984 for (; (sch = (unsigned char)*from) != '\0'; from++) { 985 if (!mimic_gnu || !found[sch]) { 986 found[sch] = 1; 987 mapvec[sch] = *to; 988 } 989 if (*to) 990 to++; 991 } 992 993 if (mimic_gnu) { 994 for (; (sch = (unsigned char)*src) != '\0'; src++) { 995 if (!found[sch]) 996 *dest++ = sch; 997 else if ((dch = mapvec[sch]) != '\0') 998 *dest++ = dch; 999 } 1000 } else { 1001 while (*src) { 1002 sch = (unsigned char)(*src++); 1003 dch = mapvec[sch]; 1004 while (dch != sch) { 1005 sch = dch; 1006 dch = mapvec[sch]; 1007 } 1008 if ((*dest = (char)dch)) 1009 dest++; 1010 } 1011 } 1012 /* 1013 * restore all the changed characters 1014 */ 1015 while (*tmp) { 1016 mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp); 1017 tmp++; 1018 } 1019 } 1020 *dest = '\0'; 1021 } 1022 1023 1024 /* 1025 * handledash: 1026 * use buffer to copy the src string, expanding character ranges 1027 * on the way. 1028 */ 1029 static const char * 1030 handledash(char *buffer, char *end, const char *src) 1031 { 1032 char *p; 1033 1034 p = buffer; 1035 while(*src) { 1036 if (src[1] == '-' && src[2]) { 1037 unsigned char i; 1038 if ((unsigned char)src[0] <= (unsigned char)src[2]) { 1039 for (i = (unsigned char)src[0]; 1040 i <= (unsigned char)src[2]; i++) { 1041 *p++ = i; 1042 if (p == end) { 1043 *p = '\0'; 1044 return buffer; 1045 } 1046 } 1047 } else { 1048 for (i = (unsigned char)src[0]; 1049 i >= (unsigned char)src[2]; i--) { 1050 *p++ = i; 1051 if (p == end) { 1052 *p = '\0'; 1053 return buffer; 1054 } 1055 } 1056 } 1057 src += 3; 1058 } else 1059 *p++ = *src++; 1060 if (p == end) 1061 break; 1062 } 1063 *p = '\0'; 1064 return buffer; 1065 } 1066