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