1 /* 1 2 * Copyright (c) 2002 - 2011 Tony Finch <dot@d 3 * 4 * Redistribution and use in source and binary 5 * modification, are permitted provided that t 6 * are met: 7 * 1. Redistributions of source code must reta 8 * notice, this list of conditions and the 9 * 2. Redistributions in binary form must repr 10 * notice, this list of conditions and the 11 * documentation and/or other materials pro 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDIN 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND F 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTH 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECI 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PRO 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILI 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF AD 23 * SUCH DAMAGE. 24 */ 25 26 /* 27 * unifdef - remove ifdef'ed lines 28 * 29 * This code was derived from software contrib 30 * It was rewritten to support ANSI C by Tony 31 * of unifdef carried the 4-clause BSD copyrig 32 * remains in this version (though some of the 33 * carries a more liberal licence. 34 * 35 * Wishlist: 36 * provide an option which will append th 37 * appropriate symbol after #else's and 38 * provide an option which will check sym 39 * #else's and #endif's to see that the 40 * corresponding #ifdef or #ifndef 41 * 42 * These require better buffer handling, whi 43 * it possible to handle all "dodgy" directi 44 */ 45 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 49 #include <ctype.h> 50 #include <err.h> 51 #include <errno.h> 52 #include <stdarg.h> 53 #include <stdbool.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 const char copyright[] = 60 "@(#) $Version: unifdef-2.5 $\n" 61 "@(#) $Author: Tony Finch (dot@dotat.at) $ 62 "@(#) $URL: http://dotat.at/prog/unifdef $ 63 ; 64 65 /* types of input lines: */ 66 typedef enum { 67 LT_TRUEI, /* a true #if 68 LT_FALSEI, /* a false #if 69 LT_IF, /* an unknown 70 LT_TRUE, /* a true #if 71 LT_FALSE, /* a false #if 72 LT_ELIF, /* an unknown 73 LT_ELTRUE, /* a true #eli 74 LT_ELFALSE, /* a false #el 75 LT_ELSE, /* #else */ 76 LT_ENDIF, /* #endif */ 77 LT_DODGY, /* flag: direc 78 LT_DODGY_LAST = LT_DODGY + LT_ENDIF, 79 LT_PLAIN, /* ordinary li 80 LT_EOF, /* end of file 81 LT_ERROR, /* unevaluable 82 LT_COUNT 83 } Linetype; 84 85 static char const * const linetype_name[] = { 86 "TRUEI", "FALSEI", "IF", "TRUE", "FALS 87 "ELIF", "ELTRUE", "ELFALSE", "ELSE", " 88 "DODGY TRUEI", "DODGY FALSEI", 89 "DODGY IF", "DODGY TRUE", "DODGY FALSE 90 "DODGY ELIF", "DODGY ELTRUE", "DODGY E 91 "DODGY ELSE", "DODGY ENDIF", 92 "PLAIN", "EOF", "ERROR" 93 }; 94 95 /* state of #if processing */ 96 typedef enum { 97 IS_OUTSIDE, 98 IS_FALSE_PREFIX, /* false #if f 99 IS_TRUE_PREFIX, /* first non-f 100 IS_PASS_MIDDLE, /* first non-f 101 IS_FALSE_MIDDLE, /* a false #el 102 IS_TRUE_MIDDLE, /* a true #eli 103 IS_PASS_ELSE, /* an else aft 104 IS_FALSE_ELSE, /* an else aft 105 IS_TRUE_ELSE, /* an else aft 106 IS_FALSE_TRAILER, /* #elifs afte 107 IS_COUNT 108 } Ifstate; 109 110 static char const * const ifstate_name[] = { 111 "OUTSIDE", "FALSE_PREFIX", "TRUE_PREFI 112 "PASS_MIDDLE", "FALSE_MIDDLE", "TRUE_M 113 "PASS_ELSE", "FALSE_ELSE", "TRUE_ELSE" 114 "FALSE_TRAILER" 115 }; 116 117 /* state of comment parser */ 118 typedef enum { 119 NO_COMMENT = false, /* outside a c 120 C_COMMENT, /* in a commen 121 CXX_COMMENT, /* between // 122 STARTING_COMMENT, /* just after 123 FINISHING_COMMENT, /* star-backsl 124 CHAR_LITERAL, /* inside '' * 125 STRING_LITERAL /* inside "" * 126 } Comment_state; 127 128 static char const * const comment_name[] = { 129 "NO", "C", "CXX", "STARTING", "FINISHI 130 }; 131 132 /* state of preprocessor line parser */ 133 typedef enum { 134 LS_START, /* only space 135 LS_HASH, /* only space, 136 LS_DIRTY /* this line c 137 } Line_state; 138 139 static char const * const linestate_name[] = { 140 "START", "HASH", "DIRTY" 141 }; 142 143 /* 144 * Minimum translation limits from ISO/IEC 989 145 */ 146 #define MAXDEPTH 64 147 #define MAXLINE 4096 148 #define MAXSYMS 4096 149 150 /* 151 * Sometimes when editing a keyword the replac 152 * we leave some space at the end of the tline 153 */ 154 #define EDITSLOP 10 155 156 /* 157 * For temporary filenames 158 */ 159 #define TEMPLATE "unifdef.XXXXXX" 160 161 /* 162 * Globals. 163 */ 164 165 static bool compblank; 166 static bool lnblank; 167 static bool complement; 168 static bool debugging; 169 static bool iocccok; 170 static bool strictlogic; 171 static bool killconsts; 172 static bool lnnum; 173 static bool symlist; 174 static bool symdepth; 175 static bool text; 176 177 static const char *symname[MAXSYMS]; 178 static const char *value[MAXSYMS]; 179 static bool ignore[MAXSYMS]; 180 static int nsyms; 181 182 static FILE *input; 183 static const char *filename; 184 static int linenum; 185 static FILE *output; 186 static const char *ofilename; 187 static bool overwriting; 188 static char tempname[FILENAME_MAX] 189 190 static char tline[MAXLINE+EDITSLOP 191 static char *keyword; 192 193 static const char *newline; 194 static const char newline_unix[] = "\n"; 195 static const char newline_crlf[] = "\r\n 196 197 static Comment_state incomment; 198 static Line_state linestate; 199 static Ifstate ifstate[MAXDEPTH]; 200 static bool ignoring[MAXDEPTH]; 201 static int stifline[MAXDEPTH]; 202 static int depth; 203 static int delcount; 204 static unsigned blankcount; 205 static unsigned blankmax; 206 static bool constexpression; 207 static bool zerosyms = true; 208 static bool firstsym; 209 210 static int exitstat; 211 212 static void addsym(bool, bool, cha 213 static void closeout(void); 214 static void debug(const char *, .. 215 static void done(void); 216 static void error(const char *); 217 static int findsym(const char *); 218 static void flushline(bool); 219 static Linetype parseline(void); 220 static Linetype ifeval(const char **); 221 static void ignoreoff(void); 222 static void ignoreon(void); 223 static void keywordedit(const char 224 static void nest(void); 225 static void process(void); 226 static const char *skipargs(const char *) 227 static const char *skipcomment(const char 228 static const char *skipsym(const char *); 229 static void state(Ifstate); 230 static int strlcmp(const char *, 231 static void unnest(void); 232 static void usage(void); 233 static void version(void); 234 235 #define endsym(c) (!isalnum((unsigned char)c) 236 237 /* 238 * The main program. 239 */ 240 int 241 main(int argc, char *argv[]) 242 { 243 int opt; 244 245 while ((opt = getopt(argc, argv, "i:D: 246 switch (opt) { 247 case 'i': /* treat stuff contr 248 /* 249 * For strict backward 250 * should be immediate 251 * matter much if we r 252 */ 253 opt = *optarg++; 254 if (opt == 'D') 255 addsym(true, t 256 else if (opt == 'U') 257 addsym(true, f 258 else 259 usage(); 260 break; 261 case 'D': /* define a symbol * 262 addsym(false, true, op 263 break; 264 case 'U': /* undef a symbol */ 265 addsym(false, false, o 266 break; 267 case 'I': /* no-op for compati 268 break; 269 case 'b': /* blank deleted lin 270 case 'l': /* backwards compati 271 lnblank = true; 272 break; 273 case 'B': /* compress blank li 274 compblank = true; 275 break; 276 case 'c': /* treat -D as -U an 277 complement = true; 278 break; 279 case 'd': 280 debugging = true; 281 break; 282 case 'e': /* fewer errors from 283 iocccok = true; 284 break; 285 case 'K': /* keep ambiguous #i 286 strictlogic = true; 287 break; 288 case 'k': /* process constant 289 killconsts = true; 290 break; 291 case 'n': /* add #line directi 292 lnnum = true; 293 break; 294 case 'o': /* output to a file 295 ofilename = optarg; 296 break; 297 case 's': /* only output list 298 symlist = true; 299 break; 300 case 'S': /* list symbols with 301 symlist = symdepth = t 302 break; 303 case 't': /* don't parse C com 304 text = true; 305 break; 306 case 'V': /* print version */ 307 version(); 308 default: 309 usage(); 310 } 311 argc -= optind; 312 argv += optind; 313 if (compblank && lnblank) 314 errx(2, "-B and -b are mutuall 315 if (argc > 1) { 316 errx(2, "can only do one file" 317 } else if (argc == 1 && strcmp(*argv, 318 filename = *argv; 319 input = fopen(filename, "rb"); 320 if (input == NULL) 321 err(2, "can't open %s" 322 } else { 323 filename = "[stdin]"; 324 input = stdin; 325 } 326 if (ofilename == NULL) { 327 ofilename = "[stdout]"; 328 output = stdout; 329 } else { 330 struct stat ist, ost; 331 if (stat(ofilename, &ost) == 0 332 fstat(fileno(input), &ist) 333 overwriting = (ist.st_ 334 && ist.st_ 335 if (overwriting) { 336 const char *dirsep; 337 int ofd; 338 339 dirsep = strrchr(ofile 340 if (dirsep != NULL) 341 snprintf(tempn 342 "%.*s/" TE 343 (int)(dirs 344 else 345 snprintf(tempn 346 TEMPLATE); 347 ofd = mkstemp(tempname 348 if (ofd != -1) 349 output = fdope 350 if (output == NULL) 351 err(2, "can't 352 fchmod(ofd, ist.st_mod 353 } else { 354 output = fopen(ofilena 355 if (output == NULL) 356 err(2, "can't 357 } 358 } 359 process(); 360 abort(); /* bug */ 361 } 362 363 static void 364 version(void) 365 { 366 const char *c = copyright; 367 for (;;) { 368 while (*++c != '$') 369 if (*c == '\0') 370 exit(0); 371 while (*++c != '$') 372 putc(*c, stderr); 373 putc('\n', stderr); 374 } 375 } 376 377 static void 378 usage(void) 379 { 380 fprintf(stderr, "usage: unifdef [-bBcd 381 " [-Dsym[=val]] [-Usym] [-iDsym[=v 382 exit(2); 383 } 384 385 /* 386 * A state transition function alters the glob 387 * in a particular way. The table below is ind 388 * processing state and the type of the curren 389 * 390 * Nesting is handled by keeping a stack of st 391 * functions increase or decrease the depth. T 392 * ignore state on a stack. In some complicate 393 * alter the preprocessor directive, as follow 394 * 395 * When we have processed a group that starts 396 * #if/#elif sequence (which has therefore bee 397 * #elif that we don't understand and therefor 398 * latter into a #if to keep the nesting corre 399 * overwrite the 4 byte token "elif" with "if 400 * 401 * When we find a true #elif in a group, the f 402 * always be kept and the rest of the sequence 403 * #else will be discarded. We edit the #elif 404 * following directive to #endif since this ha 405 * 406 * "Dodgy" directives are split across multipl 407 * example being a multi-line comment hanging 408 * directive. We can handle them correctly onl 409 * from printing to dropping (or vice versa) c 410 * If the directive is the first of a group we 411 * failing with an error, or passing it throug 412 * evaluating it. The latter is not the defaul 413 * users about unifdef unexpectedly leaving be 414 */ 415 typedef void state_fn(void); 416 417 /* report an error */ 418 static void Eelif (void) { error("Inappropriat 419 static void Eelse (void) { error("Inappropriat 420 static void Eendif(void) { error("Inappropriat 421 static void Eeof (void) { error("Premature EO 422 static void Eioccc(void) { error("Obfuscated p 423 /* plain line handling */ 424 static void print (void) { flushline(true); } 425 static void drop (void) { flushline(false); } 426 /* output lacks group's start line */ 427 static void Strue (void) { drop(); ignoreoff( 428 static void Sfalse(void) { drop(); ignoreoff( 429 static void Selse (void) { drop(); 430 /* print/pass this block */ 431 static void Pelif (void) { print(); ignoreoff( 432 static void Pelse (void) { print(); 433 static void Pendif(void) { print(); unnest(); 434 /* discard this block */ 435 static void Dfalse(void) { drop(); ignoreoff( 436 static void Delif (void) { drop(); ignoreoff( 437 static void Delse (void) { drop(); 438 static void Dendif(void) { drop(); unnest(); 439 /* first line of group */ 440 static void Fdrop (void) { nest(); Dfalse(); 441 static void Fpass (void) { nest(); Pelif(); } 442 static void Ftrue (void) { nest(); Strue(); } 443 static void Ffalse(void) { nest(); Sfalse(); 444 /* variable pedantry for obfuscated lines */ 445 static void Oiffy (void) { if (!iocccok) Eiocc 446 static void Oif (void) { if (!iocccok) Eiocc 447 static void Oelif (void) { if (!iocccok) Eiocc 448 /* ignore comments in this block */ 449 static void Idrop (void) { Fdrop(); ignoreon( 450 static void Itrue (void) { Ftrue(); ignoreon( 451 static void Ifalse(void) { Ffalse(); ignoreon( 452 /* modify this line */ 453 static void Mpass (void) { memcpy(keyword, "if 454 static void Mtrue (void) { keywordedit("else") 455 static void Melif (void) { keywordedit("endif" 456 static void Melse (void) { keywordedit("endif" 457 458 static state_fn * const trans_table[IS_COUNT][ 459 /* IS_OUTSIDE */ 460 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Ee 461 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Ee 462 print, done, abort }, 463 /* IS_FALSE_PREFIX */ 464 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, St 465 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Ei 466 drop, Eeof, abort }, 467 /* IS_TRUE_PREFIX */ 468 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Df 469 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Ei 470 print, Eeof, abort }, 471 /* IS_PASS_MIDDLE */ 472 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mt 473 Oiffy, Oiffy, Fpass, Oif, Oif, Pelif, Oe 474 print, Eeof, abort }, 475 /* IS_FALSE_MIDDLE */ 476 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mt 477 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Ei 478 drop, Eeof, abort }, 479 /* IS_TRUE_MIDDLE */ 480 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Me 481 Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Ei 482 print, Eeof, abort }, 483 /* IS_PASS_ELSE */ 484 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Ee 485 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Ee 486 print, Eeof, abort }, 487 /* IS_FALSE_ELSE */ 488 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Ee 489 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Ee 490 drop, Eeof, abort }, 491 /* IS_TRUE_ELSE */ 492 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Ee 493 Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Ee 494 print, Eeof, abort }, 495 /* IS_FALSE_TRAILER */ 496 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Df 497 Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Df 498 drop, Eeof, abort } 499 /*TRUEI FALSEI IF TRUE FALSE ELIF EL 500 TRUEI FALSEI IF TRUE FALSE ELIF EL 501 PLAIN EOF ERROR */ 502 }; 503 504 /* 505 * State machine utility functions 506 */ 507 static void 508 ignoreoff(void) 509 { 510 if (depth == 0) 511 abort(); /* bug */ 512 ignoring[depth] = ignoring[depth-1]; 513 } 514 static void 515 ignoreon(void) 516 { 517 ignoring[depth] = true; 518 } 519 static void 520 keywordedit(const char *replacement) 521 { 522 snprintf(keyword, tline + sizeof(tline 523 "%s%s", replacement, newline); 524 print(); 525 } 526 static void 527 nest(void) 528 { 529 if (depth > MAXDEPTH-1) 530 abort(); /* bug */ 531 if (depth == MAXDEPTH-1) 532 error("Too many levels of nest 533 depth += 1; 534 stifline[depth] = linenum; 535 } 536 static void 537 unnest(void) 538 { 539 if (depth == 0) 540 abort(); /* bug */ 541 depth -= 1; 542 } 543 static void 544 state(Ifstate is) 545 { 546 ifstate[depth] = is; 547 } 548 549 /* 550 * Write a line to the output or not, accordin 551 */ 552 static void 553 flushline(bool keep) 554 { 555 if (symlist) 556 return; 557 if (keep ^ complement) { 558 bool blankline = tline[strspn( 559 if (blankline && compblank && 560 delcount += 1; 561 blankcount += 1; 562 } else { 563 if (lnnum && delcount 564 printf("#line 565 fputs(tline, output); 566 delcount = 0; 567 blankmax = blankcount 568 } 569 } else { 570 if (lnblank) 571 fputs(newline, output) 572 exitstat = 1; 573 delcount += 1; 574 blankcount = 0; 575 } 576 if (debugging) 577 fflush(output); 578 } 579 580 /* 581 * The driver for the state machine. 582 */ 583 static void 584 process(void) 585 { 586 /* When compressing blank lines, act a 587 is preceded by a large number of bl 588 blankmax = blankcount = 1000; 589 for (;;) { 590 Linetype lineval = parseline() 591 trans_table[ifstate[depth]][li 592 debug("process line %d %s -> % 593 linenum, linetype_name[lin 594 ifstate_name[ifstate[depth 595 } 596 } 597 598 /* 599 * Flush the output and handle errors. 600 */ 601 static void 602 closeout(void) 603 { 604 if (symdepth && !zerosyms) 605 printf("\n"); 606 if (fclose(output) == EOF) { 607 warn("couldn't write to %s", o 608 if (overwriting) { 609 unlink(tempname); 610 errx(2, "%s unchanged" 611 } else { 612 exit(2); 613 } 614 } 615 } 616 617 /* 618 * Clean up and exit. 619 */ 620 static void 621 done(void) 622 { 623 if (incomment) 624 error("EOF in comment"); 625 closeout(); 626 if (overwriting && rename(tempname, of 627 warn("couldn't rename temporar 628 unlink(tempname); 629 errx(2, "%s unchanged", ofilen 630 } 631 exit(exitstat); 632 } 633 634 /* 635 * Parse a line and determine its type. We kee 636 * parser state between calls in the global va 637 * help from skipcomment(). 638 */ 639 static Linetype 640 parseline(void) 641 { 642 const char *cp; 643 int cursym; 644 int kwlen; 645 Linetype retval; 646 Comment_state wascomment; 647 648 linenum++; 649 if (fgets(tline, MAXLINE, input) == NU 650 return (LT_EOF); 651 if (newline == NULL) { 652 if (strrchr(tline, '\n') == st 653 newline = newline_crlf 654 else 655 newline = newline_unix 656 } 657 retval = LT_PLAIN; 658 wascomment = incomment; 659 cp = skipcomment(tline); 660 if (linestate == LS_START) { 661 if (*cp == '#') { 662 linestate = LS_HASH; 663 firstsym = true; 664 cp = skipcomment(cp + 665 } else if (*cp != '\0') 666 linestate = LS_DIRTY; 667 } 668 if (!incomment && linestate == LS_HASH 669 keyword = tline + (cp - tline) 670 cp = skipsym(cp); 671 kwlen = cp - keyword; 672 /* no way can we deal with a c 673 if (strncmp(cp, "\\\r\n", 3) = 674 strncmp(cp, "\\\n", 2) == 675 Eioccc(); 676 if (strlcmp("ifdef", keyword, 677 strlcmp("ifndef", keyword, 678 cp = skipcomment(cp); 679 if ((cursym = findsym( 680 retval = LT_IF 681 else { 682 retval = (keyw 683 ? LT_FALSE 684 if (value[curs 685 retval 686 ? 687 if (ignore[cur 688 retval 689 ? 690 } 691 cp = skipsym(cp); 692 } else if (strlcmp("if", keywo 693 retval = ifeval(&cp); 694 else if (strlcmp("elif", keywo 695 retval = ifeval(&cp) - 696 else if (strlcmp("else", keywo 697 retval = LT_ELSE; 698 else if (strlcmp("endif", keyw 699 retval = LT_ENDIF; 700 else { 701 linestate = LS_DIRTY; 702 retval = LT_PLAIN; 703 } 704 cp = skipcomment(cp); 705 if (*cp != '\0') { 706 linestate = LS_DIRTY; 707 if (retval == LT_TRUE 708 retval == LT_TRUEI 709 retval = LT_IF 710 if (retval == LT_ELTRU 711 retval = LT_EL 712 } 713 if (retval != LT_PLAIN && (was 714 retval += LT_DODGY; 715 if (incomment) 716 linestate = LS 717 } 718 /* skipcomment normally change 719 if the last line of the fil 720 if there is too much whites 721 if (linestate == LS_HASH) { 722 size_t len = cp - tlin 723 if (fgets(tline + len, 724 /* append the 725 strcpy(tline + 726 cp += strlen(n 727 linestate = LS 728 } else { 729 linestate = LS 730 } 731 } 732 } 733 if (linestate == LS_DIRTY) { 734 while (*cp != '\0') 735 cp = skipcomment(cp + 736 } 737 debug("parser line %d state %s comment 738 comment_name[incomment], linestate 739 return (retval); 740 } 741 742 /* 743 * These are the binary operators that are sup 744 * evaluator. 745 */ 746 static Linetype op_strict(int *p, int v, Linet 747 if(at == LT_IF || bt == LT_IF) return 748 return (*p = v, v ? LT_TRUE : LT_FALSE 749 } 750 static Linetype op_lt(int *p, Linetype at, int 751 return op_strict(p, a < b, at, bt); 752 } 753 static Linetype op_gt(int *p, Linetype at, int 754 return op_strict(p, a > b, at, bt); 755 } 756 static Linetype op_le(int *p, Linetype at, int 757 return op_strict(p, a <= b, at, bt); 758 } 759 static Linetype op_ge(int *p, Linetype at, int 760 return op_strict(p, a >= b, at, bt); 761 } 762 static Linetype op_eq(int *p, Linetype at, int 763 return op_strict(p, a == b, at, bt); 764 } 765 static Linetype op_ne(int *p, Linetype at, int 766 return op_strict(p, a != b, at, bt); 767 } 768 static Linetype op_or(int *p, Linetype at, int 769 if (!strictlogic && (at == LT_TRUE || 770 return (*p = 1, LT_TRUE); 771 return op_strict(p, a || b, at, bt); 772 } 773 static Linetype op_and(int *p, Linetype at, in 774 if (!strictlogic && (at == LT_FALSE || 775 return (*p = 0, LT_FALSE); 776 return op_strict(p, a && b, at, bt); 777 } 778 779 /* 780 * An evaluation function takes three argument 781 * an element of the precedence table which li 782 * level of precedence; (2) a pointer to an in 783 * value of the expression; and (3) a pointer 784 * expression to be evaluated and that is upda 785 * when evaluation is complete. The function r 786 * the expression is zero, LT_TRUE if it is no 787 * depends on an unknown symbol, or LT_ERROR i 788 */ 789 struct ops; 790 791 typedef Linetype eval_fn(const struct ops *, i 792 793 static eval_fn eval_table, eval_unary; 794 795 /* 796 * The precedence table. Expressions involving 797 * in a table-driven way by eval_table. When i 798 * calls the inner function with its first arg 799 * element of the table. Innermost expressions 800 * handling. 801 */ 802 static const struct ops { 803 eval_fn *inner; 804 struct op { 805 const char *str; 806 Linetype (*fn)(int *, Linetype 807 } op[5]; 808 } eval_ops[] = { 809 { eval_table, { { "||", op_or } } }, 810 { eval_table, { { "&&", op_and } } }, 811 { eval_table, { { "==", op_eq }, 812 { "!=", op_ne } } }, 813 { eval_unary, { { "<=", op_le }, 814 { ">=", op_ge }, 815 { "<", op_lt }, 816 { ">", op_gt } } } 817 }; 818 819 /* 820 * Function for evaluating the innermost parts 821 * viz. !expr (expr) number defined(symbol) sy 822 * We reset the constexpression flag in the la 823 */ 824 static Linetype 825 eval_unary(const struct ops *ops, int *valp, c 826 { 827 const char *cp; 828 char *ep; 829 int sym; 830 bool defparen; 831 Linetype lt; 832 833 cp = skipcomment(*cpp); 834 if (*cp == '!') { 835 debug("eval%d !", ops - eval_o 836 cp++; 837 lt = eval_unary(ops, valp, &cp 838 if (lt == LT_ERROR) 839 return (LT_ERROR); 840 if (lt != LT_IF) { 841 *valp = !*valp; 842 lt = *valp ? LT_TRUE : 843 } 844 } else if (*cp == '(') { 845 cp++; 846 debug("eval%d (", ops - eval_o 847 lt = eval_table(eval_ops, valp 848 if (lt == LT_ERROR) 849 return (LT_ERROR); 850 cp = skipcomment(cp); 851 if (*cp++ != ')') 852 return (LT_ERROR); 853 } else if (isdigit((unsigned char)*cp) 854 debug("eval%d number", ops - e 855 *valp = strtol(cp, &ep, 0); 856 if (ep == cp) 857 return (LT_ERROR); 858 lt = *valp ? LT_TRUE : LT_FALS 859 cp = skipsym(cp); 860 } else if (strncmp(cp, "defined", 7) = 861 cp = skipcomment(cp+7); 862 debug("eval%d defined", ops - 863 if (*cp == '(') { 864 cp = skipcomment(cp+1) 865 defparen = true; 866 } else { 867 defparen = false; 868 } 869 sym = findsym(cp); 870 if (sym < 0) { 871 lt = LT_IF; 872 } else { 873 *valp = (value[sym] != 874 lt = *valp ? LT_TRUE : 875 } 876 cp = skipsym(cp); 877 cp = skipcomment(cp); 878 if (defparen && *cp++ != ')') 879 return (LT_ERROR); 880 constexpression = false; 881 } else if (!endsym(*cp)) { 882 debug("eval%d symbol", ops - e 883 sym = findsym(cp); 884 cp = skipsym(cp); 885 if (sym < 0) { 886 lt = LT_IF; 887 cp = skipargs(cp); 888 } else if (value[sym] == NULL) 889 *valp = 0; 890 lt = LT_FALSE; 891 } else { 892 *valp = strtol(value[s 893 if (*ep != '\0' || ep 894 return (LT_ERR 895 lt = *valp ? LT_TRUE : 896 cp = skipargs(cp); 897 } 898 constexpression = false; 899 } else { 900 debug("eval%d bad expr", ops - 901 return (LT_ERROR); 902 } 903 904 *cpp = cp; 905 debug("eval%d = %d", ops - eval_ops, * 906 return (lt); 907 } 908 909 /* 910 * Table-driven evaluation of binary operators 911 */ 912 static Linetype 913 eval_table(const struct ops *ops, int *valp, c 914 { 915 const struct op *op; 916 const char *cp; 917 int val; 918 Linetype lt, rt; 919 920 debug("eval%d", ops - eval_ops); 921 cp = *cpp; 922 lt = ops->inner(ops+1, valp, &cp); 923 if (lt == LT_ERROR) 924 return (LT_ERROR); 925 for (;;) { 926 cp = skipcomment(cp); 927 for (op = ops->op; op->str != 928 if (strncmp(cp, op->st 929 break; 930 if (op->str == NULL) 931 break; 932 cp += strlen(op->str); 933 debug("eval%d %s", ops - eval_ 934 rt = ops->inner(ops+1, &val, & 935 if (rt == LT_ERROR) 936 return (LT_ERROR); 937 lt = op->fn(valp, lt, *valp, r 938 } 939 940 *cpp = cp; 941 debug("eval%d = %d", ops - eval_ops, * 942 debug("eval%d lt = %s", ops - eval_ops 943 return (lt); 944 } 945 946 /* 947 * Evaluate the expression on a #if or #elif l 948 * the result we return LT_TRUE or LT_FALSE ac 949 * return just a generic LT_IF. 950 */ 951 static Linetype 952 ifeval(const char **cpp) 953 { 954 int ret; 955 int val = 0; 956 957 debug("eval %s", *cpp); 958 constexpression = killconsts ? false : 959 ret = eval_table(eval_ops, &val, cpp); 960 debug("eval = %d", val); 961 return (constexpression ? LT_IF : ret 962 } 963 964 /* 965 * Skip over comments, strings, and character 966 * next character position that is not whitesp 967 * the comment state in the global variable in 968 * the global variable linestate when we see a 969 * XXX: doesn't cope with the buffer splitting 970 */ 971 static const char * 972 skipcomment(const char *cp) 973 { 974 if (text || ignoring[depth]) { 975 for (; isspace((unsigned char) 976 if (*cp == '\n') 977 linestate = LS 978 return (cp); 979 } 980 while (*cp != '\0') 981 /* don't reset to LS_START aft 982 if (strncmp(cp, "\\\r\n", 3) = 983 cp += 3; 984 else if (strncmp(cp, "\\\n", 2 985 cp += 2; 986 else switch (incomment) { 987 case NO_COMMENT: 988 if (strncmp(cp, "/\\\r 989 incomment = ST 990 cp += 4; 991 } else if (strncmp(cp, 992 incomment = ST 993 cp += 3; 994 } else if (strncmp(cp, 995 incomment = C_ 996 cp += 2; 997 } else if (strncmp(cp, 998 incomment = CX 999 cp += 2; 1000 } else if (strncmp(cp 1001 incomment = C 1002 linestate = L 1003 cp += 1; 1004 } else if (strncmp(cp 1005 incomment = S 1006 linestate = L 1007 cp += 1; 1008 } else if (strncmp(cp 1009 linestate = L 1010 cp += 1; 1011 } else if (strchr(" \ 1012 cp += 1; 1013 } else 1014 return (cp); 1015 continue; 1016 case CXX_COMMENT: 1017 if (strncmp(cp, "\n", 1018 incomment = N 1019 linestate = L 1020 } 1021 cp += 1; 1022 continue; 1023 case CHAR_LITERAL: 1024 case STRING_LITERAL: 1025 if ((incomment == CHA 1026 (incomment == STR 1027 incomment = N 1028 cp += 1; 1029 } else if (cp[0] == ' 1030 if (cp[1] == 1031 cp += 1032 else 1033 cp += 1034 } else if (strncmp(cp 1035 if (incomment 1036 error 1037 else 1038 error 1039 } else 1040 cp += 1; 1041 continue; 1042 case C_COMMENT: 1043 if (strncmp(cp, "*\\\ 1044 incomment = F 1045 cp += 4; 1046 } else if (strncmp(cp 1047 incomment = F 1048 cp += 3; 1049 } else if (strncmp(cp 1050 incomment = N 1051 cp += 2; 1052 } else 1053 cp += 1; 1054 continue; 1055 case STARTING_COMMENT: 1056 if (*cp == '*') { 1057 incomment = C 1058 cp += 1; 1059 } else if (*cp == '/' 1060 incomment = C 1061 cp += 1; 1062 } else { 1063 incomment = N 1064 linestate = L 1065 } 1066 continue; 1067 case FINISHING_COMMENT: 1068 if (*cp == '/') { 1069 incomment = N 1070 cp += 1; 1071 } else 1072 incomment = C 1073 continue; 1074 default: 1075 abort(); /* bug */ 1076 } 1077 return (cp); 1078 } 1079 1080 /* 1081 * Skip macro arguments. 1082 */ 1083 static const char * 1084 skipargs(const char *cp) 1085 { 1086 const char *ocp = cp; 1087 int level = 0; 1088 cp = skipcomment(cp); 1089 if (*cp != '(') 1090 return (cp); 1091 do { 1092 if (*cp == '(') 1093 level++; 1094 if (*cp == ')') 1095 level--; 1096 cp = skipcomment(cp+1); 1097 } while (level != 0 && *cp != '\0'); 1098 if (level == 0) 1099 return (cp); 1100 else 1101 /* Rewind and re-detect the syntax er 1102 return (ocp); 1103 } 1104 1105 /* 1106 * Skip over an identifier. 1107 */ 1108 static const char * 1109 skipsym(const char *cp) 1110 { 1111 while (!endsym(*cp)) 1112 ++cp; 1113 return (cp); 1114 } 1115 1116 /* 1117 * Look for the symbol in the symbol table. I 1118 * the symbol table index, else we return -1. 1119 */ 1120 static int 1121 findsym(const char *str) 1122 { 1123 const char *cp; 1124 int symind; 1125 1126 cp = skipsym(str); 1127 if (cp == str) 1128 return (-1); 1129 if (symlist) { 1130 if (symdepth && firstsym) 1131 printf("%s%3d", zeros 1132 firstsym = zerosyms = false; 1133 printf("%s%.*s%s", 1134 symdepth ? " " : "", 1135 (int)(cp-str), str, 1136 symdepth ? "" : "\n"); 1137 /* we don't care about the va 1138 return (0); 1139 } 1140 for (symind = 0; symind < nsyms; ++sy 1141 if (strlcmp(symname[symind], 1142 debug("findsym %s %s" 1143 value[symind] ? v 1144 return (symind); 1145 } 1146 } 1147 return (-1); 1148 } 1149 1150 /* 1151 * Add a symbol to the symbol table. 1152 */ 1153 static void 1154 addsym(bool ignorethis, bool definethis, char 1155 { 1156 int symind; 1157 char *val; 1158 1159 symind = findsym(sym); 1160 if (symind < 0) { 1161 if (nsyms >= MAXSYMS) 1162 errx(2, "too many sym 1163 symind = nsyms++; 1164 } 1165 symname[symind] = sym; 1166 ignore[symind] = ignorethis; 1167 val = sym + (skipsym(sym) - sym); 1168 if (definethis) { 1169 if (*val == '=') { 1170 value[symind] = val+1 1171 *val = '\0'; 1172 } else if (*val == '\0') 1173 value[symind] = "1"; 1174 else 1175 usage(); 1176 } else { 1177 if (*val != '\0') 1178 usage(); 1179 value[symind] = NULL; 1180 } 1181 debug("addsym %s=%s", symname[symind] 1182 value[symind] ? value[symind] : " 1183 } 1184 1185 /* 1186 * Compare s with n characters of t. 1187 * The same as strncmp() except that it check 1188 */ 1189 static int 1190 strlcmp(const char *s, const char *t, size_t 1191 { 1192 while (n-- && *t != '\0') 1193 if (*s != *t) 1194 return ((unsigned cha 1195 else 1196 ++s, ++t; 1197 return ((unsigned char)*s); 1198 } 1199 1200 /* 1201 * Diagnostics. 1202 */ 1203 static void 1204 debug(const char *msg, ...) 1205 { 1206 va_list ap; 1207 1208 if (debugging) { 1209 va_start(ap, msg); 1210 vwarnx(msg, ap); 1211 va_end(ap); 1212 } 1213 } 1214 1215 static void 1216 error(const char *msg) 1217 { 1218 if (depth == 0) 1219 warnx("%s: %d: %s", filename, 1220 else 1221 warnx("%s: %d: %s (#if line % 1222 filename, linenum, msg, s 1223 closeout(); 1224 errx(2, "output may be truncated"); 1225 } 1226
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.