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