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