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