eval.c revision 100014
1162413Ssam/* $OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $ */ 2178354Ssam/* $NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $ */ 3162413Ssam 4162413Ssam/* 5162413Ssam * Copyright (c) 1989, 1993 6162413Ssam * The Regents of the University of California. All rights reserved. 7162413Ssam * 8162413Ssam * This code is derived from software contributed to Berkeley by 9162413Ssam * Ozan Yigit at York University. 10162413Ssam * 11162413Ssam * Redistribution and use in source and binary forms, with or without 12162413Ssam * modification, are permitted provided that the following conditions 13162413Ssam * are met: 14162413Ssam * 1. Redistributions of source code must retain the above copyright 15162413Ssam * notice, this list of conditions and the following disclaimer. 16162413Ssam * 2. Redistributions in binary form must reproduce the above copyright 17162413Ssam * notice, this list of conditions and the following disclaimer in the 18162413Ssam * documentation and/or other materials provided with the distribution. 19162413Ssam * 3. All advertising materials mentioning features or use of this software 20162413Ssam * must display the following acknowledgement: 21162413Ssam * This product includes software developed by the University of 22162413Ssam * California, Berkeley and its contributors. 23162413Ssam * 4. Neither the name of the University nor the names of its contributors 24162413Ssam * may be used to endorse or promote products derived from this software 25162413Ssam * without specific prior written permission. 26162413Ssam * 27162413Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28162413Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29162413Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30162413Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31162413Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32162413Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33162413Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34162413Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35162413Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36162413Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37162413Ssam * SUCH DAMAGE. 38162413Ssam */ 39162413Ssam 40162413Ssam#ifndef lint 41162413Ssam#if 0 42162413Ssamstatic char sccsid[] = "@(#)eval.c 8.2 (Berkeley) 4/27/95"; 43162413Ssam#else 44162413Ssam#if 0 45162413Ssamstatic char rcsid[] = "$OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $"; 46185522Ssam#endif 47162413Ssam#endif 48162413Ssam#endif /* not lint */ 49162413Ssam 50162413Ssam#include <sys/cdefs.h> 51162413Ssam__FBSDID("$FreeBSD: head/usr.bin/m4/eval.c 100014 2002-07-15 02:15:12Z jmallett $"); 52162413Ssam 53162413Ssam/* 54162413Ssam * eval.c 55162413Ssam * Facility: m4 macro processor 56162413Ssam * by: oz 57162413Ssam */ 58162413Ssam 59185522Ssam#include <sys/types.h> 60162413Ssam#include <errno.h> 61162413Ssam#include <unistd.h> 62162413Ssam#include <stdio.h> 63162413Ssam#include <stdlib.h> 64162413Ssam#include <stddef.h> 65162413Ssam#include <string.h> 66162413Ssam#include <fcntl.h> 67162413Ssam#include <err.h> 68162413Ssam#include "mdef.h" 69162413Ssam#include "stdd.h" 70162413Ssam#include "extern.h" 71162413Ssam#include "pathnames.h" 72162413Ssam 73162413Ssam#define BUILTIN_MARKER "__builtin_" 74219315Sadrian 75162413Ssamstatic void dodefn(const char *); 76162413Ssamstatic void dopushdef(const char *, const char *); 77162413Ssamstatic void dodump(const char *[], int); 78162413Ssamstatic void dotrace(const char *[], int, int); 79162413Ssamstatic void doifelse(const char *[], int); 80162413Ssamstatic int doincl(const char *); 81162413Ssamstatic int dopaste(const char *); 82219315Sadrianstatic void gnu_dochq(const char *[], int); 83162413Ssamstatic void dochq(const char *[], int); 84162413Ssamstatic void gnu_dochc(const char *[], int); 85162413Ssamstatic void dochc(const char *[], int); 86162413Ssamstatic void dodiv(int); 87162413Ssamstatic void doundiv(const char *[], int); 88219942Sadrianstatic void dosub(const char *[], int); 89219942Sadrianstatic void map(char *, const char *, const char *, const char *); 90219948Sadrianstatic const char *handledash(char *, char *, const char *); 91219942Sadrianstatic void expand_builtin(const char *[], int, int); 92219942Sadrianstatic void expand_macro(const char *[], int); 93162413Ssamstatic void dump_one_def(ndptr); 94162413Ssam 95162413Ssamunsigned long expansion_id; 96162413Ssam 97162413Ssam/* 98162413Ssam * eval - eval all macros and builtins calls 99162413Ssam * argc - number of elements in argv. 100162413Ssam * argv - element vector : 101162413Ssam * argv[0] = definition of a user 102162413Ssam * macro or nil if built-in. 103162413Ssam * argv[1] = name of the macro or 104162413Ssam * built-in. 105162413Ssam * argv[2] = parameters to user-defined 106162413Ssam * . macro or built-in. 107162413Ssam * . 108162413Ssam * 109162413Ssam * A call in the form of macro-or-builtin() will result in: 110162413Ssam * argv[0] = nullstr 111162413Ssam * argv[1] = macro-or-builtin 112162413Ssam * argv[2] = nullstr 113162413Ssam * 114162413Ssam * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin 115162413Ssam */ 116162413Ssamvoid 117162413Ssameval(const char *argv[], int argc, int td) 118196935Ssam{ 119162413Ssam ssize_t mark = -1; 120162413Ssam 121162413Ssam expansion_id++; 122162413Ssam if (td & RECDEF) 123162413Ssam errx(1, "%s at line %lu: expanding recursive definition for %s", 124162413Ssam CURRENT_NAME, CURRENT_LINE, argv[1]); 125162413Ssam if (traced_macros && is_traced(argv[1])) 126162413Ssam mark = trace(argv, argc, infile+ilevel); 127162413Ssam if (td == MACRTYPE) 128162413Ssam expand_macro(argv, argc); 129162413Ssam else 130162413Ssam expand_builtin(argv, argc, td); 131162413Ssam if (mark != -1) 132162413Ssam finish_trace(mark); 133162413Ssam} 134162413Ssam 135162413Ssam/* 136162413Ssam * expand_builtin - evaluate built-in macros. 137162413Ssam */ 138162413Ssamvoid 139162413Ssamexpand_builtin(const char *argv[], int argc, int td) 140162413Ssam{ 141162413Ssam int c, n; 142162413Ssam int ac; 143162413Ssam static int sysval = 0; 144219315Sadrian 145184369Ssam#ifdef DEBUG 146184369Ssam printf("argc = %d\n", argc); 147184369Ssam for (n = 0; n < argc; n++) 148184369Ssam printf("argv[%d] = %s\n", n, argv[n]); 149184369Ssam fflush(stdout); 150184369Ssam#endif 151184369Ssam 152184369Ssam /* 153162413Ssam * if argc == 3 and argv[2] is null, then we 154162413Ssam * have macro-or-builtin() type call. We adjust 155162413Ssam * argc to avoid further checking.. 156162413Ssam */ 157162413Ssam ac = argc; 158162413Ssam 159162413Ssam if (argc == 3 && !*(argv[2])) 160162413Ssam argc--; 161162413Ssam 162162413Ssam switch (td & TYPEMASK) { 163162413Ssam 164162413Ssam case DEFITYPE: 165162413Ssam if (argc > 2) 166162413Ssam dodefine(argv[2], (argc > 3) ? argv[3] : null); 167162413Ssam break; 168162413Ssam 169162413Ssam case PUSDTYPE: 170162413Ssam if (argc > 2) 171185522Ssam dopushdef(argv[2], (argc > 3) ? argv[3] : null); 172162413Ssam break; 173162413Ssam 174162413Ssam case DUMPTYPE: 175162413Ssam dodump(argv, argc); 176220367Sadrian break; 177220367Sadrian 178220367Sadrian case TRACEONTYPE: 179220367Sadrian dotrace(argv, argc, 1); 180220367Sadrian break; 181162413Ssam 182162413Ssam case TRACEOFFTYPE: 183162413Ssam dotrace(argv, argc, 0); 184162413Ssam break; 185162413Ssam 186162413Ssam case EXPRTYPE: 187162413Ssam /* 188162413Ssam * doexpr - evaluate arithmetic 189168589Srwatson * expression 190168589Srwatson */ 191168589Srwatson if (argc > 2) 192168589Srwatson pbnum(expr(argv[2])); 193168589Srwatson break; 194168589Srwatson 195168589Srwatson case IFELTYPE: 196162413Ssam if (argc > 4) 197162413Ssam doifelse(argv, argc); 198162413Ssam break; 199162413Ssam 200162413Ssam case IFDFTYPE: 201162413Ssam /* 202162413Ssam * doifdef - select one of two 203162413Ssam * alternatives based on the existence of 204162413Ssam * another definition 205162413Ssam */ 206162413Ssam if (argc > 3) { 207162413Ssam if (lookup(argv[2]) != nil) 208162413Ssam pbstr(argv[3]); 209162413Ssam else if (argc > 4) 210162413Ssam pbstr(argv[4]); 211162413Ssam } 212162413Ssam break; 213162413Ssam 214162413Ssam case LENGTYPE: 215162413Ssam /* 216162413Ssam * dolen - find the length of the 217162413Ssam * argument 218162413Ssam */ 219162413Ssam pbnum((argc > 2) ? strlen(argv[2]) : 0); 220162413Ssam break; 221162413Ssam 222162413Ssam case INCRTYPE: 223162413Ssam /* 224162413Ssam * doincr - increment the value of the 225162413Ssam * argument 226162413Ssam */ 227162413Ssam if (argc > 2) 228162413Ssam pbnum(atoi(argv[2]) + 1); 229162413Ssam break; 230162413Ssam 231162413Ssam case DECRTYPE: 232162413Ssam /* 233162413Ssam * dodecr - decrement the value of the 234162413Ssam * argument 235162413Ssam */ 236162413Ssam if (argc > 2) 237162413Ssam pbnum(atoi(argv[2]) - 1); 238162413Ssam break; 239162413Ssam 240162413Ssam case SYSCTYPE: 241162413Ssam /* 242162413Ssam * dosys - execute system command 243162413Ssam */ 244162413Ssam if (argc > 2) 245162413Ssam sysval = system(argv[2]); 246162413Ssam break; 247162413Ssam 248162413Ssam case SYSVTYPE: 249162413Ssam /* 250162413Ssam * dosysval - return value of the last 251162413Ssam * system call. 252162413Ssam * 253185522Ssam */ 254162413Ssam pbnum(sysval); 255162413Ssam break; 256162413Ssam 257162413Ssam case ESYSCMDTYPE: 258162413Ssam if (argc > 2) 259162413Ssam doesyscmd(argv[2]); 260162413Ssam break; 261162413Ssam case INCLTYPE: 262162413Ssam if (argc > 2) 263162413Ssam if (!doincl(argv[2])) 264162413Ssam err(1, "%s at line %lu: include(%s)", 265162413Ssam CURRENT_NAME, CURRENT_LINE, argv[2]); 266195418Ssam break; 267162413Ssam 268162413Ssam case SINCTYPE: 269162413Ssam if (argc > 2) 270162413Ssam (void) doincl(argv[2]); 271162413Ssam break; 272162413Ssam#ifdef EXTENDED 273162413Ssam case PASTTYPE: 274162413Ssam if (argc > 2) 275162413Ssam if (!dopaste(argv[2])) 276162413Ssam err(1, "%s at line %lu: paste(%s)", 277185522Ssam CURRENT_NAME, CURRENT_LINE, argv[2]); 278162413Ssam break; 279162413Ssam 280162413Ssam case SPASTYPE: 281195418Ssam if (argc > 2) 282162413Ssam (void) dopaste(argv[2]); 283162413Ssam break; 284162413Ssam#endif 285162413Ssam case CHNQTYPE: 286162413Ssam if (mimic_gnu) 287162413Ssam gnu_dochq(argv, ac); 288162413Ssam else 289162413Ssam dochq(argv, argc); 290162413Ssam break; 291162413Ssam 292162413Ssam case CHNCTYPE: 293162413Ssam if (mimic_gnu) 294162413Ssam gnu_dochc(argv, ac); 295162413Ssam else 296162413Ssam dochc(argv, argc); 297162413Ssam break; 298162413Ssam 299162413Ssam case SUBSTYPE: 300162413Ssam /* 301162413Ssam * dosub - select substring 302162413Ssam * 303162413Ssam */ 304162413Ssam if (argc > 3) 305162413Ssam dosub(argv, argc); 306162413Ssam break; 307162413Ssam 308162413Ssam case SHIFTYPE: 309162413Ssam /* 310162413Ssam * doshift - push back all arguments 311162413Ssam * except the first one (i.e. skip 312162413Ssam * argv[2]) 313162413Ssam */ 314162413Ssam if (argc > 3) { 315162413Ssam for (n = argc - 1; n > 3; n--) { 316162413Ssam pbstr(rquote); 317162413Ssam pbstr(argv[n]); 318162413Ssam pbstr(lquote); 319162413Ssam putback(COMMA); 320162413Ssam } 321162413Ssam pbstr(rquote); 322162413Ssam pbstr(argv[3]); 323162413Ssam pbstr(lquote); 324162413Ssam } 325162413Ssam break; 326162413Ssam 327162413Ssam case DIVRTYPE: 328162413Ssam if (argc > 2 && (n = atoi(argv[2])) != 0) 329185522Ssam dodiv(n); 330162413Ssam else { 331162413Ssam active = stdout; 332195418Ssam oindex = 0; 333162413Ssam } 334162413Ssam break; 335162413Ssam 336162413Ssam case UNDVTYPE: 337162413Ssam doundiv(argv, argc); 338162413Ssam break; 339162413Ssam 340162413Ssam case DIVNTYPE: 341162413Ssam /* 342162413Ssam * dodivnum - return the number of 343185522Ssam * current output diversion 344162413Ssam */ 345162413Ssam pbnum(oindex); 346162413Ssam break; 347195418Ssam 348162413Ssam case UNDFTYPE: 349162413Ssam /* 350162413Ssam * doundefine - undefine a previously 351162413Ssam * defined macro(s) or m4 keyword(s). 352162413Ssam */ 353162413Ssam if (argc > 2) 354162413Ssam for (n = 2; n < argc; n++) 355162413Ssam remhash(argv[n], ALL); 356162413Ssam break; 357162413Ssam 358162413Ssam case POPDTYPE: 359162413Ssam /* 360162413Ssam * dopopdef - remove the topmost 361162413Ssam * definitions of macro(s) or m4 362162413Ssam * keyword(s). 363162413Ssam */ 364162413Ssam if (argc > 2) 365 for (n = 2; n < argc; n++) 366 remhash(argv[n], TOP); 367 break; 368 369 case MKTMTYPE: 370 /* 371 * dotemp - create a temporary file 372 */ 373 if (argc > 2) { 374 int fd; 375 char *temp; 376 377 temp = xstrdup(argv[2]); 378 379 fd = mkstemp(temp); 380 if (fd == -1) 381 err(1, 382 "%s at line %lu: couldn't make temp file %s", 383 CURRENT_NAME, CURRENT_LINE, argv[2]); 384 close(fd); 385 pbstr(temp); 386 free(temp); 387 } 388 break; 389 390 case TRNLTYPE: 391 /* 392 * dotranslit - replace all characters in 393 * the source string that appears in the 394 * "from" string with the corresponding 395 * characters in the "to" string. 396 */ 397 if (argc > 3) { 398 char *temp; 399 400 temp = xalloc(strlen(argv[2])+1); 401 if (argc > 4) 402 map(temp, argv[2], argv[3], argv[4]); 403 else 404 map(temp, argv[2], argv[3], null); 405 pbstr(temp); 406 free(temp); 407 } else if (argc > 2) 408 pbstr(argv[2]); 409 break; 410 411 case INDXTYPE: 412 /* 413 * doindex - find the index of the second 414 * argument string in the first argument 415 * string. -1 if not present. 416 */ 417 pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); 418 break; 419 420 case ERRPTYPE: 421 /* 422 * doerrp - print the arguments to stderr 423 * file 424 */ 425 if (argc > 2) { 426 for (n = 2; n < argc; n++) 427 fprintf(stderr, "%s ", argv[n]); 428 fprintf(stderr, "\n"); 429 } 430 break; 431 432 case DNLNTYPE: 433 /* 434 * dodnl - eat-up-to and including 435 * newline 436 */ 437 while ((c = gpbc()) != '\n' && c != EOF) 438 ; 439 break; 440 441 case M4WRTYPE: 442 /* 443 * dom4wrap - set up for 444 * wrap-up/wind-down activity 445 */ 446 m4wraps = (argc > 2) ? xstrdup(argv[2]) : null; 447 break; 448 449 case EXITTYPE: 450 /* 451 * doexit - immediate exit from m4. 452 */ 453 killdiv(); 454 exit((argc > 2) ? atoi(argv[2]) : 0); 455 break; 456 457 case DEFNTYPE: 458 if (argc > 2) 459 for (n = 2; n < argc; n++) 460 dodefn(argv[n]); 461 break; 462 463 case INDIRTYPE: /* Indirect call */ 464 if (argc > 2) 465 doindir(argv, argc); 466 break; 467 468 case BUILTINTYPE: /* Builtins only */ 469 if (argc > 2) 470 dobuiltin(argv, argc); 471 break; 472 473 case PATSTYPE: 474 if (argc > 2) 475 dopatsubst(argv, argc); 476 break; 477 case REGEXPTYPE: 478 if (argc > 2) 479 doregexp(argv, argc); 480 break; 481 case LINETYPE: 482 doprintlineno(infile+ilevel); 483 break; 484 case FILENAMETYPE: 485 doprintfilename(infile+ilevel); 486 break; 487 case SELFTYPE: 488 pbstr(rquote); 489 pbstr(argv[1]); 490 pbstr(lquote); 491 break; 492 default: 493 errx(1, "%s at line %lu: eval: major botch.", 494 CURRENT_NAME, CURRENT_LINE); 495 break; 496 } 497} 498 499/* 500 * expand_macro - user-defined macro expansion 501 */ 502void 503expand_macro(const char *argv[], int argc) 504{ 505 const char *t; 506 const char *p; 507 int n; 508 int argno; 509 510 t = argv[0]; /* defn string as a whole */ 511 p = t; 512 while (*p) 513 p++; 514 p--; /* last character of defn */ 515 while (p > t) { 516 if (*(p - 1) != ARGFLAG) 517 PUTBACK(*p); 518 else { 519 switch (*p) { 520 521 case '#': 522 pbnum(argc - 2); 523 break; 524 case '0': 525 case '1': 526 case '2': 527 case '3': 528 case '4': 529 case '5': 530 case '6': 531 case '7': 532 case '8': 533 case '9': 534 if ((argno = *p - '0') < argc - 1) 535 pbstr(argv[argno + 1]); 536 break; 537 case '*': 538 if (argc > 2) { 539 for (n = argc - 1; n > 2; n--) { 540 pbstr(argv[n]); 541 putback(COMMA); 542 } 543 pbstr(argv[2]); 544 } 545 break; 546 case '@': 547 if (argc > 2) { 548 for (n = argc - 1; n > 2; n--) { 549 pbstr(rquote); 550 pbstr(argv[n]); 551 pbstr(lquote); 552 putback(COMMA); 553 } 554 pbstr(rquote); 555 pbstr(argv[2]); 556 pbstr(lquote); 557 } 558 break; 559 default: 560 PUTBACK(*p); 561 PUTBACK('$'); 562 break; 563 } 564 p--; 565 } 566 p--; 567 } 568 if (p == t) /* do last character */ 569 PUTBACK(*p); 570} 571 572/* 573 * dodefine - install definition in the table 574 */ 575void 576dodefine(const char *name, const char *defn) 577{ 578 ndptr p; 579 int n; 580 581 if (!*name) 582 errx(1, "%s at line %lu: null definition.", CURRENT_NAME, 583 CURRENT_LINE); 584 if ((p = lookup(name)) == nil) 585 p = addent(name); 586 else if (p->defn != null) 587 free((char *) p->defn); 588 if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0) { 589 n = builtin_type(defn+sizeof(BUILTIN_MARKER)-1); 590 if (n != -1) { 591 p->type = n & TYPEMASK; 592 if ((n & NOARGS) == 0) 593 p->type |= NEEDARGS; 594 p->defn = xstrdup(null); 595 return; 596 } 597 } 598 if (!*defn) 599 p->defn = xstrdup(null); 600 else 601 p->defn = xstrdup(defn); 602 p->type = MACRTYPE; 603 if (STREQ(name, defn)) 604 p->type |= RECDEF; 605} 606 607/* 608 * dodefn - push back a quoted definition of 609 * the given name. 610 */ 611static void 612dodefn(const char *name) 613{ 614 ndptr p; 615 const char *real; 616 617 if ((p = lookup(name)) != nil) { 618 if (p->defn != null) { 619 pbstr(rquote); 620 pbstr(p->defn); 621 pbstr(lquote); 622 } else if ((real = builtin_realname(p->type)) != NULL) { 623 pbstr(real); 624 pbstr(BUILTIN_MARKER); 625 } 626 } 627} 628 629/* 630 * dopushdef - install a definition in the hash table 631 * without removing a previous definition. Since 632 * each new entry is entered in *front* of the 633 * hash bucket, it hides a previous definition from 634 * lookup. 635 */ 636static void 637dopushdef(const char *name, const char *defn) 638{ 639 ndptr p; 640 641 if (!*name) 642 errx(1, "%s at line %lu: null definition", CURRENT_NAME, 643 CURRENT_LINE); 644 p = addent(name); 645 if (!*defn) 646 p->defn = xstrdup(null); 647 else 648 p->defn = xstrdup(defn); 649 p->type = MACRTYPE; 650 if (STREQ(name, defn)) 651 p->type |= RECDEF; 652} 653 654/* 655 * dump_one_def - dump the specified definition. 656 */ 657static void 658dump_one_def(ndptr p) 659{ 660 const char *real; 661 662 if (mimic_gnu) { 663 if ((p->type & TYPEMASK) == MACRTYPE) 664 fprintf(traceout, "%s:\t%s\n", p->name, p->defn); 665 else { 666 real = builtin_realname(p->type); 667 if (real == NULL) 668 real = null; 669 fprintf(traceout, "%s:\t<%s>\n", p->name, real); 670 } 671 } else 672 fprintf(traceout, "`%s'\t`%s'\n", p->name, p->defn); 673} 674 675/* 676 * dodumpdef - dump the specified definitions in the hash 677 * table to stderr. If nothing is specified, the entire 678 * hash table is dumped. 679 */ 680static void 681dodump(const char *argv[], int argc) 682{ 683 int n; 684 ndptr p; 685 686 if (argc > 2) { 687 for (n = 2; n < argc; n++) 688 if ((p = lookup(argv[n])) != nil) 689 dump_one_def(p); 690 } else { 691 for (n = 0; n < HASHSIZE; n++) 692 for (p = hashtab[n]; p != nil; p = p->nxtptr) 693 dump_one_def(p); 694 } 695} 696 697/* 698 * dotrace - mark some macros as traced/untraced depending upon on. 699 */ 700static void 701dotrace(const char *argv[], int argc, int on) 702{ 703 int n; 704 705 if (argc > 2) { 706 for (n = 2; n < argc; n++) 707 mark_traced(argv[n], on); 708 } else 709 mark_traced(NULL, on); 710} 711 712/* 713 * doifelse - select one of two alternatives - loop. 714 */ 715static void 716doifelse(const char *argv[], int argc) 717{ 718 cycle { 719 if (STREQ(argv[2], argv[3])) 720 pbstr(argv[4]); 721 else if (argc == 6) 722 pbstr(argv[5]); 723 else if (argc > 6) { 724 argv += 3; 725 argc -= 3; 726 continue; 727 } 728 break; 729 } 730} 731 732/* 733 * doinclude - include a given file. 734 */ 735static int 736doincl(const char *ifile) 737{ 738 if (ilevel + 1 == MAXINP) 739 errx(1, "%s at line %lu: too many include files.", 740 CURRENT_NAME, CURRENT_LINE); 741 if (fopen_trypath(infile+ilevel+1, ifile) != NULL) { 742 ilevel++; 743 if ((inname[ilevel] = strdup(ifile)) == NULL) 744 err(1, NULL); 745 inlineno[ilevel] = 1; 746 bbase[ilevel] = bufbase = bp; 747 emitline(); 748 return (1); 749 } else 750 return (0); 751} 752 753#ifdef EXTENDED 754/* 755 * dopaste - include a given file without any 756 * macro processing. 757 */ 758static int 759dopaste(const char *pfile) 760{ 761 FILE *pf; 762 int c; 763 764 if ((pf = fopen(pfile, "r")) != NULL) { 765 fprintf(active, "#line 1 \"%s\"\n", pfile); 766 while ((c = getc(pf)) != EOF) 767 putc(c, active); 768 (void) fclose(pf); 769 emitline(); 770 return (1); 771 } else 772 return (0); 773} 774#endif 775 776static void 777gnu_dochq(const char *argv[], int ac) 778{ 779 /* In gnu-m4 mode, the only way to restore quotes is to have no 780 * arguments at all. */ 781 if (ac == 2) { 782 lquote[0] = LQUOTE, lquote[1] = EOS; 783 rquote[0] = RQUOTE, rquote[1] = EOS; 784 } else { 785 strlcpy(lquote, argv[2], sizeof(lquote)); 786 if(ac > 3) 787 strlcpy(rquote, argv[3], sizeof(rquote)); 788 else 789 rquote[0] = EOS; 790 } 791} 792 793/* 794 * dochq - change quote characters 795 */ 796static void 797dochq(const char *argv[], int argc) 798{ 799 if (argc > 2) { 800 if (*argv[2]) 801 strlcpy(lquote, argv[2], sizeof(lquote)); 802 else { 803 lquote[0] = LQUOTE; 804 lquote[1] = EOS; 805 } 806 if (argc > 3) { 807 if (*argv[3]) 808 strlcpy(rquote, argv[3], sizeof(rquote)); 809 } else 810 strcpy(rquote, lquote); 811 } else { 812 lquote[0] = LQUOTE, lquote[1] = EOS; 813 rquote[0] = RQUOTE, rquote[1] = EOS; 814 } 815} 816 817static void 818gnu_dochc(const char *argv[], int ac) 819{ 820 /* In gnu-m4 mode, no arguments mean no comment 821 * arguments at all. */ 822 if (ac == 2) { 823 scommt[0] = EOS; 824 ecommt[0] = EOS; 825 } else { 826 if (*argv[2]) 827 strlcpy(scommt, argv[2], sizeof(scommt)); 828 else 829 scommt[0] = SCOMMT, scommt[1] = EOS; 830 if(ac > 3 && *argv[3]) 831 strlcpy(ecommt, argv[3], sizeof(ecommt)); 832 else 833 ecommt[0] = ECOMMT, ecommt[1] = EOS; 834 } 835} 836/* 837 * dochc - change comment characters 838 */ 839static void 840dochc(const char *argv[], int argc) 841{ 842 if (argc > 2) { 843 if (*argv[2]) 844 strlcpy(scommt, argv[2], sizeof(scommt)); 845 if (argc > 3) { 846 if (*argv[3]) 847 strlcpy(ecommt, argv[3], sizeof(ecommt)); 848 } 849 else 850 ecommt[0] = ECOMMT, ecommt[1] = EOS; 851 } 852 else { 853 scommt[0] = SCOMMT, scommt[1] = EOS; 854 ecommt[0] = ECOMMT, ecommt[1] = EOS; 855 } 856} 857 858/* 859 * dodivert - divert the output to a temporary file 860 */ 861static void 862dodiv(int n) 863{ 864 int fd; 865 866 oindex = n; 867 if (n >= maxout) { 868 if (mimic_gnu) 869 resizedivs(n + 10); 870 else 871 n = 0; /* bitbucket */ 872 } 873 874 if (n < 0) 875 n = 0; /* bitbucket */ 876 if (outfile[n] == NULL) { 877 char fname[] = _PATH_DIVNAME; 878 879 if ((fd = mkstemp(fname)) < 0 || 880 (outfile[n] = fdopen(fd, "w+")) == NULL) 881 err(1, "%s: cannot divert", fname); 882 if (unlink(fname) == -1) 883 err(1, "%s: cannot unlink", fname); 884 } 885 active = outfile[n]; 886} 887 888/* 889 * doundivert - undivert a specified output, or all 890 * other outputs, in numerical order. 891 */ 892static void 893doundiv(const char *argv[], int argc) 894{ 895 int ind; 896 int n; 897 898 if (argc > 2) { 899 for (ind = 2; ind < argc; ind++) { 900 n = atoi(argv[ind]); 901 if (n > 0 && n < maxout && outfile[n] != NULL) 902 getdiv(n); 903 904 } 905 } 906 else 907 for (n = 1; n < maxout; n++) 908 if (outfile[n] != NULL) 909 getdiv(n); 910} 911 912/* 913 * dosub - select substring 914 */ 915static void 916dosub(const char *argv[], int argc) 917{ 918 const char *ap, *fc, *k; 919 int nc; 920 921 ap = argv[2]; /* target string */ 922#ifdef EXPR 923 fc = ap + expr(argv[3]); /* first char */ 924#else 925 fc = ap + atoi(argv[3]); /* first char */ 926#endif 927 nc = strlen(fc); 928 if (argc >= 5) 929#ifdef EXPR 930 nc = min(nc, expr(argv[4])); 931#else 932 nc = min(nc, atoi(argv[4])); 933#endif 934 if (fc >= ap && fc < ap + strlen(ap)) 935 for (k = fc + nc - 1; k >= fc; k--) 936 putback(*k); 937} 938 939/* 940 * map: 941 * map every character of s1 that is specified in from 942 * into s3 and replace in s. (source s1 remains untouched) 943 * 944 * This is a standard implementation of map(s,from,to) function of ICON 945 * language. Within mapvec, we replace every character of "from" with 946 * the corresponding character in "to". If "to" is shorter than "from", 947 * than the corresponding entries are null, which means that those 948 * characters dissapear altogether. Furthermore, imagine 949 * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, 950 * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' 951 * ultimately maps to `*'. In order to achieve this effect in an efficient 952 * manner (i.e. without multiple passes over the destination string), we 953 * loop over mapvec, starting with the initial source character. if the 954 * character value (dch) in this location is different than the source 955 * character (sch), sch becomes dch, once again to index into mapvec, until 956 * the character value stabilizes (i.e. sch = dch, in other words 957 * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary 958 * character, it will stabilize, since mapvec[0] == 0 at all times. At the 959 * end, we restore mapvec* back to normal where mapvec[n] == n for 960 * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is 961 * about 5 times faster than any algorithm that makes multiple passes over 962 * destination string. 963 */ 964static void 965map(char *dest, const char *src, const char *from, const char *to) 966{ 967 const char *tmp; 968 unsigned char sch, dch; 969 static char frombis[257]; 970 static char tobis[257]; 971 static unsigned char mapvec[256] = { 972 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 973 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 974 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 975 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 976 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 977 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 978 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 979 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 980 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 981 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 982 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 983 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 984 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 985 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 986 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 987 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 988 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 989 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 990 }; 991 992 if (*src) { 993 if (mimic_gnu) { 994 /* 995 * expand character ranges on the fly 996 */ 997 from = handledash(frombis, frombis + 256, from); 998 to = handledash(tobis, tobis + 256, to); 999 } 1000 tmp = from; 1001 /* 1002 * create a mapping between "from" and 1003 * "to" 1004 */ 1005 while (*from) 1006 mapvec[(unsigned char)(*from++)] = (*to) ? 1007 (unsigned char)(*to++) : 0; 1008 1009 while (*src) { 1010 sch = (unsigned char)(*src++); 1011 dch = mapvec[sch]; 1012 while (dch != sch) { 1013 sch = dch; 1014 dch = mapvec[sch]; 1015 } 1016 if ((*dest = (char)dch)) 1017 dest++; 1018 } 1019 /* 1020 * restore all the changed characters 1021 */ 1022 while (*tmp) { 1023 mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp); 1024 tmp++; 1025 } 1026 } 1027 *dest = '\0'; 1028} 1029 1030 1031/* 1032 * handledash: 1033 * use buffer to copy the src string, expanding character ranges 1034 * on the way. 1035 */ 1036static const char * 1037handledash(char *buffer, char *end, const char *src) 1038{ 1039 char *p; 1040 1041 p = buffer; 1042 while(*src) { 1043 if (src[1] == '-' && src[2]) { 1044 unsigned char i; 1045 for (i = (unsigned char)src[0]; 1046 i <= (unsigned char)src[2]; i++) { 1047 *p++ = i; 1048 if (p == end) { 1049 *p = '\0'; 1050 return buffer; 1051 } 1052 } 1053 src += 3; 1054 } else 1055 *p++ = *src++; 1056 if (p == end) 1057 break; 1058 } 1059 *p = '\0'; 1060 return buffer; 1061} 1062