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