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