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