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