[BACK]Return to unifdef.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / unifdef

Diff for /src/usr.bin/unifdef/unifdef.c between version 1.6 and 1.7

version 1.6, 2002/10/04 20:27:16 version 1.7, 2002/12/02 07:16:23
Line 1 
Line 1 
 /*      $OpenBSD$       */  /*      $OpenBSD$       */
 /*      $NetBSD: unifdef.c,v 1.6 1998/10/08 01:31:59 wsanchez Exp $     */  
   
 /*  /*
  * Copyright (c) 1985, 1993   * Copyright (c) 1985, 1993
  *      The Regents of the University of California.  All rights reserved.   *      The Regents of the University of California.  All rights reserved.
  *   *
  * This code is derived from software contributed to Berkeley by   * This code is derived from software contributed to Berkeley by
  * Dave Yost.   * Dave Yost. Support for #if and #elif was added by Tony Finch.
  *   *
  * Redistribution and use in source and binary forms, with or without   * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions   * modification, are permitted provided that the following conditions
Line 38 
Line 36 
  */   */
   
 #ifndef lint  #ifndef lint
 static char copyright[] =  static const char copyright[] =
 "@(#) Copyright (c) 1985, 1993\n\  "@(#) Copyright (c) 1985, 1993\n\
         The Regents of the University of California.  All rights reserved.\n";          The Regents of the University of California.  All rights reserved.\n";
 #endif                          /* not lint */  
   
 #ifndef lint  
 #if 0  #if 0
 static char sccsid[] = "@(#)unifdef.c   8.1 (Berkeley) 6/6/93";  static char sccsid[] = "@(#)unifdef.c   8.1 (Berkeley) 6/6/93";
 #endif  #endif
 static char rcsid[] = "$OpenBSD$";  static const char rcsid[] = "$OpenBSD$";
 #endif                          /* not lint */  #endif
   
 /*  /*
  * unifdef - remove ifdef'ed lines   * unifdef - remove ifdef'ed lines
  *   *
  *  Warning: will not work correctly if input contains null characters.   *  Warning: will not work correctly if input contains nul characters.
  *   *
  *  Wishlist:   *  Wishlist:
  *      provide an option which will append the name of the   *      provide an option which will append the name of the
Line 61 
Line 57 
  *      provide an option which will check symbols after   *      provide an option which will check symbols after
  *        #else's and #endif's to see that they match their   *        #else's and #endif's to see that they match their
  *        corresponding #ifdef or #ifndef   *        corresponding #ifdef or #ifndef
    *      generate #line directives in place of deleted code
  */   */
   
 #include <stdio.h>  
 #include <ctype.h>  #include <ctype.h>
   #include <err.h>
   #include <stdarg.h>
   #include <stdbool.h>
   #include <stdio.h>
   #include <stdlib.h>
   #include <string.h>
   #include <unistd.h>
   
 FILE   *input;  /* types of input lines: */
 #ifndef YES  typedef enum {
 #define YES 1          LT_PLAIN,               /* ordinary line */
 #define NO  0          LT_TRUE,                /* a true #if */
 #endif                          /* YES */          LT_FALSE,               /* a false #if */
 #define C_COMMENT   1          LT_ELTRUE,              /* a true #elif */
 #define CXX_COMMENT 2          LT_ELFALSE,             /* a false #elif */
 typedef int Bool;          LT_IF,                  /* an unknown #if */
           LT_ELIF,                /* an unknown #elif */
           LT_ELSE,                /* #else */
           LT_ENDIF,               /* #endif */
           LT_EOF                  /* end of file */
   } Linetype;
   
 char   *progname;  typedef enum {          /* 0 or 1: pass thru; 1 or 2: ignore comments */
 char   *filename;          REJ_NO,
 char text;                      /* -t option in effect: this is a text file */          REJ_IGNORE,
 char lnblank;                   /* -l option in effect: blank deleted lines */          REJ_YES
 char complement;                /* -c option in effect: complement the  } Reject_level;
                                  * operation */  
   
 #define MAXSYMS 100  typedef enum {
 char   *symname[MAXSYMS];       /* symbol name */          NO_COMMENT = false,
 char    true[MAXSYMS];          /* -Dsym */          C_COMMENT,
 char    ignore[MAXSYMS];        /* -iDsym or -iUsym */          CXX_COMMENT
 char    insym[MAXSYMS];         /* state: false, inactive, true */  } Comment_state;
 #define SYM_INACTIVE 0          /* symbol is currently inactive */  
 #define SYM_FALSE    1          /* symbol is currently false */  
 #define SYM_TRUE     2          /* symbol is currently true  */  
   
 char nsyms;  typedef enum {
 char incomment;                 /* inside C comment */          QUOTE_NONE = false,
           QUOTE_SINGLE,
           QUOTE_DOUBLE
   } Quote_state;
   
 #define QUOTE_NONE   0  const char *const errs[] = {
 #define QUOTE_SINGLE 1  
 #define QUOTE_DOUBLE 2  
 char inquote;                   /* inside single or double quotes */  
 int exitstat;  
   
 int error(int, int, int);  
 int findsym(char *);  
 void flushline(Bool);  
 int getlin(char *, int, FILE *, int);  
 void pfile(void);  
 void prname(void);  
 char   *skipcomment(char *);  
 char   *skipquote(char *, int);  
   
 int  
 main(argc, argv)  
         int     argc;  
         char  **argv;  
 {  
         char  **curarg;  
         char *cp;  
         char *cp1;  
         char    ignorethis;  
   
         progname = argv[0][0] ? argv[0] : "unifdef";  
   
         for (curarg = &argv[1]; --argc > 0; curarg++) {  
                 if (*(cp1 = cp = *curarg) != '-')  
                         break;  
                 if (*++cp1 == 'i') {  
                         ignorethis = YES;  
                         cp1++;  
                 } else  
                         ignorethis = NO;  
                 if ((*cp1 == 'D'  
                         || *cp1 == 'U'  
                     )  
                     && cp1[1] != '\0'  
                     ) {  
                         int symind;  
   
                         if ((symind = findsym(&cp1[1])) < 0) {  
                                 if (nsyms >= MAXSYMS) {  
                                         prname();  
                                         fprintf(stderr, "too many symbols.\n");  
                                         exit(2);  
                                 }  
                                 symind = nsyms++;  
                                 symname[symind] = &cp1[1];  
                                 insym[symind] = SYM_INACTIVE;  
                         }  
                         ignore[symind] = ignorethis;  
                         true[symind] = *cp1 == 'D' ? YES : NO;  
                 } else  
                         if (ignorethis)  
                                 goto unrec;  
                         else  
                                 if (strcmp(&cp[1], "t") == 0)  
                                         text = YES;  
                                 else  
                                         if (strcmp(&cp[1], "l") == 0)  
                                                 lnblank = YES;  
                                         else  
                                                 if (strcmp(&cp[1], "c") == 0)  
                                                         complement = YES;  
                                                 else {  
                                         unrec:  
                                                         prname();  
                                                         fprintf(stderr, "unrecognized option: %s\n", cp);  
                                                         goto usage;  
                                                 }  
         }  
         if (nsyms == 0) {  
 usage:  
                 fprintf(stderr, "\  
 Usage: %s [-l] [-t] [-c] [[-Dsym] [-Usym] [-iDsym] [-iUsym]]... [file]\n\  
     At least one arg from [-D -U -iD -iU] is required\n", progname);  
                 exit(2);  
         }  
         if (argc > 1) {  
                 prname();  
                 fprintf(stderr, "can only do one file.\n");  
         } else  
                 if (argc == 1) {  
                         filename = *curarg;  
                         if ((input = fopen(filename, "r")) != NULL) {  
                                 pfile();  
                                 (void) fclose(input);  
                         } else {  
                                 prname();  
                                 fprintf(stderr, "can't open ");  
                                 perror(*curarg);  
                         }  
                 } else {  
                         filename = "[stdin]";  
                         input = stdin;  
                         pfile();  
                 }  
   
         (void) fflush(stdout);  
         exit(exitstat);  
 }  
 /* types of input lines: */  
 typedef int Linetype;  
 #define LT_PLAIN       0        /* ordinary line */  
 #define LT_TRUE        1        /* a true  #ifdef of a symbol known to us */  
 #define LT_FALSE       2        /* a false #ifdef of a symbol known to us */  
 #define LT_OTHER       3        /* an #ifdef of a symbol not known to us */  
 #define LT_IF          4        /* an #ifdef of a symbol not known to us */  
 #define LT_ELSE        5        /* #else */  
 #define LT_ENDIF       6        /* #endif */  
 #define LT_LEOF        7        /* end of file */  
 Linetype checkline(int *);  
   
 typedef int Reject_level;  
 Reject_level reject;            /* 0 or 1: pass thru; 1 or 2: ignore comments */  
 #define REJ_NO          0  
 #define REJ_IGNORE      1  
 #define REJ_YES         2  
 int doif(int, int, Reject_level, int);  
   
 int linenum;                    /* current line number */  
 int stqcline;                   /* start of current coment or quote */  
 char   *errs[] = {  
 #define NO_ERR      0  #define NO_ERR      0
         "",          "",
 #define END_ERR     1  #define END_ERR     1
         "",          "",
 #define ELSE_ERR    2  #define ELIF_ERR    2
           "Inappropriate elif",
   #define ELSE_ERR    3
         "Inappropriate else",          "Inappropriate else",
 #define ENDIF_ERR   3  #define ENDIF_ERR   4
         "Inappropriate endif",          "Inappropriate endif",
 #define IEOF_ERR    4  #define IEOF_ERR    5
         "Premature EOF in ifdef",          "Premature EOF in ifdef",
 #define CEOF_ERR    5  #define CEOF_ERR    6
         "Premature EOF in comment",          "Premature EOF in comment",
 #define Q1EOF_ERR   6  #define Q1EOF_ERR   7
         "Premature EOF in quoted character",          "Premature EOF in quoted character",
 #define Q2EOF_ERR   7  #define Q2EOF_ERR   8
         "Premature EOF in quoted string"          "Premature EOF in quoted string"
 };  };
 /* States for inif arg to doif */  
 #define IN_NONE 0  
 #define IN_IF   1  
 #define IN_ELSE 2  
   
   /*
    * These are the operators that are supported by the expression evaluator.
    */
   static int op_lt(int a, int b) { return a < b; }
   static int op_gt(int a, int b) { return a > b; }
   static int op_le(int a, int b) { return a <= b; }
   static int op_ge(int a, int b) { return a >= b; }
   static int op_eq(int a, int b) { return a == b; }
   static int op_ne(int a, int b) { return a != b; }
   static int op_or(int a, int b) { return a || b; }
   static int op_and(int a, int b) { return a && b; }
   
   struct ops;
   
   /*
    * An evaluation function takes three arguments, as follows: (1) a pointer to
    * an element of the precedence table which lists the operators at the current
    * level of precedence; (2) a pointer to an integer which will receive the
    * value of the expression; and (3) a pointer to a char* that points to the
    * expression to be evaluated and that is updated to the end of the expression
    * when evaluation is complete. The function returns LT_FALSE if the value of
    * the expression is zero, LT_TRUE if it is non-zero, or LT_IF if the
    * expression could not be evaluated.
    */
   typedef Linetype eval_fn(struct ops *, int *, const char **);
   
   eval_fn eval_table, eval_unary;
   
   /*
    * The precedence table. Expressions involving binary operators are evaluated
    * in a table-driven way by eval_table. When it evaluates a subexpression it
    * calls the inner function with its first argument pointing to the next
    * element of the table. Innermost expressions have special non-table-driven
    * handling.
    */
   struct ops {
           eval_fn *inner;
           struct op {
                   const char *str;
                   int (*fn)(int, int);
           } op[5];
   } eval_ops[] = {
           { eval_table, { { "||", op_or } } },
           { eval_table, { { "&&", op_and } } },
           { eval_table, { { "==", op_eq },
                           { "!=", op_ne } } },
           { eval_unary, { { "<=", op_le },
                           { ">=", op_ge },
                           { "<", op_lt },
                           { ">", op_gt } } }
   };
   
   FILE           *input;
   const char     *filename;
   int             linenum;        /* current line number */
   int             stifline;       /* start of current #if */
   int             stqcline;       /* start of current coment or quote */
   bool            keepthis;       /* ignore this #if's value 'cause it's const */
   
   #define MAXLINE 1024
   #define KWSIZE 8
   /* tline has extra space so that it isn't overflowed when editing #elifs */
   char    tline[MAXLINE+KWSIZE];  /* input buffer */
   char   *keyword;                /* used for editing #elif's */
   
   bool            complement;     /* -c option in effect: do the complement */
   bool            debugging;      /* -d option in effect: debugging reports */
   bool            killconsts;     /* -k option in effect: eval constant #ifs */
   bool            lnblank;        /* -l option in effect: blank deleted lines */
   bool            symlist;        /* -s option in effect: output symbol list */
   bool            text;           /* -t option in effect: this is a text file */
   
   #define MAXSYMS 1000
   const char     *symname[MAXSYMS];       /* symbol name */
   const char     *value[MAXSYMS];         /* -Dsym=value */
   bool            ignore[MAXSYMS];        /* -iDsym or -iUsym */
   
   int             nsyms = 1;      /* symbol 0 is used for tracking #ifs */
   
   Reject_level    reject;         /* what kind of filtering we are doing */
   Comment_state   incomment;      /* inside C comment */
   Quote_state     inquote;        /* inside single or double quotes */
   
   Linetype        checkline(int *);
   void            debug(const char *, ...);
   Linetype        process(int);
   void            doif(int, Linetype, bool);
   void            elif2if(void);
   void            elif2endif(void);
   void            error(int, int);
   void            addsym(bool, bool, char *);
   int             findsym(const char *);
   void            flushline(bool);
   #if 0
   int             getline(char *, int, FILE *, bool);
   #endif
   Linetype        ifeval(const char **);
   const char     *skipcomment(const char *);
   const char     *skipquote(const char *, Quote_state);
   const char     *skipsym(const char *);
   void            usage(void);
   
   #define endsym(c) (!isalpha((unsigned char)c) && !isdigit((unsigned char)c) && c != '_')
   
   int
   main(int argc, char *argv[])
   {
           int opt;
   
           while ((opt = getopt(argc, argv, "i:D:U:I:cdklst")) != -1)
                   switch (opt) {
                   case 'i': /* treat stuff controlled by these symbols as text */
                           /*
                            * For strict backwards-compatibility the U or D
                            * should be immediately after the -i but it doesn't
                            * matter much if we relax that requirement.
                            */
                           opt = *optarg++;
                           if (opt == 'D')
                                   addsym(true, true, optarg);
                           else if (opt == 'U')
                                   addsym(true, false, optarg);
                           else
                                   usage();
                           break;
                   case 'D': /* define a symbol */
                           addsym(false, true, optarg);
                           break;
                   case 'U': /* undef a symbol */
                           addsym(false, false, optarg);
                           break;
                   case 'I':
                           /* ignore for compatibility with cpp */
                           break;
                   case 'c': /* treat -D as -U and vice versa */
                           complement = true;
                           break;
                   case 'k': /* process constant #ifs */
                           killconsts = true;
                           break;
                   case 'd':
                           debugging = true;
                           break;
                   case 'l': /* blank deleted lines instead of omitting them */
                           lnblank = true;
                           break;
                   case 's': /* only output list of symbols that control #ifs */
                           symlist = true;
                           break;
                   case 't': /* don't parse C comments or strings */
                           text = true;
                           break;
                   default:
                           usage();
                   }
           argc -= optind;
           argv += optind;
           if (nsyms == 1 && !symlist) {
                   warnx("must -D or -U at least one symbol");
                   usage();
           }
           if (argc > 1) {
                   errx(2, "can only do one file");
           } else if (argc == 1 && strcmp(*argv, "-") != 0) {
                   filename = *argv;
                   if ((input = fopen(filename, "r")) != NULL) {
                           (void) process(0);
                           (void) fclose(input);
                   } else
                           err(2, "can't open %s", *argv);
           } else {
                   filename = "[stdin]";
                   input = stdin;
                   (void) process(0);
           }
   
           exit(0);
   }
   
 void  void
 pfile()  usage(void)
 {  {
         reject = REJ_NO;          fprintf (stderr, "usage: unifdef [-cdklst] [[-Dsym[=val]]"
         (void) doif(-1, IN_NONE, reject, 0);              "[-Usym] [-iDsym[=val]] [-iUsym]] ... [file]\n");
         return;          exit (2);
 }  }
   
 int  /*
 doif(thissym, inif, prevreject, depth)   * This function processes #if lines and alters the pass-through
         int thissym;    /* index of the symbol who was last ifdef'ed */   * state accordingly. All the complicated state transition suff is
         int     inif;           /* YES or NO we are inside an ifdef */   * dealt with in this function, as well as checking that the
         Reject_level prevreject;/* previous value of reject */   * #if/#elif/#else/#endif lines happen in the correct order. Lines
         int     depth;          /* depth of ifdef's */   * between #if lines are handled by a recursive call to process().
    */
   void
   doif(int depth, Linetype lineval, bool ignoring)
 {  {
         Linetype lineval;          Reject_level savereject;
         Reject_level thisreject;          bool active;
         int     doret;          /* tmp return value of doif */          bool donetrue;
         int     cursym;         /* index of the symbol returned by checkline */          bool inelse;
         int     stline;         /* line number when called this time */          int saveline;
   
         stline = linenum;          debug("#if line %d code %d depth %d",
               linenum, lineval, depth);
           saveline = stifline;
           stifline = linenum;
           savereject = reject;
           inelse = false;
           donetrue = false;
           if (lineval == LT_IF || reject != REJ_NO) {
                   active = false;
                   ignoring = false;
                   flushline(true);
           } else if (ignoring) {
                   active = false;
                   flushline(true);
                   if (lineval == LT_FALSE)
                           reject = REJ_IGNORE;
                   else
                           donetrue = true;
           } else {
                   active = true;
                   flushline(false);
                   if (lineval == LT_FALSE)
                           reject = REJ_YES;
                   else
                           donetrue = true;
           }
           debug("active %d ignore %d", active, ignoring);
         for (;;) {          for (;;) {
                 switch (lineval = checkline(&cursym)) {                  switch (lineval = process(depth)) {
                 case LT_PLAIN:                  case LT_ELIF:
                         flushline(YES);                          debug("#elif start %d line %d code %d depth %d",
                               stifline, linenum, lineval, depth);
                           if (inelse)
                                   error(ELIF_ERR, depth);
                           donetrue = false;
                           reject = savereject;
                           if (active) {
                                   active = false;
                                   elif2if();
                                   flushline(true);
                           } else {
                                   ignoring = false;
                                   flushline(true);
                           }
                           debug("active %d ignore %d", active, ignoring);
                         break;                          break;
                   case LT_ELTRUE:
                 case LT_TRUE:                  case LT_ELFALSE:
                 case LT_FALSE:                          debug("#elif start %d line %d code %d depth %d",
                         thisreject = reject;                              stifline, linenum, lineval, depth);
                         if (lineval == LT_TRUE)                          if (inelse)
                                 insym[cursym] = SYM_TRUE;                                  error(ELIF_ERR, depth);
                           if (active)
                                   flushline(false);
                         else {                          else {
                                 if (reject != REJ_YES)                                  ignoring = false;
                                         reject = ignore[cursym] ? REJ_IGNORE : REJ_YES;                                  active = true;
                                 insym[cursym] = SYM_FALSE;                                  elif2endif();
                                   flushline(true);
                         }                          }
                         if (ignore[cursym])                          if (lineval == LT_ELFALSE)
                                 flushline(YES);                                  reject = REJ_YES;
                         else {                          else {
                                 exitstat = 1;                                  reject = REJ_NO;
                                 flushline(NO);                                  donetrue = true;
                         }                          }
                         if ((doret = doif(cursym, IN_IF, thisreject, depth + 1)) != NO_ERR)                          debug("active %d ignore %d", active, ignoring);
                                 return error(doret, stline, depth);  
                         break;                          break;
   
                 case LT_IF:  
                 case LT_OTHER:  
                         flushline(YES);  
                         if ((doret = doif(-1, IN_IF, reject, depth + 1)) != NO_ERR)  
                                 return error(doret, stline, depth);  
                         break;  
   
                 case LT_ELSE:                  case LT_ELSE:
                         if (inif != IN_IF)                          debug("#else start %d line %d code %d depth %d",
                                 return error(ELSE_ERR, linenum, depth);                              stifline, linenum, lineval, depth);
                         inif = IN_ELSE;                          if (inelse)
                         if (thissym >= 0) {                                  error(ELSE_ERR, depth);
                                 if (insym[thissym] == SYM_TRUE) {                          if (active) {
                                         reject = ignore[thissym] ? REJ_IGNORE : REJ_YES;                                  flushline(false);
                                         insym[thissym] = SYM_FALSE;                                  reject = REJ_YES;
                                 } else {        /* (insym[thissym] ==                                  if (reject == REJ_YES && !donetrue)
                                                  * SYM_FALSE) */                                          reject = REJ_NO;
                                         reject = prevreject;                          } else {
                                         insym[thissym] = SYM_TRUE;                                  flushline(true);
                                   if (ignoring) {
                                           if (reject == REJ_IGNORE)
                                                   reject = REJ_NO;
                                 }                                  }
                                 if (!ignore[thissym]) {  
                                         flushline(NO);  
                                         break;  
                                 }  
                         }                          }
                         flushline(YES);                          inelse = true;
                           debug("active %d ignore %d", active, ignoring);
                         break;                          break;
   
                 case LT_ENDIF:                  case LT_ENDIF:
                         if (inif == IN_NONE)                          debug("#endif start %d line %d code %d depth %d",
                                 return error(ENDIF_ERR, linenum, depth);                              stifline, linenum, lineval, depth);
                         if (thissym >= 0) {                          if (active)
                                 insym[thissym] = SYM_INACTIVE;                                  flushline(false);
                                 reject = prevreject;                          else
                                 if (!ignore[thissym]) {                                  flushline(true);
                                         flushline(NO);                          reject = savereject;
                                         return NO_ERR;                          stifline = saveline;
                                 }                          return;
                         }                  default:
                         flushline(YES);                          /* bug */
                         return NO_ERR;                          abort();
   
                 case LT_LEOF:{  
                                 int     err;  
                                 err = incomment  
                                     ? CEOF_ERR  
                                     : inquote == QUOTE_SINGLE  
                                     ? Q1EOF_ERR  
                                     : inquote == QUOTE_DOUBLE  
                                     ? Q2EOF_ERR  
                                     : NO_ERR;  
                                 if (inif != IN_NONE) {  
                                         if (err != NO_ERR)  
                                                 (void) error(err, stqcline, depth);  
                                         return error(IEOF_ERR, stline, depth);  
                                 } else  
                                         if (err != NO_ERR)  
                                                 return error(err, stqcline, depth);  
                                         else  
                                                 return NO_ERR;  
                         }  
                 }                  }
         }          }
 }  }
 #define endsym(c) (!isalpha (c) && !isdigit (c) && c != '_')  
   
 #define MAXLINE 256  /*
 char    tline[MAXLINE];   * The main file processing routine. This function deals with passing
    * through normal non-#if lines, correct nesting of #if sections, and
    * checking that things terminate correctly at the end of file. The
    * complicated stuff is delegated to doif().
    */
   Linetype
   process(int depth)
   {
           Linetype lineval;
           int cursym;
   
           for (;;) {
                   linenum++;
                   if (fgets(tline, MAXLINE, input) == NULL) {
                           if (incomment)
                                   error(CEOF_ERR, depth);
                           if (inquote == QUOTE_SINGLE)
                                   error(Q1EOF_ERR, depth);
                           if (inquote == QUOTE_DOUBLE)
                                   error(Q2EOF_ERR, depth);
                           if (depth != 0)
                                   error(IEOF_ERR, depth);
                           return (LT_EOF);
                   }
                   switch (lineval = checkline(&cursym)) {
                   case LT_PLAIN:
                           flushline(true);
                           break;
                   case LT_IF:
                   case LT_TRUE:
                   case LT_FALSE:
                           doif(depth + 1, lineval, ignore[cursym]);
                           break;
                   case LT_ELIF:
                   case LT_ELTRUE:
                   case LT_ELFALSE:
                   case LT_ELSE:
                   case LT_ENDIF:
                           if (depth != 0)
                                   return (lineval);
                           if (lineval == LT_ENDIF)
                                   error(ENDIF_ERR, depth);
                           if (lineval == LT_ELSE)
                                   error(ELSE_ERR, depth);
                           error(ELIF_ERR, depth);
                   default:
                           /* bug */
                           abort();
                   }
           }
   }
   
   /*
    * Parse a line and determine its type.
    */
 Linetype  Linetype
 checkline(cursym)  checkline(int *cursym)
         int    *cursym;         /* if LT_TRUE or LT_FALSE returned, set this  
                                  * to sym index */  
 {  {
         char *cp;          const char *cp;
         char *symp;          char *symp;
         char   *scp;  
         Linetype retval;          Linetype retval;
 #define KWSIZE 8          char kw[KWSIZE];
         char    keyword[KWSIZE];  
   
         linenum++;  
         if (getlin(tline, sizeof tline, input, NO) == EOF)  
                 return LT_LEOF;  
   
         retval = LT_PLAIN;          retval = LT_PLAIN;
         if (*(cp = tline) != '#'          cp = skipcomment(tline);
             || incomment          if (*cp != '#' || incomment || inquote == QUOTE_SINGLE ||
             || inquote == QUOTE_SINGLE              inquote == QUOTE_DOUBLE)
             || inquote == QUOTE_DOUBLE  
             )  
                 goto eol;                  goto eol;
   
         cp = skipcomment(++cp);          cp = skipcomment(++cp);
         symp = keyword;          keyword = (char *)cp;
           symp = kw;
         while (!endsym(*cp)) {          while (!endsym(*cp)) {
                 *symp = *cp++;                  *symp = *cp++;
                 if (++symp >= &keyword[KWSIZE])                  if (++symp >= &kw[KWSIZE])
                         goto eol;                          goto eol;
         }          }
         *symp = '\0';          *symp = '\0';
   
         if (strcmp(keyword, "ifdef") == 0) {          if (strcmp(kw, "ifdef") == 0) {
                 retval = YES;                  retval = LT_TRUE;
                 goto ifdef;                  goto ifdef;
         } else          } else if (strcmp(kw, "ifndef") == 0) {
                 if (strcmp(keyword, "ifndef") == 0) {                  retval = LT_FALSE;
                         retval = NO;  ifdef:
         ifdef:                  cp = skipcomment(++cp);
                         scp = cp = skipcomment(++cp);                  if (incomment) {
                         if (incomment) {                          retval = LT_PLAIN;
                                 retval = LT_PLAIN;                          goto eol;
                                 goto eol;                  }
                         } {                  if ((*cursym = findsym(cp)) == 0)
                                 int     symind;                          retval = LT_IF;
                   else if (value[*cursym] == NULL)
                           retval = (retval == LT_TRUE)
                               ? LT_FALSE : LT_TRUE;
           } else if (strcmp(kw, "if") == 0) {
                   retval = ifeval(&cp);
                   cp = skipcomment(cp);
                   if (*cp != '\n' || keepthis)
                           retval = LT_IF;
                   *cursym = 0;
           } else if (strcmp(kw, "elif") == 0) {
                   retval = ifeval(&cp);
                   cp = skipcomment(cp);
                   if (*cp != '\n' || keepthis)
                           retval = LT_ELIF;
                   if (retval == LT_IF)
                           retval = LT_ELIF;
                   if (retval == LT_TRUE)
                           retval = LT_ELTRUE;
                   if (retval == LT_FALSE)
                           retval = LT_ELFALSE;
                   *cursym = 0;
           } else if (strcmp(kw, "else") == 0)
                   retval = LT_ELSE;
           else if (strcmp(kw, "endif") == 0)
                   retval = LT_ENDIF;
   
                                 if ((symind = findsym(scp)) >= 0)  
                                         retval = (retval ^ true[*cursym = symind])  
                                             ? LT_FALSE : LT_TRUE;  
                                 else  
                                         retval = LT_OTHER;  
                         }  
                 } else  
                         if (strcmp(keyword, "if") == 0)  
                                 retval = LT_IF;  
                         else  
                                 if (strcmp(keyword, "else") == 0)  
                                         retval = LT_ELSE;  
                                 else  
                                         if (strcmp(keyword, "endif") == 0)  
                                                 retval = LT_ENDIF;  
   
 eol:  eol:
         if (!text && reject != REJ_IGNORE)          if (!text && reject != REJ_IGNORE) {
                 for (; *cp;) {                  for (; *cp;) {
                         if (incomment)                          if (incomment)
                                 cp = skipcomment(cp);                                  cp = skipcomment(cp);
                           else if (inquote == QUOTE_SINGLE)
                                   cp = skipquote(cp, QUOTE_SINGLE);
                           else if (inquote == QUOTE_DOUBLE)
                                   cp = skipquote(cp, QUOTE_DOUBLE);
                           else if (*cp == '/' && (cp[1] == '*' || cp[1] == '/'))
                                   cp = skipcomment(cp);
                           else if (*cp == '\'')
                                   cp = skipquote(cp, QUOTE_SINGLE);
                           else if (*cp == '"')
                                   cp = skipquote(cp, QUOTE_DOUBLE);
                         else                          else
                                 if (inquote == QUOTE_SINGLE)                                  cp++;
                                         cp = skipquote(cp, QUOTE_SINGLE);  
                                 else  
                                         if (inquote == QUOTE_DOUBLE)  
                                                 cp = skipquote(cp, QUOTE_DOUBLE);  
                                         else  
                                                 if (*cp == '/' && (cp[1] == '*' || cp[1] == '/'))  
                                                         cp = skipcomment(cp);  
                                                 else  
                                                         if (*cp == '\'')  
                                                                 cp = skipquote(cp, QUOTE_SINGLE);  
                                                         else  
                                                                 if (*cp == '"')  
                                                                         cp = skipquote(cp, QUOTE_DOUBLE);  
                                                                 else  
                                                                         cp++;  
                 }                  }
         return retval;          }
           return (retval);
 }  }
   
 /*  /*
  *  Skip over comments and stop at the next charaacter   * Turn a #elif line into a #if. This function is used when we are
  *  position that is not whitespace.   * processing a #if/#elif/#else/#endif sequence that starts off with a
    * #if that we understand (and therefore it has been deleted) which is
    * followed by a #elif that we don't understand and therefore must be
    * kept. We turn it into a #if to keep the nesting correct.
  */   */
 char   *  void
 skipcomment(cp)  elif2if(void)
         char *cp;  
 {  {
           strncpy(keyword, "if  ", 4);
   }
   
   /*
    * Turn a #elif line into a #endif. This is used in the opposite
    * situation to elif2if, i.e. a #if that we don't understand is
    * followed by a #elif that we do; rather than deleting the #elif (as
    * we would for a #if) we turn it into a #endif to keep the nesting
    * correct.
    */
   void
   elif2endif(void)
   {
           strcpy(keyword, "endif\n");
   }
   
   /*
    * Function for evaluating the innermost parts of expressions,
    * viz. !expr (expr) defined(symbol) symbol number
    * We reset the keepthis flag when we find a non-constant subexpression.
    */
   Linetype
   eval_unary(struct ops *ops, int *valp, const char **cpp)
   {
           const char *cp;
           char *ep;
           int sym;
   
           cp = skipcomment(*cpp);
           if(*cp == '!') {
                   debug("eval%d !", ops - eval_ops);
                   cp++;
                   if (eval_unary(ops, valp, &cp) == LT_IF)
                           return (LT_IF);
                   *valp = !*valp;
           } else if (*cp == '(') {
                   cp++;
                   debug("eval%d (", ops - eval_ops);
                   if (eval_table(eval_ops, valp, &cp) == LT_IF)
                           return (LT_IF);
                   cp = skipcomment(cp);
                   if (*cp++ != ')')
                           return (LT_IF);
           } else if (isdigit((unsigned char)*cp)) {
                   debug("eval%d number", ops - eval_ops);
                   *valp = strtol(cp, &ep, 0);
                   cp = skipsym(cp);
           } else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) {
                   cp = skipcomment(cp+7);
                   debug("eval%d defined", ops - eval_ops);
                   if (*cp++ != '(')
                           return (LT_IF);
                   cp = skipcomment(cp);
                   sym = findsym(cp);
                   if (sym == 0 && !symlist)
                           return (LT_IF);
                   *valp = (value[sym] != NULL);
                   cp = skipsym(cp);
                   cp = skipcomment(cp);
                   if (*cp++ != ')')
                           return (LT_IF);
                   keepthis = false;
           } else if (!endsym(*cp)) {
                   debug("eval%d symbol", ops - eval_ops);
                   sym = findsym(cp);
                   if (sym == 0 && !symlist)
                           return (LT_IF);
                   if (value[sym] == NULL)
                           *valp = 0;
                   else {
                           *valp = strtol(value[sym], &ep, 0);
                           if (*ep != '\0' || ep == value[sym])
                                   return (LT_IF);
                   }
                   cp = skipsym(cp);
                   keepthis = false;
           } else
                   return (LT_IF);
   
           *cpp = cp;
           debug("eval%d = %d", ops - eval_ops, *valp);
           return (*valp ? LT_TRUE : LT_FALSE);
   }
   
   /*
    * Table-driven evaluation of binary operators.
    */
   Linetype
   eval_table(struct ops *ops, int *valp, const char **cpp)
   {
           const char *cp;
           struct op *op;
           int val;
   
           debug("eval%d", ops - eval_ops);
           cp = *cpp;
           if (ops->inner(ops+1, valp, &cp) == LT_IF)
                   return (LT_IF);
           for (;;) {
                   cp = skipcomment(cp);
                   for (op = ops->op; op->str != NULL; op++)
                           if (strncmp(cp, op->str, strlen(op->str)) == 0)
                                   break;
                   if (op->str == NULL)
                           break;
                   cp += strlen(op->str);
                   debug("eval%d %s", ops - eval_ops, op->str);
                   if (ops->inner(ops+1, &val, &cp) == LT_IF)
                           return LT_IF;
                   *valp = op->fn(*valp, val);
           }
   
           *cpp = cp;
           debug("eval%d = %d", ops - eval_ops, *valp);
           return (*valp ? LT_TRUE : LT_FALSE);
   }
   
   /*
    * Evaluate the expression on a #if or #elif line. If we can work out
    * the result we return LT_TRUE or LT_FALSE accordingly, otherwise we
    * return just a generic LT_IF. If the expression is constant and
    * we are not processing constant #ifs then the keepthis flag is true.
    */
   Linetype
   ifeval(const char **cpp)
   {
           int val;
   
           debug("eval %s", *cpp);
           keepthis = killconsts ? false : true;
           return (eval_table(eval_ops, &val, cpp));
   }
   
   /*
    * Skip over comments and stop at the next character position that is
    * not whitespace.
    */
   const char *
   skipcomment(const char *cp)
   {
         if (incomment)          if (incomment)
                 goto inside;                  goto inside;
         for (;; cp++) {          for (;; cp++) {
                 while (*cp == ' ' || *cp == '\t')                  while (*cp == ' ' || *cp == '\t')
                         cp++;                          cp++;
                 if (text)                  if (text)
                         return cp;                          return (cp);
                 if (cp[0] != '/')                  if (cp[0] != '/')
                         return cp;                          return (cp);
   
                 if (cp[1] == '*') {                  if (cp[1] == '*') {
                         if (!incomment) {                          if (!incomment) {
Line 475 
Line 742 
                                 stqcline = linenum;                                  stqcline = linenum;
                         }                          }
                 } else                  } else
                         return cp;                          return (cp);
   
                 cp += 2;                  cp += 2;
 inside:  inside:
Line 483 
Line 750 
                         for (;;) {                          for (;;) {
                                 for (; *cp != '*'; cp++)                                  for (; *cp != '*'; cp++)
                                         if (*cp == '\0')                                          if (*cp == '\0')
                                                 return cp;                                                  return (cp);
                                 if (*++cp == '/') {                                  if (*++cp == '/') {
                                         incomment = NO;                                          incomment = NO_COMMENT;
                                         break;                                          break;
                                 }                                  }
                         }                          }
                 }                  } else if (incomment == CXX_COMMENT) {
                 else if (incomment == CXX_COMMENT) {  
                         for (; *cp != '\n'; cp++)                          for (; *cp != '\n'; cp++)
                                 if (*cp == '\0')                                  if (*cp == '\0')
                                         return cp;                                          return (cp);
                         incomment = NO;                          incomment = NO_COMMENT;
                 }                  }
         }          }
 }  }
   
 /*  /*
  *  Skip over a quoted string or character and stop at the next charaacter   * Skip over a quoted string or character and stop at the next charaacter
  *  position that is not whitespace.   * position that is not whitespace.
  */   */
 char   *  const char *
 skipquote(cp, type)  skipquote(const char *cp, Quote_state type)
         char *cp;  
         int type;  
 {  {
         char qchar;          char qchar;
   
Line 515 
Line 780 
                 goto inside;                  goto inside;
         for (;; cp++) {          for (;; cp++) {
                 if (*cp != qchar)                  if (*cp != qchar)
                         return cp;                          return (cp);
                 cp++;                  cp++;
                 inquote = type;                  inquote = type;
                 stqcline = linenum;                  stqcline = linenum;
Line 524 
Line 789 
                         if (*cp == qchar)                          if (*cp == qchar)
                                 break;                                  break;
                         if (*cp == '\0' || (*cp == '\\' && *++cp == '\0'))                          if (*cp == '\0' || (*cp == '\\' && *++cp == '\0'))
                                 return cp;                                  return (cp);
                 }                  }
                 inquote = QUOTE_NONE;                  inquote = QUOTE_NONE;
         }          }
 }  }
   
 /*  /*
  *  findsym - look for the symbol in the symbol table.   * Skip over an identifier.
  *            if found, return symbol table index,  
  *            else return -1.  
  */   */
   const char *
   skipsym(const char *cp)
   {
           while (!endsym(*cp))
                   ++cp;
           return (cp);
   }
   
   /*
    * Look for the symbol in the symbol table. If is is found, we return
    * the symbol table index, else we return 0.
    */
 int  int
 findsym(str)  findsym(const char *str)
         char   *str;  
 {  {
         char *cp;          const char *cp;
         char *symp;          const char *symp;
         int symind;          int symind;
         char chr;  
   
         for (symind = 0; symind < nsyms; ++symind) {          if (symlist) {
                 if (insym[symind] == SYM_INACTIVE) {                  for (cp = str; !endsym(*cp); cp++)
                         for (symp = symname[symind], cp = str                          continue;
                             ; *symp && *cp == *symp                  printf("%.*s\n", (int)(cp-str), str);
                             ; cp++, symp++          }
                             )          for (symind = 1; symind < nsyms; ++symind) {
                                 continue;                  for (cp = str, symp = symname[symind];
                         chr = *cp;                      *cp && *symp && *cp == *symp; cp++, symp++)
                         if (*symp == '\0' && endsym(chr))                          continue;
                                 return symind;                  if (*symp == '\0' && endsym(*cp)) {
                           debug("findsym %s %s", symname[symind],
                               value[symind] ? value[symind] : "");
                           return (symind);
                 }                  }
         }          }
         return -1;          return (0);
 }  }
   
 /*  /*
  *   getlin - expands tabs if asked for   * Add a symbol to the symbol table.
  *            and (if compiled in) treats form-feed as an end-of-line  
  */   */
   void
   addsym(bool ignorethis, bool definethis, char *sym)
   {
           int symind;
           char *val;
   
           symind = findsym(sym);
           if (symind == 0) {
                   if (nsyms >= MAXSYMS)
                           errx(2, "too many symbols");
                   symind = nsyms++;
           }
           symname[symind] = sym;
           ignore[symind] = ignorethis;
           val = (char *)skipsym(sym);
           if (definethis) {
                   if (*val == '=') {
                           value[symind] = val+1;
                           *val = '\0';
                   } else if (*val == '\0')
                           value[symind] = "";
                   else
                           usage();
           } else {
                   if (*val != '\0')
                           usage();
                   value[symind] = NULL;
           }
   }
   
   #if 0
   /*
    * Read a line from the input and expand tabs if requested and (if
    * compiled in) treats form-feed as an end-of-line.
    */
 int  int
 getlin(line, maxline, inp, expandtabs)  getline(char *line, int maxline, FILE *inp, bool expandtabs)
         char *line;  
         int     maxline;  
         FILE   *inp;  
         int     expandtabs;  
 {  {
         int     tmp;          int tmp;
         int num;          int num;
         int chr;          int chr;
 #ifdef  FFSPECIAL  #ifdef  FFSPECIAL
         static char havechar = NO;      /* have leftover char from last time */          static bool havechar = false;   /* have leftover char from last time */
         static char svchar;          static char svchar;
 #endif                          /* FFSPECIAL */  #endif  /* FFSPECIAL */
   
         num = 0;          num = 0;
 #ifdef  FFSPECIAL  #ifdef  FFSPECIAL
         if (havechar) {          if (havechar) {
                 havechar = NO;                  havechar = false;
                 chr = svchar;                  chr = svchar;
                 goto ent;                  goto ent;
         }          }
 #endif                          /* FFSPECIAL */  #endif  /* FFSPECIAL */
         while (num + 8 < maxline) {     /* leave room for tab */          while (num + 8 < maxline) {     /* leave room for tab */
                 chr = getc(inp);                  chr = getc(inp);
                 if (chr == EOF)                  if (chr == EOF)
                         return EOF;                          return (EOF);
                 if (isprint(chr)) {                  if (0 && isprint(chr)) {
 #ifdef  FFSPECIAL  #ifdef  FFSPECIAL
         ent:  ent:
 #endif                          /* FFSPECIAL */  #endif  /* FFSPECIAL */
                         *line++ = chr;                          *line++ = chr;
                         num++;                          num++;
                 } else                  } else
                         switch (chr) {                          switch (chr) {
                           case EOF:
                                   return (EOF);
   
                         case '\t':                          case '\t':
                                 if (expandtabs) {                                  if (expandtabs) {
                                         num += tmp = 8 - (num & 7);                                          num += tmp = 8 - (num & 7);
Line 604 
Line 915 
                                         while (--tmp);                                          while (--tmp);
                                         break;                                          break;
                                 }                                  }
                         default:  
                                 *line++ = chr;  
                                 num++;  
                                 break;  
   
                         case '\n':                          case '\n':
                                 *line = '\n';                                  *line = '\n';
Line 620 
Line 927 
                                         *line = '\f';                                          *line = '\f';
                                 else {                                  else {
                                         *line = '\n';                                          *line = '\n';
                                         havechar = YES;                                          havechar = true;
                                         svchar = chr;                                          svchar = chr;
                                 }                                  }
                                 goto end;                                  goto end;
 #endif                          /* FFSPECIAL */  #endif  /* FFSPECIAL */
                           default:
                                   *line++ = chr;
                                   num++;
                                   break;
                         }                          }
         }          }
 end:  end:
         *++line = '\0';          *++line = '\0';
         return num;          return (num);
 }  }
   #endif
   
   /*
    * Write a line to the output or not, according to the current
    * filtering state.
    */
 void  void
 flushline(keep)  flushline(bool keep)
         Bool    keep;  
 {  {
         if ((keep && reject != REJ_YES) ^ complement) {          if (symlist)
                 char *line = tline;                  return;
                 FILE *out = stdout;          if ((keep && reject != REJ_YES) ^ complement)
                 char chr;                  fputs(tline, stdout);
           else if (lnblank)
                 while ((chr = *line++))                  putc('\n', stdout);
                         putc(chr, out);  
         } else  
                 if (lnblank)  
                         putc('\n', stdout);  
         return;  
 }  }
   
 void  void
 prname()  debug(const char *msg, ...)
 {  {
         fprintf(stderr, "%s: ", progname);          va_list ap;
         return;  
           if (debugging) {
                   va_start(ap, msg);
                   vwarnx(msg, ap);
                   va_end(ap);
           }
 }  }
   
 int  void
 error(err, line, depth)  error(int code, int depth)
         int     err;            /* type of error & index into error string  
                                  * array */  
         int     line;           /* line number */  
         int     depth;          /* how many ifdefs we are inside */  
 {  {
         if (err == END_ERR)          if (incomment || inquote)
                 return err;                  errx(2, "error in %s line %d: %s (#if depth %d)",
                       filename, stqcline, errs[code], depth);
         prname();          else
                   errx(2, "error in %s line %d: %s"
 #ifndef TESTING                      " (#if depth %d start line %d)",
         fprintf(stderr, "Error in %s line %d: %s.\n", filename, line, errs[err]);                      filename, linenum, errs[code], depth, stifline);
 #else                           /* TESTING */  
         fprintf(stderr, "Error in %s line %d: %s. ", filename, line, errs[err]);  
         fprintf(stderr, "ifdef depth: %d\n", depth);  
 #endif                          /* TESTING */  
   
         exitstat = 2;  
         return depth > 1 ? IEOF_ERR : END_ERR;  
 }  }

Legend:
Removed from v.1.6  
changed lines
  Added in v.1.7