This source file includes following definitions.
- main
- version
- usage
- Eelif
- Eelse
- Eendif
- Eeof
- Eioccc
- print
- drop
- Strue
- Sfalse
- Selse
- Pelif
- Pelse
- Pendif
- Dfalse
- Delif
- Delse
- Dendif
- Fdrop
- Fpass
- Ftrue
- Ffalse
- Oiffy
- Oif
- Oelif
- Idrop
- Itrue
- Ifalse
- Mpass
- Mtrue
- Melif
- Melse
- ignoreoff
- ignoreon
- keywordedit
- nest
- unnest
- state
- flushline
- process
- closeout
- done
- parseline
- op_strict
- op_lt
- op_gt
- op_le
- op_ge
- op_eq
- op_ne
- op_or
- op_and
- eval_unary
- eval_table
- ifeval
- skipcomment
- skipargs
- skipsym
- findsym
- addsym
- strlcmp
- debug
- error
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 
  11 
  12 
  13 
  14 
  15 
  16 
  17 
  18 
  19 
  20 
  21 
  22 
  23 
  24 
  25 
  26 
  27 
  28 
  29 
  30 
  31 
  32 
  33 
  34 
  35 
  36 
  37 
  38 
  39 
  40 
  41 
  42 
  43 
  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) $\n"
  62     "@(#) $URL: http://dotat.at/prog/unifdef $\n"
  63 ;
  64 
  65 
  66 typedef enum {
  67         LT_TRUEI,               
  68         LT_FALSEI,              
  69         LT_IF,                  
  70         LT_TRUE,                
  71         LT_FALSE,               
  72         LT_ELIF,                
  73         LT_ELTRUE,              
  74         LT_ELFALSE,             
  75         LT_ELSE,                
  76         LT_ENDIF,               
  77         LT_DODGY,               
  78         LT_DODGY_LAST = LT_DODGY + LT_ENDIF,
  79         LT_PLAIN,               
  80         LT_EOF,                 
  81         LT_ERROR,               
  82         LT_COUNT
  83 } Linetype;
  84 
  85 static char const * const linetype_name[] = {
  86         "TRUEI", "FALSEI", "IF", "TRUE", "FALSE",
  87         "ELIF", "ELTRUE", "ELFALSE", "ELSE", "ENDIF",
  88         "DODGY TRUEI", "DODGY FALSEI",
  89         "DODGY IF", "DODGY TRUE", "DODGY FALSE",
  90         "DODGY ELIF", "DODGY ELTRUE", "DODGY ELFALSE",
  91         "DODGY ELSE", "DODGY ENDIF",
  92         "PLAIN", "EOF", "ERROR"
  93 };
  94 
  95 
  96 typedef enum {
  97         IS_OUTSIDE,
  98         IS_FALSE_PREFIX,        
  99         IS_TRUE_PREFIX,         
 100         IS_PASS_MIDDLE,         
 101         IS_FALSE_MIDDLE,        
 102         IS_TRUE_MIDDLE,         
 103         IS_PASS_ELSE,           
 104         IS_FALSE_ELSE,          
 105         IS_TRUE_ELSE,           
 106         IS_FALSE_TRAILER,       
 107         IS_COUNT
 108 } Ifstate;
 109 
 110 static char const * const ifstate_name[] = {
 111         "OUTSIDE", "FALSE_PREFIX", "TRUE_PREFIX",
 112         "PASS_MIDDLE", "FALSE_MIDDLE", "TRUE_MIDDLE",
 113         "PASS_ELSE", "FALSE_ELSE", "TRUE_ELSE",
 114         "FALSE_TRAILER"
 115 };
 116 
 117 
 118 typedef enum {
 119         NO_COMMENT = false,     
 120         C_COMMENT,              
 121         CXX_COMMENT,            
 122         STARTING_COMMENT,       
 123         FINISHING_COMMENT,      
 124         CHAR_LITERAL,           
 125         STRING_LITERAL          
 126 } Comment_state;
 127 
 128 static char const * const comment_name[] = {
 129         "NO", "C", "CXX", "STARTING", "FINISHING", "CHAR", "STRING"
 130 };
 131 
 132 
 133 typedef enum {
 134         LS_START,               
 135         LS_HASH,                
 136         LS_DIRTY                
 137 } Line_state;
 138 
 139 static char const * const linestate_name[] = {
 140         "START", "HASH", "DIRTY"
 141 };
 142 
 143 
 144 
 145 
 146 #define MAXDEPTH        64                      
 147 #define MAXLINE         4096                    
 148 #define MAXSYMS         4096                    
 149 
 150 
 151 
 152 
 153 
 154 #define EDITSLOP        10
 155 
 156 
 157 
 158 
 159 #define TEMPLATE        "unifdef.XXXXXX"
 160 
 161 
 162 
 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             constexpr;              
 207 static bool             zerosyms = true;        
 208 static bool             firstsym;               
 209 
 210 static int              exitstat;               
 211 
 212 static void             addsym(bool, bool, char *);
 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 *, const char *, size_t);
 231 static void             unnest(void);
 232 static void             usage(void);
 233 static void             version(void);
 234 
 235 #define endsym(c) (!isalnum((unsigned char)c) && c != '_')
 236 
 237 
 238 
 239 
 240 int
 241 main(int argc, char *argv[])
 242 {
 243         int opt;
 244 
 245         while ((opt = getopt(argc, argv, "i:D:U:I:o:bBcdeKklnsStV")) != -1)
 246                 switch (opt) {
 247                 case 'i': 
 248                         
 249 
 250 
 251 
 252 
 253                         opt = *optarg++;
 254                         if (opt == 'D')
 255                                 addsym(true, true, optarg);
 256                         else if (opt == 'U')
 257                                 addsym(true, false, optarg);
 258                         else
 259                                 usage();
 260                         break;
 261                 case 'D': 
 262                         addsym(false, true, optarg);
 263                         break;
 264                 case 'U': 
 265                         addsym(false, false, optarg);
 266                         break;
 267                 case 'I': 
 268                         break;
 269                 case 'b': 
 270                 case 'l': 
 271                         lnblank = true;
 272                         break;
 273                 case 'B': 
 274                         compblank = true;
 275                         break;
 276                 case 'c': 
 277                         complement = true;
 278                         break;
 279                 case 'd':
 280                         debugging = true;
 281                         break;
 282                 case 'e': 
 283                         iocccok = true;
 284                         break;
 285                 case 'K': 
 286                         strictlogic = true;
 287                         break;
 288                 case 'k': 
 289                         killconsts = true;
 290                         break;
 291                 case 'n': 
 292                         lnnum = true;
 293                         break;
 294                 case 'o': 
 295                         ofilename = optarg;
 296                         break;
 297                 case 's': 
 298                         symlist = true;
 299                         break;
 300                 case 'S': 
 301                         symlist = symdepth = true;
 302                         break;
 303                 case 't': 
 304                         text = true;
 305                         break;
 306                 case 'V': 
 307                         version();
 308                 default:
 309                         usage();
 310                 }
 311         argc -= optind;
 312         argv += optind;
 313         if (compblank && lnblank)
 314                 errx(2, "-B and -b are mutually exclusive");
 315         if (argc > 1) {
 316                 errx(2, "can only do one file");
 317         } else if (argc == 1 && strcmp(*argv, "-") != 0) {
 318                 filename = *argv;
 319                 input = fopen(filename, "rb");
 320                 if (input == NULL)
 321                         err(2, "can't open %s", filename);
 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) == 0)
 333                         overwriting = (ist.st_dev == ost.st_dev
 334                                     && ist.st_ino == ost.st_ino);
 335                 if (overwriting) {
 336                         const char *dirsep;
 337                         int ofd;
 338 
 339                         dirsep = strrchr(ofilename, '/');
 340                         if (dirsep != NULL)
 341                                 snprintf(tempname, sizeof(tempname),
 342                                     "%.*s/" TEMPLATE,
 343                                     (int)(dirsep - ofilename), ofilename);
 344                         else
 345                                 snprintf(tempname, sizeof(tempname),
 346                                     TEMPLATE);
 347                         ofd = mkstemp(tempname);
 348                         if (ofd != -1)
 349                                 output = fdopen(ofd, "wb+");
 350                         if (output == NULL)
 351                                 err(2, "can't create temporary file");
 352                         fchmod(ofd, ist.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO));
 353                 } else {
 354                         output = fopen(ofilename, "wb");
 355                         if (output == NULL)
 356                                 err(2, "can't open %s", ofilename);
 357                 }
 358         }
 359         process();
 360         abort(); 
 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 [-bBcdeKknsStV] [-Ipath]"
 381             " [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file]\n");
 382         exit(2);
 383 }
 384 
 385 
 386 
 387 
 388 
 389 
 390 
 391 
 392 
 393 
 394 
 395 
 396 
 397 
 398 
 399 
 400 
 401 
 402 
 403 
 404 
 405 
 406 
 407 
 408 
 409 
 410 
 411 
 412 
 413 
 414 
 415 typedef void state_fn(void);
 416 
 417 
 418 static void Eelif (void) { error("Inappropriate #elif"); }
 419 static void Eelse (void) { error("Inappropriate #else"); }
 420 static void Eendif(void) { error("Inappropriate #endif"); }
 421 static void Eeof  (void) { error("Premature EOF"); }
 422 static void Eioccc(void) { error("Obfuscated preprocessor control line"); }
 423 
 424 static void print (void) { flushline(true); }
 425 static void drop  (void) { flushline(false); }
 426 
 427 static void Strue (void) { drop();  ignoreoff(); state(IS_TRUE_PREFIX); }
 428 static void Sfalse(void) { drop();  ignoreoff(); state(IS_FALSE_PREFIX); }
 429 static void Selse (void) { drop();               state(IS_TRUE_ELSE); }
 430 
 431 static void Pelif (void) { print(); ignoreoff(); state(IS_PASS_MIDDLE); }
 432 static void Pelse (void) { print();              state(IS_PASS_ELSE); }
 433 static void Pendif(void) { print(); unnest(); }
 434 
 435 static void Dfalse(void) { drop();  ignoreoff(); state(IS_FALSE_TRAILER); }
 436 static void Delif (void) { drop();  ignoreoff(); state(IS_FALSE_MIDDLE); }
 437 static void Delse (void) { drop();               state(IS_FALSE_ELSE); }
 438 static void Dendif(void) { drop();  unnest(); }
 439 
 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 
 445 static void Oiffy (void) { if (!iocccok) Eioccc(); Fpass(); ignoreon(); }
 446 static void Oif   (void) { if (!iocccok) Eioccc(); Fpass(); }
 447 static void Oelif (void) { if (!iocccok) Eioccc(); Pelif(); }
 448 
 449 static void Idrop (void) { Fdrop();  ignoreon(); }
 450 static void Itrue (void) { Ftrue();  ignoreon(); }
 451 static void Ifalse(void) { Ffalse(); ignoreon(); }
 452 
 453 static void Mpass (void) { memcpy(keyword, "if  ", 4); Pelif(); }
 454 static void Mtrue (void) { keywordedit("else");  state(IS_TRUE_MIDDLE); }
 455 static void Melif (void) { keywordedit("endif"); state(IS_FALSE_TRAILER); }
 456 static void Melse (void) { keywordedit("endif"); state(IS_FALSE_ELSE); }
 457 
 458 static state_fn * const trans_table[IS_COUNT][LT_COUNT] = {
 459 
 460 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Eendif,
 461   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Eendif,
 462   print, done,  abort },
 463 
 464 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Strue, Sfalse,Selse, Dendif,
 465   Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Eioccc,Eioccc,Eioccc,Eioccc,
 466   drop,  Eeof,  abort },
 467 
 468 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Dfalse,Dfalse,Delse, Dendif,
 469   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
 470   print, Eeof,  abort },
 471 
 472 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mtrue, Delif, Pelse, Pendif,
 473   Oiffy, Oiffy, Fpass, Oif,   Oif,   Pelif, Oelif, Oelif, Pelse, Pendif,
 474   print, Eeof,  abort },
 475 
 476 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mtrue, Delif, Pelse, Pendif,
 477   Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc,
 478   drop,  Eeof,  abort },
 479 
 480 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Melif, Melif, Melse, Pendif,
 481   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eioccc,Eioccc,Eioccc,Eioccc,Pendif,
 482   print, Eeof,  abort },
 483 
 484 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Pendif,
 485   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Pendif,
 486   print, Eeof,  abort },
 487 
 488 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Dendif,
 489   Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Eioccc,
 490   drop,  Eeof,  abort },
 491 
 492 { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Dendif,
 493   Oiffy, Oiffy, Fpass, Oif,   Oif,   Eelif, Eelif, Eelif, Eelse, Eioccc,
 494   print, Eeof,  abort },
 495 
 496 { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Dendif,
 497   Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Eioccc,
 498   drop,  Eeof,  abort }
 499 
 500 
 501 
 502 };
 503 
 504 
 505 
 506 
 507 static void
 508 ignoreoff(void)
 509 {
 510         if (depth == 0)
 511                 abort(); 
 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) - keyword,
 523             "%s%s", replacement, newline);
 524         print();
 525 }
 526 static void
 527 nest(void)
 528 {
 529         if (depth > MAXDEPTH-1)
 530                 abort(); 
 531         if (depth == MAXDEPTH-1)
 532                 error("Too many levels of nesting");
 533         depth += 1;
 534         stifline[depth] = linenum;
 535 }
 536 static void
 537 unnest(void)
 538 {
 539         if (depth == 0)
 540                 abort(); 
 541         depth -= 1;
 542 }
 543 static void
 544 state(Ifstate is)
 545 {
 546         ifstate[depth] = is;
 547 }
 548 
 549 
 550 
 551 
 552 static void
 553 flushline(bool keep)
 554 {
 555         if (symlist)
 556                 return;
 557         if (keep ^ complement) {
 558                 bool blankline = tline[strspn(tline, " \t\r\n")] == '\0';
 559                 if (blankline && compblank && blankcount != blankmax) {
 560                         delcount += 1;
 561                         blankcount += 1;
 562                 } else {
 563                         if (lnnum && delcount > 0)
 564                                 printf("#line %d%s", linenum, newline);
 565                         fputs(tline, output);
 566                         delcount = 0;
 567                         blankmax = blankcount = blankline ? blankcount + 1 : 0;
 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 
 582 
 583 static void
 584 process(void)
 585 {
 586         
 587 
 588         blankmax = blankcount = 1000;
 589         for (;;) {
 590                 Linetype lineval = parseline();
 591                 trans_table[ifstate[depth]][lineval]();
 592                 debug("process line %d %s -> %s depth %d",
 593                     linenum, linetype_name[lineval],
 594                     ifstate_name[ifstate[depth]], depth);
 595         }
 596 }
 597 
 598 
 599 
 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", ofilename);
 608                 if (overwriting) {
 609                         unlink(tempname);
 610                         errx(2, "%s unchanged", filename);
 611                 } else {
 612                         exit(2);
 613                 }
 614         }
 615 }
 616 
 617 
 618 
 619 
 620 static void
 621 done(void)
 622 {
 623         if (incomment)
 624                 error("EOF in comment");
 625         closeout();
 626         if (overwriting && rename(tempname, ofilename) == -1) {
 627                 warn("couldn't rename temporary file");
 628                 unlink(tempname);
 629                 errx(2, "%s unchanged", ofilename);
 630         }
 631         exit(exitstat);
 632 }
 633 
 634 
 635 
 636 
 637 
 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) == NULL)
 650                 return (LT_EOF);
 651         if (newline == NULL) {
 652                 if (strrchr(tline, '\n') == strrchr(tline, '\r') + 1)
 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 + 1);
 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                 
 673                 if (strncmp(cp, "\\\r\n", 3) == 0 ||
 674                     strncmp(cp, "\\\n", 2) == 0)
 675                         Eioccc();
 676                 if (strlcmp("ifdef", keyword, kwlen) == 0 ||
 677                     strlcmp("ifndef", keyword, kwlen) == 0) {
 678                         cp = skipcomment(cp);
 679                         if ((cursym = findsym(cp)) < 0)
 680                                 retval = LT_IF;
 681                         else {
 682                                 retval = (keyword[2] == 'n')
 683                                     ? LT_FALSE : LT_TRUE;
 684                                 if (value[cursym] == NULL)
 685                                         retval = (retval == LT_TRUE)
 686                                             ? LT_FALSE : LT_TRUE;
 687                                 if (ignore[cursym])
 688                                         retval = (retval == LT_TRUE)
 689                                             ? LT_TRUEI : LT_FALSEI;
 690                         }
 691                         cp = skipsym(cp);
 692                 } else if (strlcmp("if", keyword, kwlen) == 0)
 693                         retval = ifeval(&cp);
 694                 else if (strlcmp("elif", keyword, kwlen) == 0)
 695                         retval = ifeval(&cp) - LT_IF + LT_ELIF;
 696                 else if (strlcmp("else", keyword, kwlen) == 0)
 697                         retval = LT_ELSE;
 698                 else if (strlcmp("endif", keyword, kwlen) == 0)
 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 || retval == LT_FALSE ||
 708                             retval == LT_TRUEI || retval == LT_FALSEI)
 709                                 retval = LT_IF;
 710                         if (retval == LT_ELTRUE || retval == LT_ELFALSE)
 711                                 retval = LT_ELIF;
 712                 }
 713                 if (retval != LT_PLAIN && (wascomment || incomment)) {
 714                         retval += LT_DODGY;
 715                         if (incomment)
 716                                 linestate = LS_DIRTY;
 717                 }
 718                 
 719 
 720 
 721                 if (linestate == LS_HASH) {
 722                         size_t len = cp - tline;
 723                         if (fgets(tline + len, MAXLINE - len, input) == NULL) {
 724                                 
 725                                 strcpy(tline + len, newline);
 726                                 cp += strlen(newline);
 727                                 linestate = LS_START;
 728                         } else {
 729                                 linestate = LS_DIRTY;
 730                         }
 731                 }
 732         }
 733         if (linestate == LS_DIRTY) {
 734                 while (*cp != '\0')
 735                         cp = skipcomment(cp + 1);
 736         }
 737         debug("parser line %d state %s comment %s line", linenum,
 738             comment_name[incomment], linestate_name[linestate]);
 739         return (retval);
 740 }
 741 
 742 
 743 
 744 
 745 
 746 static Linetype op_strict(int *p, int v, Linetype at, Linetype bt) {
 747         if(at == LT_IF || bt == LT_IF) return (LT_IF);
 748         return (*p = v, v ? LT_TRUE : LT_FALSE);
 749 }
 750 static Linetype op_lt(int *p, Linetype at, int a, Linetype bt, int b) {
 751         return op_strict(p, a < b, at, bt);
 752 }
 753 static Linetype op_gt(int *p, Linetype at, int a, Linetype bt, int b) {
 754         return op_strict(p, a > b, at, bt);
 755 }
 756 static Linetype op_le(int *p, Linetype at, int a, Linetype bt, int b) {
 757         return op_strict(p, a <= b, at, bt);
 758 }
 759 static Linetype op_ge(int *p, Linetype at, int a, Linetype bt, int b) {
 760         return op_strict(p, a >= b, at, bt);
 761 }
 762 static Linetype op_eq(int *p, Linetype at, int a, Linetype bt, int b) {
 763         return op_strict(p, a == b, at, bt);
 764 }
 765 static Linetype op_ne(int *p, Linetype at, int a, Linetype bt, int b) {
 766         return op_strict(p, a != b, at, bt);
 767 }
 768 static Linetype op_or(int *p, Linetype at, int a, Linetype bt, int b) {
 769         if (!strictlogic && (at == LT_TRUE || bt == 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, int a, Linetype bt, int b) {
 774         if (!strictlogic && (at == LT_FALSE || bt == LT_FALSE))
 775                 return (*p = 0, LT_FALSE);
 776         return op_strict(p, a && b, at, bt);
 777 }
 778 
 779 
 780 
 781 
 782 
 783 
 784 
 785 
 786 
 787 
 788 
 789 struct ops;
 790 
 791 typedef Linetype eval_fn(const struct ops *, int *, const char **);
 792 
 793 static eval_fn eval_table, eval_unary;
 794 
 795 
 796 
 797 
 798 
 799 
 800 
 801 
 802 static const struct ops {
 803         eval_fn *inner;
 804         struct op {
 805                 const char *str;
 806                 Linetype (*fn)(int *, Linetype, int, Linetype, int);
 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 
 821 
 822 
 823 
 824 static Linetype
 825 eval_unary(const struct ops *ops, int *valp, const char **cpp)
 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_ops);
 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 : LT_FALSE;
 843                 }
 844         } else if (*cp == '(') {
 845                 cp++;
 846                 debug("eval%d (", ops - eval_ops);
 847                 lt = eval_table(eval_ops, valp, &cp);
 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 - eval_ops);
 855                 *valp = strtol(cp, &ep, 0);
 856                 if (ep == cp)
 857                         return (LT_ERROR);
 858                 lt = *valp ? LT_TRUE : LT_FALSE;
 859                 cp = skipsym(cp);
 860         } else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) {
 861                 cp = skipcomment(cp+7);
 862                 debug("eval%d defined", ops - eval_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] != NULL);
 874                         lt = *valp ? LT_TRUE : LT_FALSE;
 875                 }
 876                 cp = skipsym(cp);
 877                 cp = skipcomment(cp);
 878                 if (defparen && *cp++ != ')')
 879                         return (LT_ERROR);
 880                 constexpr = false;
 881         } else if (!endsym(*cp)) {
 882                 debug("eval%d symbol", ops - eval_ops);
 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[sym], &ep, 0);
 893                         if (*ep != '\0' || ep == value[sym])
 894                                 return (LT_ERROR);
 895                         lt = *valp ? LT_TRUE : LT_FALSE;
 896                         cp = skipargs(cp);
 897                 }
 898                 constexpr = false;
 899         } else {
 900                 debug("eval%d bad expr", ops - eval_ops);
 901                 return (LT_ERROR);
 902         }
 903 
 904         *cpp = cp;
 905         debug("eval%d = %d", ops - eval_ops, *valp);
 906         return (lt);
 907 }
 908 
 909 
 910 
 911 
 912 static Linetype
 913 eval_table(const struct ops *ops, int *valp, const char **cpp)
 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 != NULL; op++)
 928                         if (strncmp(cp, op->str, strlen(op->str)) == 0)
 929                                 break;
 930                 if (op->str == NULL)
 931                         break;
 932                 cp += strlen(op->str);
 933                 debug("eval%d %s", ops - eval_ops, op->str);
 934                 rt = ops->inner(ops+1, &val, &cp);
 935                 if (rt == LT_ERROR)
 936                         return (LT_ERROR);
 937                 lt = op->fn(valp, lt, *valp, rt, val);
 938         }
 939 
 940         *cpp = cp;
 941         debug("eval%d = %d", ops - eval_ops, *valp);
 942         debug("eval%d lt = %s", ops - eval_ops, linetype_name[lt]);
 943         return (lt);
 944 }
 945 
 946 
 947 
 948 
 949 
 950 
 951 static Linetype
 952 ifeval(const char **cpp)
 953 {
 954         int ret;
 955         int val = 0;
 956 
 957         debug("eval %s", *cpp);
 958         constexpr = killconsts ? false : true;
 959         ret = eval_table(eval_ops, &val, cpp);
 960         debug("eval = %d", val);
 961         return (constexpr ? LT_IF : ret == LT_ERROR ? LT_IF : ret);
 962 }
 963 
 964 
 965 
 966 
 967 
 968 
 969 
 970 
 971 static const char *
 972 skipcomment(const char *cp)
 973 {
 974         if (text || ignoring[depth]) {
 975                 for (; isspace((unsigned char)*cp); cp++)
 976                         if (*cp == '\n')
 977                                 linestate = LS_START;
 978                 return (cp);
 979         }
 980         while (*cp != '\0')
 981                 
 982                 if (strncmp(cp, "\\\r\n", 3) == 0)
 983                         cp += 3;
 984                 else if (strncmp(cp, "\\\n", 2) == 0)
 985                         cp += 2;
 986                 else switch (incomment) {
 987                 case NO_COMMENT:
 988                         if (strncmp(cp, "/\\\r\n", 4) == 0) {
 989                                 incomment = STARTING_COMMENT;
 990                                 cp += 4;
 991                         } else if (strncmp(cp, "/\\\n", 3) == 0) {
 992                                 incomment = STARTING_COMMENT;
 993                                 cp += 3;
 994                         } else if (strncmp(cp, "/*", 2) == 0) {
 995                                 incomment = C_COMMENT;
 996                                 cp += 2;
 997                         } else if (strncmp(cp, "//", 2) == 0) {
 998                                 incomment = CXX_COMMENT;
 999                                 cp += 2;
1000                         } else if (strncmp(cp, "\'", 1) == 0) {
1001                                 incomment = CHAR_LITERAL;
1002                                 linestate = LS_DIRTY;
1003                                 cp += 1;
1004                         } else if (strncmp(cp, "\"", 1) == 0) {
1005                                 incomment = STRING_LITERAL;
1006                                 linestate = LS_DIRTY;
1007                                 cp += 1;
1008                         } else if (strncmp(cp, "\n", 1) == 0) {
1009                                 linestate = LS_START;
1010                                 cp += 1;
1011                         } else if (strchr(" \r\t", *cp) != NULL) {
1012                                 cp += 1;
1013                         } else
1014                                 return (cp);
1015                         continue;
1016                 case CXX_COMMENT:
1017                         if (strncmp(cp, "\n", 1) == 0) {
1018                                 incomment = NO_COMMENT;
1019                                 linestate = LS_START;
1020                         }
1021                         cp += 1;
1022                         continue;
1023                 case CHAR_LITERAL:
1024                 case STRING_LITERAL:
1025                         if ((incomment == CHAR_LITERAL && cp[0] == '\'') ||
1026                             (incomment == STRING_LITERAL && cp[0] == '\"')) {
1027                                 incomment = NO_COMMENT;
1028                                 cp += 1;
1029                         } else if (cp[0] == '\\') {
1030                                 if (cp[1] == '\0')
1031                                         cp += 1;
1032                                 else
1033                                         cp += 2;
1034                         } else if (strncmp(cp, "\n", 1) == 0) {
1035                                 if (incomment == CHAR_LITERAL)
1036                                         error("unterminated char literal");
1037                                 else
1038                                         error("unterminated string literal");
1039                         } else
1040                                 cp += 1;
1041                         continue;
1042                 case C_COMMENT:
1043                         if (strncmp(cp, "*\\\r\n", 4) == 0) {
1044                                 incomment = FINISHING_COMMENT;
1045                                 cp += 4;
1046                         } else if (strncmp(cp, "*\\\n", 3) == 0) {
1047                                 incomment = FINISHING_COMMENT;
1048                                 cp += 3;
1049                         } else if (strncmp(cp, "*/", 2) == 0) {
1050                                 incomment = NO_COMMENT;
1051                                 cp += 2;
1052                         } else
1053                                 cp += 1;
1054                         continue;
1055                 case STARTING_COMMENT:
1056                         if (*cp == '*') {
1057                                 incomment = C_COMMENT;
1058                                 cp += 1;
1059                         } else if (*cp == '/') {
1060                                 incomment = CXX_COMMENT;
1061                                 cp += 1;
1062                         } else {
1063                                 incomment = NO_COMMENT;
1064                                 linestate = LS_DIRTY;
1065                         }
1066                         continue;
1067                 case FINISHING_COMMENT:
1068                         if (*cp == '/') {
1069                                 incomment = NO_COMMENT;
1070                                 cp += 1;
1071                         } else
1072                                 incomment = C_COMMENT;
1073                         continue;
1074                 default:
1075                         abort(); 
1076                 }
1077         return (cp);
1078 }
1079 
1080 
1081 
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         
1102                 return (ocp);
1103 }
1104 
1105 
1106 
1107 
1108 static const char *
1109 skipsym(const char *cp)
1110 {
1111         while (!endsym(*cp))
1112                 ++cp;
1113         return (cp);
1114 }
1115 
1116 
1117 
1118 
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", zerosyms ? "" : "\n", depth);
1132                 firstsym = zerosyms = false;
1133                 printf("%s%.*s%s",
1134                     symdepth ? " " : "",
1135                     (int)(cp-str), str,
1136                     symdepth ? "" : "\n");
1137                 
1138                 return (0);
1139         }
1140         for (symind = 0; symind < nsyms; ++symind) {
1141                 if (strlcmp(symname[symind], str, cp-str) == 0) {
1142                         debug("findsym %s %s", symname[symind],
1143                             value[symind] ? value[symind] : "");
1144                         return (symind);
1145                 }
1146         }
1147         return (-1);
1148 }
1149 
1150 
1151 
1152 
1153 static void
1154 addsym(bool ignorethis, bool definethis, char *sym)
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 symbols");
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] : "undef");
1183 }
1184 
1185 
1186 
1187 
1188 
1189 static int
1190 strlcmp(const char *s, const char *t, size_t n)
1191 {
1192         while (n-- && *t != '\0')
1193                 if (*s != *t)
1194                         return ((unsigned char)*s - (unsigned char)*t);
1195                 else
1196                         ++s, ++t;
1197         return ((unsigned char)*s);
1198 }
1199 
1200 
1201 
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, linenum, msg);
1220         else
1221                 warnx("%s: %d: %s (#if line %d depth %d)",
1222                     filename, linenum, msg, stifline[depth], depth);
1223         closeout();
1224         errx(2, "output may be truncated");
1225 }