unifdef.c revision 103906
1/* 2 * Copyright (c) 1985, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Dave Yost. Support for #if and #elif was added by Tony Finch. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#include <sys/cdefs.h> 38 39#ifndef lint 40static const char copyright[] = 41"@(#) Copyright (c) 1985, 1993\n\ 42 The Regents of the University of California. All rights reserved.\n"; 43 44#ifdef __IDSTRING 45__IDSTRING(Berkeley, "@(#)unifdef.c 8.1 (Berkeley) 6/6/93"); 46__IDSTRING(NetBSD, "$NetBSD: unifdef.c,v 1.8 2000/07/03 02:51:36 matt Exp $"); 47__IDSTRING(dotat, "$dotat: things/unifdef.c,v 1.75 2002/09/24 19:16:29 fanf2 Exp $"); 48#endif 49#ifdef __FBSDID 50__FBSDID("$FreeBSD: head/usr.bin/unifdef/unifdef.c 103906 2002-09-24 19:27:44Z fanf $"); 51#endif 52#endif 53 54/* 55 * unifdef - remove ifdef'ed lines 56 * 57 * Warning: will not work correctly if input contains nul characters. 58 * 59 * Wishlist: 60 * provide an option which will append the name of the 61 * appropriate symbol after #else's and #endif's 62 * provide an option which will check symbols after 63 * #else's and #endif's to see that they match their 64 * corresponding #ifdef or #ifndef 65 * generate #line directives in place of deleted code 66 */ 67 68#include <ctype.h> 69#include <err.h> 70#include <stdarg.h> 71#include <stdbool.h> 72#include <stdio.h> 73#include <stdlib.h> 74#include <string.h> 75#include <unistd.h> 76 77/* types of input lines: */ 78typedef enum { 79 LT_PLAIN, /* ordinary line */ 80 LT_TRUE, /* a true #if */ 81 LT_FALSE, /* a false #if */ 82 LT_ELTRUE, /* a true #elif */ 83 LT_ELFALSE, /* a false #elif */ 84 LT_IF, /* an unknown #if */ 85 LT_ELIF, /* an unknown #elif */ 86 LT_ELSE, /* #else */ 87 LT_ENDIF, /* #endif */ 88 LT_EOF /* end of file */ 89} Linetype; 90 91typedef enum { /* 0 or 1: pass thru; 1 or 2: ignore comments */ 92 REJ_NO, 93 REJ_IGNORE, 94 REJ_YES 95} Reject_level; 96 97typedef enum { 98 NO_COMMENT = false, 99 C_COMMENT, 100 CXX_COMMENT 101} Comment_state; 102 103typedef enum { 104 QUOTE_NONE = false, 105 QUOTE_SINGLE, 106 QUOTE_DOUBLE 107} Quote_state; 108 109const char *const errs[] = { 110#define NO_ERR 0 111 "", 112#define END_ERR 1 113 "", 114#define ELIF_ERR 2 115 "Inappropriate elif", 116#define ELSE_ERR 3 117 "Inappropriate else", 118#define ENDIF_ERR 4 119 "Inappropriate endif", 120#define IEOF_ERR 5 121 "Premature EOF in ifdef", 122#define CEOF_ERR 6 123 "Premature EOF in comment", 124#define Q1EOF_ERR 7 125 "Premature EOF in quoted character", 126#define Q2EOF_ERR 8 127 "Premature EOF in quoted string" 128}; 129 130/* 131 * These are the operators that are supported by the expression evaluator. 132 */ 133static int op_lt(int a, int b) { return a < b; } 134static int op_gt(int a, int b) { return a > b; } 135static int op_le(int a, int b) { return a <= b; } 136static int op_ge(int a, int b) { return a >= b; } 137static int op_eq(int a, int b) { return a == b; } 138static int op_ne(int a, int b) { return a != b; } 139static int op_or(int a, int b) { return a || b; } 140static int op_and(int a, int b) { return a && b; } 141 142struct ops; 143 144/* 145 * An evaluation function takes three arguments, as follows: (1) a pointer to 146 * an element of the precedence table which lists the operators at the current 147 * level of precedence; (2) a pointer to an integer which will receive the 148 * value of the expression; and (3) a pointer to a char* that points to the 149 * expression to be evaluated and that is updated to the end of the expression 150 * when evaluation is complete. The function returns LT_FALSE if the value of 151 * the expression is zero, LT_TRUE if it is non-zero, or LT_IF if the 152 * expression could not be evaluated. 153 */ 154typedef Linetype eval_fn(struct ops *, int *, const char **); 155 156eval_fn eval_table, eval_unary; 157 158/* 159 * The precedence table. Expressions involving binary operators are evaluated 160 * in a table-driven way by eval_table. When it evaluates a subexpression it 161 * calls the inner function with its first argument pointing to the next 162 * element of the table. Innermost expressions have special non-table-driven 163 * handling. 164 */ 165struct ops { 166 eval_fn *inner; 167 struct op { 168 const char *str; 169 int (*fn)(int, int); 170 } op[5]; 171} eval_ops[] = { 172 { eval_table, { { "||", op_or } } }, 173 { eval_table, { { "&&", op_and } } }, 174 { eval_table, { { "==", op_eq }, 175 { "!=", op_ne } } }, 176 { eval_unary, { { "<=", op_le }, 177 { ">=", op_ge }, 178 { "<", op_lt }, 179 { ">", op_gt } } } 180}; 181 182FILE *input; 183const char *filename; 184int linenum; /* current line number */ 185int stifline; /* start of current #if */ 186int stqcline; /* start of current coment or quote */ 187bool keepthis; /* ignore this #if's value 'cause it's const */ 188 189#define MAXLINE 1024 190#define KWSIZE 8 191/* tline has extra space so that it isn't overflowed when editing #elifs */ 192char tline[MAXLINE+KWSIZE]; /* input buffer */ 193char *keyword; /* used for editing #elif's */ 194 195bool complement; /* -c option in effect: do the complement */ 196bool debugging; /* -d option in effect: debugging reports */ 197bool killconsts; /* -k option in effect: eval constant #ifs */ 198bool lnblank; /* -l option in effect: blank deleted lines */ 199bool symlist; /* -s option in effect: output symbol list */ 200bool text; /* -t option in effect: this is a text file */ 201 202int exitstat; /* program exit status */ 203 204#define MAXSYMS 1000 205const char *symname[MAXSYMS]; /* symbol name */ 206const char *value[MAXSYMS]; /* -Dsym=value */ 207bool ignore[MAXSYMS]; /* -iDsym or -iUsym */ 208 209int nsyms = 1; /* symbol 0 is used for tracking #ifs */ 210 211Reject_level reject; /* what kind of filtering we are doing */ 212Comment_state incomment; /* inside C comment */ 213Quote_state inquote; /* inside single or double quotes */ 214 215Linetype checkline(int *); 216void debug(const char *, ...); 217Linetype process(int); 218void doif(int, Linetype, bool); 219void elif2if(void); 220void elif2endif(void); 221void error(int, int); 222void addsym(bool, bool, char *); 223int findsym(const char *); 224void flushline(bool); 225int getline(char *, int, FILE *, bool); 226Linetype ifeval(const char **); 227int main(int, char **); 228const char *skipcomment(const char *); 229const char *skipquote(const char *, Quote_state); 230const char *skipsym(const char *); 231void usage(void); 232 233#define endsym(c) (!isalpha((unsigned char)c) && !isdigit((unsigned char)c) && c != '_') 234 235int 236main(int argc, char *argv[]) 237{ 238 int opt; 239 240 while ((opt = getopt(argc, argv, "i:D:U:I:cdklst")) != -1) 241 switch (opt) { 242 case 'i': /* treat stuff controlled by these symbols as text */ 243 /* 244 * For strict backwards-compatibility the U or D 245 * should be immediately after the -i but it doesn't 246 * matter much if we relax that requirement. 247 */ 248 opt = *optarg++; 249 if (opt == 'D') 250 addsym(true, true, optarg); 251 else if (opt == 'U') 252 addsym(true, false, optarg); 253 else 254 usage(); 255 break; 256 case 'D': /* define a symbol */ 257 addsym(false, true, optarg); 258 break; 259 case 'U': /* undef a symbol */ 260 addsym(false, false, optarg); 261 break; 262 case 'I': 263 /* ignore for compatibility with cpp */ 264 break; 265 case 'c': /* treat -D as -U and vice versa */ 266 complement = true; 267 break; 268 case 'k': /* process constant #ifs */ 269 killconsts = true; 270 break; 271 case 'd': 272 debugging = true; 273 break; 274 case 'l': /* blank deleted lines instead of omitting them */ 275 lnblank = true; 276 break; 277 case 's': /* only output list of symbols that control #ifs */ 278 symlist = true; 279 break; 280 case 't': /* don't parse C comments or strings */ 281 text = true; 282 break; 283 default: 284 usage(); 285 } 286 argc -= optind; 287 argv += optind; 288 if (nsyms == 1 && !symlist) { 289 warnx("must -D or -U at least one symbol"); 290 usage(); 291 } 292 if (argc > 1) { 293 errx(2, "can only do one file"); 294 } else if (argc == 1 && strcmp(*argv, "-") != 0) { 295 filename = *argv; 296 if ((input = fopen(filename, "r")) != NULL) { 297 (void) process(0); 298 (void) fclose(input); 299 } else 300 err(2, "can't open %s", *argv); 301 } else { 302 filename = "[stdin]"; 303 input = stdin; 304 (void) process(0); 305 } 306 307 exit(exitstat); 308} 309 310void 311usage(void) 312{ 313 fprintf (stderr, "usage: %s", 314"unifdef [-cdklst] [[-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym]] ... [file]\n"); 315 exit (2); 316} 317 318/* 319 * This function processes #if lines and alters the pass-through 320 * state accordingly. All the complicated state transition suff is 321 * dealt with in this function, as well as checking that the 322 * #if/#elif/#else/#endif lines happen in the correct order. Lines 323 * between #if lines are handled by a recursive call to process(). 324 */ 325void 326doif(int depth, Linetype lineval, bool ignoring) 327{ 328 Reject_level savereject; 329 bool active; 330 bool donetrue; 331 bool inelse; 332 int saveline; 333 334 debug("#if line %d code %d depth %d", 335 linenum, lineval, depth); 336 saveline = stifline; 337 stifline = linenum; 338 savereject = reject; 339 inelse = false; 340 donetrue = false; 341 if (lineval == LT_IF || reject != REJ_NO) { 342 active = false; 343 ignoring = false; 344 flushline(true); 345 } else if (ignoring) { 346 active = false; 347 flushline(true); 348 if (lineval == LT_FALSE) 349 reject = REJ_IGNORE; 350 else 351 donetrue = true; 352 } else { 353 active = true; 354 flushline(false); 355 if (lineval == LT_FALSE) 356 reject = REJ_YES; 357 else 358 donetrue = true; 359 } 360 debug("active %d ignore %d", active, ignoring); 361 for (;;) { 362 switch (lineval = process(depth)) { 363 case LT_ELIF: 364 debug("#elif start %d line %d code %d depth %d", 365 stifline, linenum, lineval, depth); 366 if (inelse) 367 error(ELIF_ERR, depth); 368 donetrue = false; 369 reject = savereject; 370 if (active) { 371 active = false; 372 elif2if(); 373 flushline(true); 374 } else { 375 ignoring = false; 376 flushline(true); 377 } 378 debug("active %d ignore %d", active, ignoring); 379 break; 380 case LT_ELTRUE: 381 case LT_ELFALSE: 382 debug("#elif start %d line %d code %d depth %d", 383 stifline, linenum, lineval, depth); 384 if (inelse) 385 error(ELIF_ERR, depth); 386 if (active) { 387 flushline(false); 388 } else { 389 ignoring = false; 390 active = true; 391 elif2endif(); 392 flushline(true); 393 } 394 if (lineval == LT_ELFALSE) 395 reject = REJ_YES; 396 else { 397 reject = REJ_NO; 398 donetrue = true; 399 } 400 debug("active %d ignore %d", active, ignoring); 401 break; 402 case LT_ELSE: 403 debug("#else start %d line %d code %d depth %d", 404 stifline, linenum, lineval, depth); 405 if (inelse) 406 error(ELSE_ERR, depth); 407 if (active) { 408 flushline(false); 409 if (reject == REJ_YES && !donetrue) 410 reject = REJ_NO; 411 else 412 reject = REJ_YES; 413 } else { 414 flushline(true); 415 if (ignoring) { 416 if (reject == REJ_IGNORE) 417 reject = REJ_NO; 418 else 419 reject = REJ_IGNORE; 420 } 421 } 422 inelse = true; 423 debug("active %d ignore %d", active, ignoring); 424 break; 425 case LT_ENDIF: 426 debug("#endif start %d line %d code %d depth %d", 427 stifline, linenum, lineval, depth); 428 if (active) 429 flushline(false); 430 else 431 flushline(true); 432 reject = savereject; 433 stifline = saveline; 434 return; 435 default: 436 /* bug */ 437 abort(); 438 } 439 } 440} 441 442/* 443 * The main file processing routine. This function deals with passing 444 * through normal non-#if lines, correct nesting of #if sections, and 445 * checking that things terminate correctly at the end of file. The 446 * complicated stuff is delegated to doif(). 447 */ 448Linetype 449process(int depth) 450{ 451 Linetype lineval; 452 int cursym; 453 454 for (;;) { 455 linenum++; 456 if (getline(tline, MAXLINE, input, false) == EOF) { 457 if (incomment) 458 error(CEOF_ERR, depth); 459 if (inquote == QUOTE_SINGLE) 460 error(Q1EOF_ERR, depth); 461 if (inquote == QUOTE_DOUBLE) 462 error(Q2EOF_ERR, depth); 463 if (depth != 0) 464 error(IEOF_ERR, depth); 465 return LT_EOF; 466 } 467 switch (lineval = checkline(&cursym)) { 468 case LT_PLAIN: 469 flushline(true); 470 break; 471 case LT_IF: 472 case LT_TRUE: 473 case LT_FALSE: 474 doif(depth + 1, lineval, ignore[cursym]); 475 break; 476 case LT_ELIF: 477 case LT_ELTRUE: 478 case LT_ELFALSE: 479 case LT_ELSE: 480 case LT_ENDIF: 481 if (depth != 0) 482 return lineval; 483 if (lineval == LT_ENDIF) 484 error(ENDIF_ERR, depth); 485 if (lineval == LT_ELSE) 486 error(ELSE_ERR, depth); 487 error(ELIF_ERR, depth); 488 default: 489 /* bug */ 490 abort(); 491 } 492 } 493} 494 495/* 496 * Parse a line and determine its type. 497 */ 498Linetype 499checkline(int *cursym) 500{ 501 const char *cp; 502 char *symp; 503 Linetype retval; 504 char kw[KWSIZE]; 505 506 retval = LT_PLAIN; 507 cp = skipcomment(tline); 508 if (*cp != '#' 509 || incomment 510 || inquote == QUOTE_SINGLE 511 || inquote == QUOTE_DOUBLE 512 ) 513 goto eol; 514 515 cp = skipcomment(++cp); 516 keyword = (char *)cp; 517 symp = kw; 518 while (!endsym(*cp)) { 519 *symp = *cp++; 520 if (++symp >= &kw[KWSIZE]) 521 goto eol; 522 } 523 *symp = '\0'; 524 525 if (strcmp(kw, "ifdef") == 0) { 526 retval = LT_TRUE; 527 goto ifdef; 528 } else if (strcmp(kw, "ifndef") == 0) { 529 retval = LT_FALSE; 530 ifdef: 531 cp = skipcomment(++cp); 532 if (incomment) { 533 retval = LT_PLAIN; 534 goto eol; 535 } 536 if ((*cursym = findsym(cp)) == 0) 537 retval = LT_IF; 538 else if (value[*cursym] == NULL) 539 retval = (retval == LT_TRUE) 540 ? LT_FALSE : LT_TRUE; 541 } else if (strcmp(kw, "if") == 0) { 542 retval = ifeval(&cp); 543 cp = skipcomment(cp); 544 if (*cp != '\n' || keepthis) 545 retval = LT_IF; 546 *cursym = 0; 547 } else if (strcmp(kw, "elif") == 0) { 548 retval = ifeval(&cp); 549 cp = skipcomment(cp); 550 if (*cp != '\n' || keepthis) 551 retval = LT_ELIF; 552 if (retval == LT_IF) 553 retval = LT_ELIF; 554 if (retval == LT_TRUE) 555 retval = LT_ELTRUE; 556 if (retval == LT_FALSE) 557 retval = LT_ELFALSE; 558 *cursym = 0; 559 } else if (strcmp(kw, "else") == 0) 560 retval = LT_ELSE; 561 else if (strcmp(kw, "endif") == 0) 562 retval = LT_ENDIF; 563 564eol: 565 if (!text && reject != REJ_IGNORE) 566 for (; *cp;) { 567 if (incomment) 568 cp = skipcomment(cp); 569 else if (inquote == QUOTE_SINGLE) 570 cp = skipquote(cp, QUOTE_SINGLE); 571 else if (inquote == QUOTE_DOUBLE) 572 cp = skipquote(cp, QUOTE_DOUBLE); 573 else if (*cp == '/' && (cp[1] == '*' || cp[1] == '/')) 574 cp = skipcomment(cp); 575 else if (*cp == '\'') 576 cp = skipquote(cp, QUOTE_SINGLE); 577 else if (*cp == '"') 578 cp = skipquote(cp, QUOTE_DOUBLE); 579 else 580 cp++; 581 } 582 return retval; 583} 584 585/* 586 * Turn a #elif line into a #if. This function is used when we are 587 * processing a #if/#elif/#else/#endif sequence that starts off with a 588 * #if that we understand (and therefore it has been deleted) which is 589 * followed by a #elif that we don't understand and therefore must be 590 * kept. We turn it into a #if to keep the nesting correct. 591 */ 592void 593elif2if(void) 594{ 595 strncpy(keyword, "if ", 4); 596} 597 598/* 599 * Turn a #elif line into a #endif. This is used in the opposite 600 * situation to elif2if, i.e. a #if that we don't understand is 601 * followed by a #elif that we do; rather than deleting the #elif (as 602 * we would for a #if) we turn it into a #endif to keep the nesting 603 * correct. 604 */ 605void 606elif2endif(void) 607{ 608 strcpy(keyword, "endif\n"); 609} 610 611/* 612 * Function for evaluating the innermost parts of expressions, 613 * viz. !expr (expr) defined(symbol) symbol number 614 * We reset the keepthis flag when we find a non-constant subexpression. 615 */ 616Linetype 617eval_unary(struct ops *ops, int *valp, const char **cpp) 618{ 619 const char *cp; 620 char *ep; 621 int sym; 622 623 cp = skipcomment(*cpp); 624 if(*cp == '!') { 625 debug("eval%d !", ops - eval_ops); 626 cp++; 627 if (eval_unary(ops, valp, &cp) == LT_IF) 628 return LT_IF; 629 *valp = !*valp; 630 } else if (*cp == '(') { 631 cp++; 632 debug("eval%d (", ops - eval_ops); 633 if (eval_table(eval_ops, valp, &cp) == LT_IF) 634 return LT_IF; 635 cp = skipcomment(cp); 636 if (*cp++ != ')') 637 return LT_IF; 638 } else if (isdigit((unsigned char)*cp)) { 639 debug("eval%d number", ops - eval_ops); 640 *valp = strtol(cp, &ep, 0); 641 cp = skipsym(cp); 642 } else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) { 643 cp = skipcomment(cp+7); 644 debug("eval%d defined", ops - eval_ops); 645 if (*cp++ != '(') 646 return LT_IF; 647 cp = skipcomment(cp); 648 sym = findsym(cp); 649 if (sym == 0 && !symlist) 650 return LT_IF; 651 *valp = (value[sym] != NULL); 652 cp = skipsym(cp); 653 cp = skipcomment(cp); 654 if (*cp++ != ')') 655 return LT_IF; 656 keepthis = false; 657 } else if (!endsym(*cp)) { 658 debug("eval%d symbol", ops - eval_ops); 659 sym = findsym(cp); 660 if (sym == 0 && !symlist) 661 return LT_IF; 662 if (value[sym] == NULL) 663 *valp = 0; 664 else { 665 *valp = strtol(value[sym], &ep, 0); 666 if (*ep != '\0' || ep == value[sym]) 667 return LT_IF; 668 } 669 cp = skipsym(cp); 670 keepthis = false; 671 } else 672 return LT_IF; 673 674 *cpp = cp; 675 debug("eval%d = %d", ops - eval_ops, *valp); 676 return *valp ? LT_TRUE : LT_FALSE; 677} 678 679/* 680 * Table-driven evaluation of binary operators. 681 */ 682Linetype 683eval_table(struct ops *ops, int *valp, const char **cpp) 684{ 685 const char *cp; 686 struct op *op; 687 int val; 688 689 debug("eval%d", ops - eval_ops); 690 cp = *cpp; 691 if (ops->inner(ops+1, valp, &cp) == LT_IF) 692 return LT_IF; 693 for (;;) { 694 cp = skipcomment(cp); 695 for (op = ops->op; op->str != NULL; op++) 696 if (strncmp(cp, op->str, strlen(op->str)) == 0) 697 break; 698 if (op->str == NULL) 699 break; 700 cp += strlen(op->str); 701 debug("eval%d %s", ops - eval_ops, op->str); 702 if (ops->inner(ops+1, &val, &cp) == LT_IF) 703 return LT_IF; 704 *valp = op->fn(*valp, val); 705 } 706 707 *cpp = cp; 708 debug("eval%d = %d", ops - eval_ops, *valp); 709 return *valp ? LT_TRUE : LT_FALSE; 710} 711 712/* 713 * Evaluate the expression on a #if or #elif line. If we can work out 714 * the result we return LT_TRUE or LT_FALSE accordingly, otherwise we 715 * return just a generic LT_IF. If the expression is constant and 716 * we are not processing constant #ifs then the keepthis flag is true. 717 */ 718Linetype 719ifeval(const char **cpp) 720{ 721 int val; 722 debug("eval %s", *cpp); 723 keepthis = killconsts ? false : true; 724 return eval_table(eval_ops, &val, cpp); 725} 726 727/* 728 * Skip over comments and stop at the next character position that is 729 * not whitespace. 730 */ 731const char * 732skipcomment(const char *cp) 733{ 734 if (incomment) 735 goto inside; 736 for (;; cp++) { 737 while (*cp == ' ' || *cp == '\t') 738 cp++; 739 if (text) 740 return cp; 741 if (cp[0] != '/') 742 return cp; 743 744 if (cp[1] == '*') { 745 if (!incomment) { 746 incomment = C_COMMENT; 747 stqcline = linenum; 748 } 749 } else if (cp[1] == '/') { 750 if (!incomment) { 751 incomment = CXX_COMMENT; 752 stqcline = linenum; 753 } 754 } else 755 return cp; 756 757 cp += 2; 758inside: 759 if (incomment == C_COMMENT) { 760 for (;;) { 761 for (; *cp != '*'; cp++) 762 if (*cp == '\0') 763 return cp; 764 if (*++cp == '/') { 765 incomment = NO_COMMENT; 766 break; 767 } 768 } 769 } 770 else if (incomment == CXX_COMMENT) { 771 for (; *cp != '\n'; cp++) 772 if (*cp == '\0') 773 return cp; 774 incomment = NO_COMMENT; 775 } 776 } 777} 778 779/* 780 * Skip over a quoted string or character and stop at the next charaacter 781 * position that is not whitespace. 782 */ 783const char * 784skipquote(const char *cp, Quote_state type) 785{ 786 char qchar; 787 788 qchar = type == QUOTE_SINGLE ? '\'' : '"'; 789 790 if (inquote == type) 791 goto inside; 792 for (;; cp++) { 793 if (*cp != qchar) 794 return cp; 795 cp++; 796 inquote = type; 797 stqcline = linenum; 798inside: 799 for (;; cp++) { 800 if (*cp == qchar) 801 break; 802 if (*cp == '\0' || (*cp == '\\' && *++cp == '\0')) 803 return cp; 804 } 805 inquote = QUOTE_NONE; 806 } 807} 808 809/* 810 * Skip over an identifier. 811 */ 812const char * 813skipsym(const char *cp) 814{ 815 while (!endsym(*cp)) 816 ++cp; 817 return cp; 818} 819 820/* 821 * Look for the symbol in the symbol table. If is is found, we return 822 * the symbol table index, else we return 0. 823 */ 824int 825findsym(const char *str) 826{ 827 const char *cp; 828 const char *symp; 829 int symind; 830 831 if (symlist) { 832 for (cp = str; !endsym(*cp); cp++) 833 continue; 834 printf("%.*s\n", cp-str, str); 835 } 836 for (symind = 1; symind < nsyms; ++symind) { 837 for (cp = str, symp = symname[symind] 838 ; *cp && *symp && *cp == *symp 839 ; cp++, symp++ 840 ) 841 continue; 842 if (*symp == '\0' && endsym(*cp)) { 843 debug("findsym %s %s", symname[symind], 844 value[symind] ? value[symind] : ""); 845 return symind; 846 } 847 } 848 return 0; 849} 850 851/* 852 * Add a symbol to the symbol table. 853 */ 854void 855addsym(bool ignorethis, bool definethis, char *sym) 856{ 857 int symind; 858 char *val; 859 860 symind = findsym(sym); 861 if (symind == 0) { 862 if (nsyms >= MAXSYMS) 863 errx(2, "too many symbols"); 864 symind = nsyms++; 865 } 866 symname[symind] = sym; 867 ignore[symind] = ignorethis; 868 val = (char *)skipsym(sym); 869 if (definethis) { 870 if (*val == '=') { 871 value[symind] = val+1; 872 *val = '\0'; 873 } else if (*val == '\0') 874 value[symind] = ""; 875 else 876 usage(); 877 } else { 878 if (*val != '\0') 879 usage(); 880 value[symind] = NULL; 881 } 882} 883 884/* 885 * Read a line from the input and expand tabs if requested and (if 886 * compiled in) treats form-feed as an end-of-line. 887 */ 888int 889getline(char *line, int maxline, FILE *inp, bool expandtabs) 890{ 891 int tmp; 892 int num; 893 int chr; 894#ifdef FFSPECIAL 895 static bool havechar = false; /* have leftover char from last time */ 896 static char svchar; 897#endif /* FFSPECIAL */ 898 899 num = 0; 900#ifdef FFSPECIAL 901 if (havechar) { 902 havechar = false; 903 chr = svchar; 904 goto ent; 905 } 906#endif /* FFSPECIAL */ 907 while (num + 8 < maxline) { /* leave room for tab */ 908 chr = getc(inp); 909 if (isprint(chr)) { 910#ifdef FFSPECIAL 911 ent: 912#endif /* FFSPECIAL */ 913 *line++ = chr; 914 num++; 915 } else 916 switch (chr) { 917 case EOF: 918 return EOF; 919 920 case '\t': 921 if (expandtabs) { 922 num += tmp = 8 - (num & 7); 923 do 924 *line++ = ' '; 925 while (--tmp); 926 break; 927 } 928 default: 929 *line++ = chr; 930 num++; 931 break; 932 933 case '\n': 934 *line = '\n'; 935 num++; 936 goto end; 937 938#ifdef FFSPECIAL 939 case '\f': 940 if (++num == 1) 941 *line = '\f'; 942 else { 943 *line = '\n'; 944 havechar = true; 945 svchar = chr; 946 } 947 goto end; 948#endif /* FFSPECIAL */ 949 } 950 } 951end: 952 *++line = '\0'; 953 return num; 954} 955 956/* 957 * Write a line to the output or not, according to the current 958 * filtering state. 959 */ 960void 961flushline(bool keep) 962{ 963 if (symlist) 964 return; 965 if ((keep && reject != REJ_YES) ^ complement) 966 fputs(tline, stdout); 967 else if (lnblank) 968 putc('\n', stdout); 969 return; 970} 971 972void 973debug(const char *msg, ...) 974{ 975 va_list ap; 976 977 if (debugging) { 978 va_start(ap, msg); 979 vwarnx(msg, ap); 980 va_end(ap); 981 } 982} 983 984void 985error(int code, int depth) 986{ 987 if (incomment || inquote) 988 errx(2, "error in %s line %d: %s (#if depth %d)", 989 filename, stqcline, errs[code], depth); 990 else 991 errx(2, "error in %s line %d: %s" 992 " (#if depth %d start line %d)", 993 filename, linenum, errs[code], depth, stifline); 994} 995