eval.c revision 228697
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: head/usr.bin/m4/eval.c 228697 2011-12-18 22:04:55Z bapt $"); 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 62static void dodefn(const char *); 63static void dopushdef(const char *, const char *); 64static void dodump(const char *[], int); 65static void dotrace(const char *[], int, int); 66static void doifelse(const char *[], int); 67static int doincl(const char *); 68static int dopaste(const char *); 69static void dochq(const char *[], int); 70static void dochc(const char *[], int); 71static void dom4wrap(const char *); 72static void dodiv(int); 73static void doundiv(const char *[], int); 74static void dosub(const char *[], int); 75static void map(char *, const char *, const char *, const char *); 76static const char *handledash(char *, char *, const char *); 77static void expand_builtin(const char *[], int, int); 78static void expand_macro(const char *[], int); 79static void dump_one_def(const char *, struct macro_definition *); 80 81unsigned 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 */ 102void 103eval(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 */ 123void 124expand_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 else 276 err(1, "%s at line %lu: include(%s)", 277 CURRENT_NAME, CURRENT_LINE, argv[2]); 278 break; 279 280 case SINCTYPE: 281 if (argc > 2) 282 (void) doincl(argv[2]); 283 break; 284#ifdef EXTENDED 285 case PASTTYPE: 286 if (argc > 2) 287 if (!dopaste(argv[2])) 288 err(1, "%s at line %lu: paste(%s)", 289 CURRENT_NAME, CURRENT_LINE, argv[2]); 290 break; 291 292 case SPASTYPE: 293 if (argc > 2) 294 (void) dopaste(argv[2]); 295 break; 296 case FORMATTYPE: 297 doformat(argv, argc); 298 break; 299#endif 300 case CHNQTYPE: 301 dochq(argv, ac); 302 break; 303 304 case CHNCTYPE: 305 dochc(argv, argc); 306 break; 307 308 case SUBSTYPE: 309 /* 310 * dosub - select substring 311 * 312 */ 313 if (argc > 3) 314 dosub(argv, argc); 315 break; 316 317 case SHIFTYPE: 318 /* 319 * doshift - push back all arguments 320 * except the first one (i.e. skip 321 * argv[2]) 322 */ 323 if (argc > 3) { 324 for (n = argc - 1; n > 3; n--) { 325 pbstr(rquote); 326 pbstr(argv[n]); 327 pbstr(lquote); 328 pushback(COMMA); 329 } 330 pbstr(rquote); 331 pbstr(argv[3]); 332 pbstr(lquote); 333 } 334 break; 335 336 case DIVRTYPE: 337 if (argc > 2 && (n = atoi(argv[2])) != 0) 338 dodiv(n); 339 else { 340 active = stdout; 341 oindex = 0; 342 } 343 break; 344 345 case UNDVTYPE: 346 doundiv(argv, argc); 347 break; 348 349 case DIVNTYPE: 350 /* 351 * dodivnum - return the number of 352 * current output diversion 353 */ 354 pbnum(oindex); 355 break; 356 357 case UNDFTYPE: 358 /* 359 * doundefine - undefine a previously 360 * defined macro(s) or m4 keyword(s). 361 */ 362 if (argc > 2) 363 for (n = 2; n < argc; n++) 364 macro_undefine(argv[n]); 365 break; 366 367 case POPDTYPE: 368 /* 369 * dopopdef - remove the topmost 370 * definitions of macro(s) or m4 371 * keyword(s). 372 */ 373 if (argc > 2) 374 for (n = 2; n < argc; n++) 375 macro_popdef(argv[n]); 376 break; 377 378 case MKTMTYPE: 379 /* 380 * dotemp - create a temporary file 381 */ 382 if (argc > 2) { 383 int fd; 384 char *temp; 385 386 temp = xstrdup(argv[2]); 387 388 fd = mkstemp(temp); 389 if (fd == -1) 390 err(1, 391 "%s at line %lu: couldn't make temp file %s", 392 CURRENT_NAME, CURRENT_LINE, argv[2]); 393 close(fd); 394 pbstr(temp); 395 free(temp); 396 } 397 break; 398 399 case TRNLTYPE: 400 /* 401 * dotranslit - replace all characters in 402 * the source string that appears in the 403 * "from" string with the corresponding 404 * characters in the "to" string. 405 */ 406 if (argc > 3) { 407 char *temp; 408 409 temp = xalloc(strlen(argv[2])+1, NULL); 410 if (argc > 4) 411 map(temp, argv[2], argv[3], argv[4]); 412 else 413 map(temp, argv[2], argv[3], null); 414 pbstr(temp); 415 free(temp); 416 } else if (argc > 2) 417 pbstr(argv[2]); 418 break; 419 420 case INDXTYPE: 421 /* 422 * doindex - find the index of the second 423 * argument string in the first argument 424 * string. -1 if not present. 425 */ 426 pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); 427 break; 428 429 case ERRPTYPE: 430 /* 431 * doerrp - print the arguments to stderr 432 * file 433 */ 434 if (argc > 2) { 435 for (n = 2; n < argc; n++) 436 fprintf(stderr, "%s ", argv[n]); 437 fprintf(stderr, "\n"); 438 } 439 break; 440 441 case DNLNTYPE: 442 /* 443 * dodnl - eat-up-to and including 444 * newline 445 */ 446 while ((c = gpbc()) != '\n' && c != EOF) 447 ; 448 break; 449 450 case M4WRTYPE: 451 /* 452 * dom4wrap - set up for 453 * wrap-up/wind-down activity 454 */ 455 if (argc > 2) 456 dom4wrap(argv[2]); 457 break; 458 459 case EXITTYPE: 460 /* 461 * doexit - immediate exit from m4. 462 */ 463 killdiv(); 464 exit((argc > 2) ? atoi(argv[2]) : 0); 465 break; 466 467 case DEFNTYPE: 468 if (argc > 2) 469 for (n = 2; n < argc; n++) 470 dodefn(argv[n]); 471 break; 472 473 case INDIRTYPE: /* Indirect call */ 474 if (argc > 2) 475 doindir(argv, argc); 476 break; 477 478 case BUILTINTYPE: /* Builtins only */ 479 if (argc > 2) 480 dobuiltin(argv, argc); 481 break; 482 483 case PATSTYPE: 484 if (argc > 2) 485 dopatsubst(argv, argc); 486 break; 487 case REGEXPTYPE: 488 if (argc > 2) 489 doregexp(argv, argc); 490 break; 491 case LINETYPE: 492 doprintlineno(infile+ilevel); 493 break; 494 case FILENAMETYPE: 495 doprintfilename(infile+ilevel); 496 break; 497 case SELFTYPE: 498 pbstr(rquote); 499 pbstr(argv[1]); 500 pbstr(lquote); 501 break; 502 default: 503 m4errx(1, "eval: major botch."); 504 break; 505 } 506} 507 508/* 509 * expand_macro - user-defined macro expansion 510 */ 511void 512expand_macro(const char *argv[], int argc) 513{ 514 const char *t; 515 const char *p; 516 int n; 517 int argno; 518 519 t = argv[0]; /* defn string as a whole */ 520 p = t; 521 while (*p) 522 p++; 523 p--; /* last character of defn */ 524 while (p > t) { 525 if (*(p - 1) != ARGFLAG) 526 PUSHBACK(*p); 527 else { 528 switch (*p) { 529 530 case '#': 531 pbnum(argc - 2); 532 break; 533 case '0': 534 case '1': 535 case '2': 536 case '3': 537 case '4': 538 case '5': 539 case '6': 540 case '7': 541 case '8': 542 case '9': 543 if ((argno = *p - '0') < argc - 1) 544 pbstr(argv[argno + 1]); 545 break; 546 case '*': 547 if (argc > 2) { 548 for (n = argc - 1; n > 2; n--) { 549 pbstr(argv[n]); 550 pushback(COMMA); 551 } 552 pbstr(argv[2]); 553 } 554 break; 555 case '@': 556 if (argc > 2) { 557 for (n = argc - 1; n > 2; n--) { 558 pbstr(rquote); 559 pbstr(argv[n]); 560 pbstr(lquote); 561 pushback(COMMA); 562 } 563 pbstr(rquote); 564 pbstr(argv[2]); 565 pbstr(lquote); 566 } 567 break; 568 default: 569 PUSHBACK(*p); 570 PUSHBACK('$'); 571 break; 572 } 573 p--; 574 } 575 p--; 576 } 577 if (p == t) /* do last character */ 578 PUSHBACK(*p); 579} 580 581 582/* 583 * dodefine - install definition in the table 584 */ 585void 586dodefine(const char *name, const char *defn) 587{ 588 if (!*name && !mimic_gnu) 589 m4errx(1, "null definition."); 590 else 591 macro_define(name, defn); 592} 593 594/* 595 * dodefn - push back a quoted definition of 596 * the given name. 597 */ 598static void 599dodefn(const char *name) 600{ 601 struct macro_definition *p; 602 603 if ((p = lookup_macro_definition(name)) != NULL) { 604 if ((p->type & TYPEMASK) == MACRTYPE) { 605 pbstr(rquote); 606 pbstr(p->defn); 607 pbstr(lquote); 608 } else { 609 pbstr(p->defn); 610 pbstr(BUILTIN_MARKER); 611 } 612 } 613} 614 615/* 616 * dopushdef - install a definition in the hash table 617 * without removing a previous definition. Since 618 * each new entry is entered in *front* of the 619 * hash bucket, it hides a previous definition from 620 * lookup. 621 */ 622static void 623dopushdef(const char *name, const char *defn) 624{ 625 if (!*name && !mimic_gnu) 626 m4errx(1, "null definition."); 627 else 628 macro_pushdef(name, defn); 629} 630 631/* 632 * dump_one_def - dump the specified definition. 633 */ 634static void 635dump_one_def(const char *name, struct macro_definition *p) 636{ 637 if (!traceout) 638 traceout = stderr; 639 if (mimic_gnu) { 640 if ((p->type & TYPEMASK) == MACRTYPE) 641 fprintf(traceout, "%s:\t%s\n", name, p->defn); 642 else { 643 fprintf(traceout, "%s:\t<%s>\n", name, p->defn); 644 } 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 */ 654static void 655dodump(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 */ 671static void 672dotrace(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 */ 686static void 687doifelse(const char *argv[], int argc) 688{ 689 cycle { 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 */ 706static int 707doincl(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 */ 724static int 725dopaste(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 (void) fclose(pf); 736 emit_synchline(); 737 return (1); 738 } else 739 return (0); 740} 741#endif 742 743/* 744 * dochq - change quote characters 745 */ 746static void 747dochq(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 */ 765static void 766dochc(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 */ 787static void 788dom4wrap(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 */ 804static void 805dodiv(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 */ 835static void 836doundiv(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 */ 863static void 864dosub(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 */ 898static void 899map(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 */ 976static const char * 977handledash(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