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