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