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