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