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

Diff for /src/usr.bin/make/cond.c between version 1.38 and 1.39

version 1.38, 2007/09/17 09:28:36 version 1.39, 2007/09/18 09:44:35
Line 92 
Line 92 
  *   *
  * All Non-Terminal functions (CondE, CondF and CondT) return Err on error.  */   * All Non-Terminal functions (CondE, CondF and CondT) return Err on error.  */
 typedef enum {  typedef enum {
     False = 0, True = 1, And, Or, Not, LParen, RParen, EndOfFile, None, Err          False = 0, True = 1, And, Or, Not, LParen, RParen, EndOfFile, None, Err
 } Token;  } Token;
   
 /*-  /*-
Line 118 
Line 118 
   
   
 struct If {  struct If {
     bool        isElse;         /* true for else forms */          bool isElse;                    /* true for else forms */
     bool        doNot;          /* true for embedded negation */          bool doNot;                     /* true for embedded negation */
     bool        (*defProc)(struct Name *);          bool (*defProc)(struct Name *); /* function to apply */
                                 /* function to apply */  
 };  };
   
 static struct If ifs[] = {  static struct If ifs[] = {
     { false,    false,  CondDoDefined },        /* if, ifdef */          { false,false,  CondDoDefined },        /* if, ifdef */
     { false,    true,   CondDoDefined },        /* ifndef */          { false,true,   CondDoDefined },        /* ifndef */
     { false,    false,  CondDoMake },           /* ifmake */          { false,false,  CondDoMake },           /* ifmake */
     { false,    true,   CondDoMake },           /* ifnmake */          { false,true,   CondDoMake },           /* ifnmake */
     { true,     false,  CondDoDefined },        /* elif, elifdef */          { true, false,  CondDoDefined },        /* elif, elifdef */
     { true,     true,   CondDoDefined },        /* elifndef */          { true, true,   CondDoDefined },        /* elifndef */
     { true,     false,  CondDoMake },           /* elifmake */          { true, false,  CondDoMake },           /* elifmake */
     { true,     true,   CondDoMake },           /* elifnmake */          { true, true,   CondDoMake },           /* elifnmake */
     { true,     false,  NULL }          { true, false,  NULL }
 };  };
   
 #define COND_IF_INDEX           0  #define COND_IF_INDEX           0
Line 148 
Line 147 
 #define COND_ELIFNMAKE_INDEX    7  #define COND_ELIFNMAKE_INDEX    7
 #define COND_ELSE_INDEX         8  #define COND_ELSE_INDEX         8
   
 static bool       condInvert;           /* Invert the default function */  static bool condInvert;         /* Invert the default function */
 static bool       (*condDefProc)        /* Default function to apply */  static bool (*condDefProc)(struct Name *);
                    (struct Name *);                                  /* Default function to apply */
 static const char *condExpr;            /* The expression to parse */  static const char *condExpr;    /* The expression to parse */
 static Token      condPushBack=None;    /* Single push-back token used in  static Token condPushBack=None; /* Single push-back token used in parsing */
                                          * parsing */  
   
 #define MAXIF           30        /* greatest depth of #if'ing */  #define MAXIF 30                /* greatest depth of #if'ing */
   
 static struct {  static struct {
         bool    value;          bool    value;
         unsigned long   lineno;          unsigned long   lineno;
         const char      *filename;          const char      *filename;
 } condStack[MAXIF];                     /* Stack of conditionals */  } condStack[MAXIF];             /* Stack of conditionals */
 static int        condTop = MAXIF;      /* Top-most conditional */  
 static int        skipIfLevel=0;        /* Depth of skipped conditionals */  
 static bool       skipLine = false;     /* Whether the parse module is skipping  
                                          * lines */  
   
   static int condTop = MAXIF;     /* Top-most conditional */
   static int skipIfLevel=0;       /* Depth of skipped conditionals */
   static bool skipLine = false;   /* Whether the parse module is skipping lines */
   
 static const char *  static const char *
 find_cond(const char *p)  find_cond(const char *p)
 {  {
     for (;;p++) {          for (;;p++) {
         /* XXX: when *p == '\0', strchr() returns !NULL */                  /* XXX: when *p == '\0', strchr() returns !NULL */
         if (strchr(" \t)&|$", *p) != NULL)                  if (strchr(" \t)&|$", *p) != NULL)
             return p;                          return p;
     }          }
 }  }
   
   
Line 195 
Line 193 
 CondGetArg(const char **linePtr, struct Name *arg, const char *func,  CondGetArg(const char **linePtr, struct Name *arg, const char *func,
     bool parens) /* true if arg should be bounded by parens */      bool parens) /* true if arg should be bounded by parens */
 {  {
     const char          *cp;          const char *cp;
   
     cp = *linePtr;          cp = *linePtr;
     if (parens) {          if (parens) {
         while (*cp != '(' && *cp != '\0')                  while (*cp != '(' && *cp != '\0')
             cp++;                          cp++;
         if (*cp == '(')                  if (*cp == '(')
             cp++;                          cp++;
     }          }
   
     if (*cp == '\0') {          if (*cp == '\0') {
         /* No arguments whatsoever. Because 'make' and 'defined' aren't really                  /* No arguments whatsoever. Because 'make' and 'defined' aren't
          * "reserved words", we don't print a message. I think this is better                   * really "reserved words", we don't print a message. I think
          * than hitting the user with a warning message every time s/he uses                   * this is better than hitting the user with a warning message
          * the word 'make' or 'defined' at the beginning of a symbol...  */                   * every time s/he uses the word 'make' or 'defined' at the
         arg->s = cp;                   * beginning of a symbol...  */
         arg->e = cp;                  arg->s = cp;
         arg->tofree = false;                  arg->e = cp;
         return false;                  arg->tofree = false;
     }                  return false;
           }
   
     while (*cp == ' ' || *cp == '\t')          while (*cp == ' ' || *cp == '\t')
         cp++;                  cp++;
   
   
     cp = VarName_Get(cp, arg, NULL, true, find_cond);          cp = VarName_Get(cp, arg, NULL, true, find_cond);
   
     while (*cp == ' ' || *cp == '\t')          while (*cp == ' ' || *cp == '\t')
         cp++;                  cp++;
     if (parens && *cp != ')') {          if (parens && *cp != ')') {
         Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",                  Parse_Error(PARSE_WARNING,
                      func);                      "Missing closing parenthesis for %s()", func);
         return false;              return false;
     } else if (parens)          } else if (parens)
         /* Advance pointer past close parenthesis.  */                  /* Advance pointer past close parenthesis.  */
         cp++;                  cp++;
   
     *linePtr = cp;          *linePtr = cp;
     return true;          return true;
 }  }
   
 /*-  /*-
Line 248 
Line 247 
 static bool  static bool
 CondDoDefined(struct Name *arg)  CondDoDefined(struct Name *arg)
 {  {
     return Var_Definedi(arg->s, arg->e);          return Var_Definedi(arg->s, arg->e);
 }  }
   
 /*-  /*-
Line 263 
Line 262 
 static bool  static bool
 CondDoMake(struct Name *arg)  CondDoMake(struct Name *arg)
 {  {
     LstNode ln;          LstNode ln;
   
     for (ln = Lst_First(create); ln != NULL; ln = Lst_Adv(ln)) {          for (ln = Lst_First(create); ln != NULL; ln = Lst_Adv(ln)) {
         char *s = (char *)Lst_Datum(ln);                  char *s = (char *)Lst_Datum(ln);
         if (Str_Matchi(s, strchr(s, '\0'), arg->s, arg->e))                  if (Str_Matchi(s, strchr(s, '\0'), arg->s, arg->e))
             return true;                          return true;
     }          }
   
     return false;          return false;
 }  }
   
 /*-  /*-
Line 286 
Line 285 
 static bool  static bool
 CondDoExists(struct Name *arg)  CondDoExists(struct Name *arg)
 {  {
     bool result;          bool result;
     char    *path;          char *path;
   
     path = Dir_FindFilei(arg->s, arg->e, defaultPath);          path = Dir_FindFilei(arg->s, arg->e, defaultPath);
     if (path != NULL) {          if (path != NULL) {
         result = true;                  result = true;
         free(path);                  free(path);
     } else {          } else {
         result = false;                  result = false;
     }          }
     return result;          return result;
 }  }
   
 /*-  /*-
Line 311 
Line 310 
 static bool  static bool
 CondDoTarget(struct Name *arg)  CondDoTarget(struct Name *arg)
 {  {
     GNode   *gn;      GNode *gn;
   
     gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE);          gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE);
     if (gn != NULL && !OP_NOP(gn->type))          if (gn != NULL && !OP_NOP(gn->type))
         return true;                  return true;
     else          else
         return false;                  return false;
 }  }
   
   
Line 340 
Line 339 
 static bool  static bool
 CondCvtArg(const char *str, double *value)  CondCvtArg(const char *str, double *value)
 {  {
     if (*str == '0' && str[1] == 'x') {          if (*str == '0' && str[1] == 'x') {
         long i;                  long i;
   
         for (str += 2, i = 0; *str; str++) {                  for (str += 2, i = 0; *str; str++) {
             int x;                          int x;
             if (isdigit(*str))                          if (isdigit(*str))
                 x  = *str - '0';                                  x  = *str - '0';
             else if (isxdigit(*str))                          else if (isxdigit(*str))
                 x = 10 + *str - isupper(*str) ? 'A' : 'a';                                  x = 10 + *str - isupper(*str) ? 'A' : 'a';
             else                          else
                 return false;                                  return false;
             i = (i << 4) + x;                          i = (i << 4) + x;
                   }
                   *value = (double) i;
                   return true;
         }          }
         *value = (double) i;          else {
         return true;                  char *eptr;
     }                  *value = strtod(str, &eptr);
     else {                  return *eptr == '\0';
         char *eptr;          }
         *value = strtod(str, &eptr);  
         return *eptr == '\0';  
     }  
 }  }
   
   
 static Token  static Token
 CondHandleVarSpec(bool doEval)  CondHandleVarSpec(bool doEval)
 {  {
     char        *lhs;          char *lhs;
     size_t      varSpecLen;          size_t varSpecLen;
     bool        doFree;          bool doFree;
   
     /* Parse the variable spec and skip over it, saving its          /* Parse the variable spec and skip over it, saving its
      * value in lhs.  */           * value in lhs.  */
     lhs = Var_Parse(condExpr, NULL, doEval,&varSpecLen,&doFree);          lhs = Var_Parse(condExpr, NULL, doEval,&varSpecLen,&doFree);
     if (lhs == var_Error)          if (lhs == var_Error)
         /* Even if !doEval, we still report syntax errors, which                  /* Even if !doEval, we still report syntax errors, which
          * is what getting var_Error back with !doEval means.  */                   * is what getting var_Error back with !doEval means.  */
         return Err;                  return Err;
     condExpr += varSpecLen;          condExpr += varSpecLen;
   
     if (!isspace(*condExpr) &&          if (!isspace(*condExpr) &&
         strchr("!=><", *condExpr) == NULL) {                  strchr("!=><", *condExpr) == NULL) {
         BUFFER buf;                  BUFFER buf;
   
         Buf_Init(&buf, 0);                  Buf_Init(&buf, 0);
   
         Buf_AddString(&buf, lhs);                  Buf_AddString(&buf, lhs);
   
         if (doFree)                  if (doFree)
             free(lhs);                          free(lhs);
   
         for (;*condExpr && !isspace(*condExpr); condExpr++)                  for (;*condExpr && !isspace(*condExpr); condExpr++)
             Buf_AddChar(&buf, *condExpr);                          Buf_AddChar(&buf, *condExpr);
   
         lhs = Var_Subst(Buf_Retrieve(&buf), NULL, doEval);                  lhs = Var_Subst(Buf_Retrieve(&buf), NULL, doEval);
         Buf_Destroy(&buf);                  Buf_Destroy(&buf);
         doFree = true;                  doFree = true;
     }          }
   
     return CondHandleComparison(lhs, doFree, doEval);          return CondHandleComparison(lhs, doFree, doEval);
 }  }
   
 static Token  static Token
Line 427 
Line 426 
 static Token  static Token
 CondHandleComparison(char *lhs, bool doFree, bool doEval)  CondHandleComparison(char *lhs, bool doFree, bool doEval)
 {  {
     Token       t;          Token t;
     const char  *rhs;          const char *rhs;
     const char  *op;          const char *op;
   
     t = Err;          t = Err;
     /* Skip whitespace to get to the operator.  */          /* Skip whitespace to get to the operator.      */
     while (isspace(*condExpr))          while (isspace(*condExpr))
         condExpr++;                  condExpr++;
   
     /* Make sure the operator is a valid one. If it isn't a          /* Make sure the operator is a valid one. If it isn't a
      * known relational operator, pretend we got a           * known relational operator, pretend we got a
      * != 0 comparison.  */           * != 0 comparison.  */
     op = condExpr;          op = condExpr;
     switch (*condExpr) {          switch (*condExpr) {
         case '!':          case '!':
         case '=':          case '=':
         case '<':          case '<':
         case '>':          case '>':
             if (condExpr[1] == '=')                  if (condExpr[1] == '=')
                 condExpr += 2;                          condExpr += 2;
             else                  else
                 condExpr += 1;                          condExpr += 1;
             break;                  break;
         default:          default:
             op = "!=";                  op = "!=";
             rhs = "0";                  rhs = "0";
   
             goto do_compare;                  goto do_compare;
     }          }
     while (isspace(*condExpr))          while (isspace(*condExpr))
         condExpr++;                  condExpr++;
     if (*condExpr == '\0') {          if (*condExpr == '\0') {
         Parse_Error(PARSE_WARNING,                  Parse_Error(PARSE_WARNING,
                     "Missing right-hand-side of operator");                      "Missing right-hand-side of operator");
         goto error;                  goto error;
     }          }
     rhs = condExpr;          rhs = condExpr;
 do_compare:  do_compare:
     if (*rhs == '"') {          if (*rhs == '"') {
         /* Doing a string comparison. Only allow == and != for                  /* Doing a string comparison. Only allow == and != for
          * operators.  */                   * operators.  */
         char    *string;                  char *string;
         const char *cp;                  const char *cp;
         int         qt;                  int qt;
         BUFFER  buf;                  BUFFER buf;
   
 do_string_compare:  do_string_compare:
         if ((*op != '!' && *op != '=') || op[1] != '=') {                  if ((*op != '!' && *op != '=') || op[1] != '=') {
             Parse_Error(PARSE_WARNING,                          Parse_Error(PARSE_WARNING,
     "String comparison operator should be either == or !=");                              "String comparison operator should be either == or !=");
             goto error;                          goto error;
         }                  }
   
         Buf_Init(&buf, 0);                  Buf_Init(&buf, 0);
         qt = *rhs == '"' ? 1 : 0;                  qt = *rhs == '"' ? 1 : 0;
   
         for (cp = &rhs[qt];                  for (cp = &rhs[qt]; ((qt && *cp != '"') ||
              ((qt && *cp != '"') ||                      (!qt && strchr(" \t)", *cp) == NULL)) && *cp != '\0';) {
               (!qt && strchr(" \t)", *cp) == NULL)) &&                          if (*cp == '$') {
              *cp != '\0';) {                                  size_t len;
             if (*cp == '$') {  
                 size_t  len;  
   
                 if (Var_ParseBuffer(&buf, cp, NULL, doEval, &len)) {                                  if (Var_ParseBuffer(&buf, cp, NULL, doEval,
                     cp += len;                                      &len)) {
                     continue;                                          cp += len;
                                           continue;
                                   }
                           } else if (*cp == '\\' && cp[1] != '\0')
                                   /* Backslash escapes things -- skip over next
                                    * character, if it exists.  */
                                   cp++;
                           Buf_AddChar(&buf, *cp++);
                 }                  }
             } else if (*cp == '\\' && cp[1] != '\0')  
                 /* Backslash escapes things -- skip over next  
                  * character, if it exists.  */  
                 cp++;  
             Buf_AddChar(&buf, *cp++);  
         }  
   
         string = Buf_Retrieve(&buf);                  string = Buf_Retrieve(&buf);
   
         if (DEBUG(COND))                  if (DEBUG(COND))
             printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n",                          printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
                    lhs, string, op);                              lhs, string, op);
         /* Null-terminate rhs and perform the comparison.                  /* Null-terminate rhs and perform the comparison.
          * t is set to the result.  */                   * t is set to the result.  */
         if (*op == '=')                  if (*op == '=')
             t = strcmp(lhs, string) ? False : True;                          t = strcmp(lhs, string) ? False : True;
         else                  else
             t = strcmp(lhs, string) ? True : False;                          t = strcmp(lhs, string) ? True : False;
         free(string);                  free(string);
         if (rhs == condExpr) {                  if (rhs == condExpr) {
             if (!qt && *cp == ')')                          if (!qt && *cp == ')')
                 condExpr = cp;                                  condExpr = cp;
             else if (*cp == '\0')                          else if (*cp == '\0')
                 condExpr = cp;                                  condExpr = cp;
             else                          else
                 condExpr = cp + 1;                                  condExpr = cp + 1;
         }                  }
     } else {          } else {
         /* rhs is either a float or an integer. Convert both the                  /* rhs is either a float or an integer. Convert both the
          * lhs and the rhs to a double and compare the two.  */                   * lhs and the rhs to a double and compare the two.  */
         double          left, right;                  double left, right;
         char            *string;                  char *string;
   
         if (!CondCvtArg(lhs, &left))                  if (!CondCvtArg(lhs, &left))
             goto do_string_compare;                          goto do_string_compare;
         if (*rhs == '$') {                  if (*rhs == '$') {
             size_t      len;                          size_t len;
             bool        freeIt;                          bool freeIt;
   
             string = Var_Parse(rhs, NULL, doEval,&len,&freeIt);                          string = Var_Parse(rhs, NULL, doEval,&len,&freeIt);
             if (string == var_Error)                          if (string == var_Error)
                 right = 0.0;                                  right = 0.0;
             else {                          else {
                 if (!CondCvtArg(string, &right)) {                                  if (!CondCvtArg(string, &right)) {
                     if (freeIt)                                          if (freeIt)
                         free(string);                                                  free(string);
                     goto do_string_compare;                                          goto do_string_compare;
                                   }
                                   if (freeIt)
                                           free(string);
                                   if (rhs == condExpr)
                                           condExpr += len;
                           }
                   } else {
                           if (!CondCvtArg(rhs, &right))
                                   goto do_string_compare;
                           if (rhs == condExpr) {
                                   /* Skip over the right-hand side.  */
                                   while (!isspace(*condExpr) && *condExpr != '\0')
                                           condExpr++;
                           }
                 }                  }
                 if (freeIt)  
                     free(string);  
                 if (rhs == condExpr)  
                     condExpr += len;  
             }  
         } else {  
             if (!CondCvtArg(rhs, &right))  
                 goto do_string_compare;  
             if (rhs == condExpr) {  
                 /* Skip over the right-hand side.  */  
                 while (!isspace(*condExpr) &&  
                       *condExpr != '\0')  
                     condExpr++;  
   
             }                  if (DEBUG(COND))
                           printf("left = %f, right = %f, op = %.2s\n", left,
                               right, op);
                   switch (op[0]) {
                   case '!':
                           if (op[1] != '=') {
                                   Parse_Error(PARSE_WARNING, "Unknown operator");
                                   goto error;
                           }
                           t = left != right ? True : False;
                           break;
                   case '=':
                           if (op[1] != '=') {
                                   Parse_Error(PARSE_WARNING, "Unknown operator");
                                   goto error;
                           }
                           t = left == right ? True : False;
                           break;
                   case '<':
                           if (op[1] == '=')
                                   t = left <= right ? True : False;
                           else
                                   t = left < right ? True : False;
                           break;
                   case '>':
                           if (op[1] == '=')
                                   t = left >= right ? True : False;
                           else
                                   t = left > right ? True : False;
                           break;
                   }
         }          }
   
         if (DEBUG(COND))  
             printf("left = %f, right = %f, op = %.2s\n", left,  
                    right, op);  
         switch (op[0]) {  
         case '!':  
             if (op[1] != '=') {  
                 Parse_Error(PARSE_WARNING,  
                             "Unknown operator");  
                 goto error;  
             }  
             t = left != right ? True : False;  
             break;  
         case '=':  
             if (op[1] != '=') {  
                 Parse_Error(PARSE_WARNING,  
                             "Unknown operator");  
                 goto error;  
             }  
             t = left == right ? True : False;  
             break;  
         case '<':  
             if (op[1] == '=')  
                 t = left <= right ? True : False;  
             else  
                 t = left < right ? True : False;  
             break;  
         case '>':  
             if (op[1] == '=')  
                 t = left >= right ? True : False;  
             else  
                 t = left > right ? True : False;  
             break;  
         }  
     }  
 error:  error:
     if (doFree)          if (doFree)
         free(lhs);                  free(lhs);
     return t;          return t;
 }  }
   
 #define S(s)    s, sizeof(s)-1  #define S(s)    s, sizeof(s)-1
 static struct operator {  static struct operator {
     const char *s;          const char *s;
     size_t len;          size_t len;
     bool (*proc)(struct Name *);          bool (*proc)(struct Name *);
 } ops[] = {  } ops[] = {
     {S("defined"), CondDoDefined},          {S("defined"), CondDoDefined},
     {S("make"), CondDoMake},          {S("make"), CondDoMake},
     {S("exists"), CondDoExists},          {S("exists"), CondDoExists},
     {S("target"), CondDoTarget},          {S("target"), CondDoTarget},
     {NULL, 0, NULL}          {NULL, 0, NULL}
 };  };
   
 static Token  static Token
 CondHandleDefault(bool doEval)  CondHandleDefault(bool doEval)
 {  {
     bool        t;          bool t;
     bool        (*evalProc)(struct Name *);          bool (*evalProc)(struct Name *);
     bool        invert = false;          bool invert = false;
     struct Name arg;          struct Name arg;
     size_t arglen;          size_t arglen;
   
     evalProc = NULL;          evalProc = NULL;
     if (strncmp(condExpr, "empty", 5) == 0) {          if (strncmp(condExpr, "empty", 5) == 0) {
         /* Use Var_Parse to parse the spec in parens and return                  /* Use Var_Parse to parse the spec in parens and return
          * True if the resulting string is empty.  */                   * True if the resulting string is empty.  */
         size_t   length;                  size_t length;
         bool doFree;                  bool doFree;
         char    *val;                  char *val;
   
         condExpr += 5;                  condExpr += 5;
   
         for (arglen = 0; condExpr[arglen] != '(' && condExpr[arglen] != '\0';)                  for (arglen = 0; condExpr[arglen] != '(' &&
              arglen++;                      condExpr[arglen] != '\0';)
                           arglen++;
   
         if (condExpr[arglen] != '\0') {                  if (condExpr[arglen] != '\0') {
             val = Var_Parse(&condExpr[arglen - 1], NULL,                          val = Var_Parse(&condExpr[arglen - 1], NULL,
                             doEval, &length, &doFree);                              doEval, &length, &doFree);
             if (val == var_Error)                          if (val == var_Error)
                 t = Err;                                  t = Err;
             else {                          else {
                 /* A variable is empty when it just contains                                  /* A variable is empty when it just contains
                  * spaces... 4/15/92, christos */                                   * spaces... 4/15/92, christos */
                 char *p;                                  char *p;
                 for (p = val; isspace(*p); p++)                                  for (p = val; isspace(*p); p++)
                     continue;                                          continue;
                 t = *p == '\0' ? True : False;                                  t = *p == '\0' ? True : False;
             }                          }
             if (doFree)                          if (doFree)
                 free(val);                                  free(val);
             /* Advance condExpr to beyond the closing ). Note that                          /* Advance condExpr to beyond the closing ). Note that
              * we subtract one from arglen + length b/c length                           * we subtract one from arglen + length b/c length
              * is calculated from condExpr[arglen - 1].  */                           * is calculated from condExpr[arglen - 1].  */
             condExpr += arglen + length - 1;                          condExpr += arglen + length - 1;
             return t;                          return t;
         } else                  } else
             condExpr -= 5;                          condExpr -= 5;
     } else {          } else {
         struct operator *op;                  struct operator *op;
   
         for (op = ops; op != NULL; op++)                  for (op = ops; op != NULL; op++)
             if (strncmp(condExpr, op->s, op->len) == 0) {                          if (strncmp(condExpr, op->s, op->len) == 0) {
                 condExpr += op->len;                                  condExpr += op->len;
                 if (CondGetArg(&condExpr, &arg, op->s, true))                                  if (CondGetArg(&condExpr, &arg, op->s, true))
                     evalProc = op->proc;                                          evalProc = op->proc;
                 else                                  else
                     condExpr -= op->len;                                          condExpr -= op->len;
                 break;                                  break;
             }                          }
     }          }
     if (evalProc == NULL) {          if (evalProc == NULL) {
         /* The symbol is itself the argument to the default                  /* The symbol is itself the argument to the default
          * function. We advance condExpr to the end of the symbol                   * function. We advance condExpr to the end of the symbol
          * by hand (the next whitespace, closing paren or                   * by hand (the next whitespace, closing paren or
          * binary operator) and set to invert the evaluation                   * binary operator) and set to invert the evaluation
          * function if condInvert is true.  */                   * function if condInvert is true.  */
         invert = condInvert;                  invert = condInvert;
         evalProc = condDefProc;                  evalProc = condDefProc;
         /* XXX should we ignore problems now ? */                  /* XXX should we ignore problems now ? */
         CondGetArg(&condExpr, &arg, "", false);                  CondGetArg(&condExpr, &arg, "", false);
     }          }
   
     /* Evaluate the argument using the set function. If invert          /* Evaluate the argument using the set function. If invert
      * is true, we invert the sense of the function.  */           * is true, we invert the sense of the function.  */
     t = (!doEval || (*evalProc)(&arg) ?          t = (!doEval || (*evalProc)(&arg) ?
          (invert ? False : True) :               (invert ? False : True) :
          (invert ? True : False));               (invert ? True : False));
     VarName_Free(&arg);          VarName_Free(&arg);
     return t;          return t;
 }  }
   
 /*-  /*-
Line 705 
Line 701 
 CondToken(bool doEval)  CondToken(bool doEval)
 {  {
   
     if (condPushBack != None) {          if (condPushBack != None) {
         Token     t;                  Token t;
   
         t = condPushBack;                  t = condPushBack;
         condPushBack = None;                  condPushBack = None;
         return t;                  return t;
     }          }
   
     while (*condExpr == ' ' || *condExpr == '\t')          while (*condExpr == ' ' || *condExpr == '\t')
         condExpr++;                  condExpr++;
     switch (*condExpr) {          switch (*condExpr) {
         case '(':          case '(':
             condExpr++;                  condExpr++;
             return LParen;                  return LParen;
         case ')':          case ')':
             condExpr++;                  condExpr++;
             return RParen;                  return RParen;
         case '|':          case '|':
             if (condExpr[1] == '|')                  if (condExpr[1] == '|')
                           condExpr++;
                 condExpr++;                  condExpr++;
             condExpr++;                  return Or;
             return Or;  
         case '&':          case '&':
             if (condExpr[1] == '&')                  if (condExpr[1] == '&')
                           condExpr++;
                 condExpr++;                  condExpr++;
             condExpr++;                  return And;
             return And;  
         case '!':          case '!':
             condExpr++;                  condExpr++;
             return Not;                  return Not;
         case '\n':          case '\n':
         case '\0':          case '\0':
             return EndOfFile;                  return EndOfFile;
         case '"':          case '"':
             return CondHandleString(doEval);                  return CondHandleString(doEval);
         case '$':          case '$':
             return CondHandleVarSpec(doEval);                  return CondHandleVarSpec(doEval);
         default:          default:
             return CondHandleDefault(doEval);                  return CondHandleDefault(doEval);
     }          }
 }  }
   
 /*-  /*-
Line 766 
Line 762 
 static Token  static Token
 CondT(bool doEval)  CondT(bool doEval)
 {  {
     Token   t;          Token t;
   
     t = CondToken(doEval);          t = CondToken(doEval);
   
     if (t == EndOfFile)          if (t == EndOfFile)
         /* If we reached the end of the expression, the expression                  /* If we reached the end of the expression, the expression
          * is malformed...  */                   * is malformed...  */
         t = Err;  
     else if (t == LParen) {  
         /* T -> ( E ).  */  
         t = CondE(doEval);  
         if (t != Err)  
             if (CondToken(doEval) != RParen)  
                 t = Err;                  t = Err;
     } else if (t == Not) {          else if (t == LParen) {
         t = CondT(doEval);                  /* T -> ( E ).  */
         if (t == True)                  t = CondE(doEval);
             t = False;                  if (t != Err)
         else if (t == False)                          if (CondToken(doEval) != RParen)
             t = True;                                  t = Err;
     }          } else if (t == Not) {
     return t;                  t = CondT(doEval);
                   if (t == True)
                           t = False;
                   else if (t == False)
                           t = True;
           }
           return t;
 }  }
   
 /*-  /*-
Line 806 
Line 802 
 static Token  static Token
 CondF(bool doEval)  CondF(bool doEval)
 {  {
     Token   l, o;          Token l, o;
   
     l = CondT(doEval);          l = CondT(doEval);
     if (l != Err) {          if (l != Err) {
         o = CondToken(doEval);                  o = CondToken(doEval);
   
         if (o == And) {                  if (o == And) {
             /* F -> T && F                      /* F -> T && F
              *                       *
              * If T is False, the whole thing will be False, but we have to                       * If T is False, the whole thing will be False, but we
              * parse the r.h.s. anyway (to throw it away).                       * have to parse the r.h.s. anyway (to throw it away).  If
              * If T is True, the result is the r.h.s., be it an Err or no.  */                       * T is True, the result is the r.h.s., be it an Err or no.
             if (l == True)                       * */
                 l = CondF(doEval);                      if (l == True)
             else                              l = CondF(doEval);
                 (void)CondF(false);                      else
         } else                              (void)CondF(false);
             /* F -> T.  */                  } else
             condPushBack = o;                          /* F -> T.      */
     }                          condPushBack = o;
     return l;          }
           return l;
 }  }
   
 /*-  /*-
Line 845 
Line 842 
 static Token  static Token
 CondE(bool doEval)  CondE(bool doEval)
 {  {
     Token   l, o;          Token l, o;
   
     l = CondF(doEval);          l = CondF(doEval);
     if (l != Err) {          if (l != Err) {
         o = CondToken(doEval);                  o = CondToken(doEval);
   
         if (o == Or) {                  if (o == Or) {
             /* E -> F || E                          /* E -> F || E
              *                           *
              * A similar thing occurs for ||, except that here we make sure                           * A similar thing occurs for ||, except that here we
              * the l.h.s. is False before we bother to evaluate the r.h.s.                           * make sure the l.h.s. is False before we bother to
              * Once again, if l is False, the result is the r.h.s. and once                           * evaluate the r.h.s.  Once again, if l is False, the
              * again if l is True, we parse the r.h.s. to throw it away.  */                           * result is the r.h.s. and once again if l is True, we
             if (l == False)                           * parse the r.h.s. to throw it away.  */
                 l = CondE(doEval);                          if (l == False)
             else                                  l = CondE(doEval);
                 (void)CondE(false);                          else
         } else                                  (void)CondE(false);
             /* E -> F.  */                  } else
             condPushBack = o;                          /* E -> F.      */
     }                          condPushBack = o;
     return l;          }
           return l;
 }  }
   
 /* Evaluate conditional in line.  /* Evaluate conditional in line.
Line 882 
Line 880 
 int  int
 Cond_Eval(const char *line)  Cond_Eval(const char *line)
 {  {
     /* find end of keyword */          /* find end of keyword */
     const char  *end;          const char *end;
     uint32_t    k;          uint32_t k;
     size_t      len;          size_t len;
     struct If   *ifp;          struct If *ifp;
     bool        value = false;          bool value = false;
     int         level;  /* Level at which to report errors. */          int level;      /* Level at which to report errors. */
   
     level = PARSE_FATAL;          level = PARSE_FATAL;
   
     for (end = line; islower(*end); end++)          for (end = line; islower(*end); end++)
         ;                  ;
     /* quick path: recognize special targets early on */          /* quick path: recognize special targets early on */
     if (*end == '.' || *end == ':')          if (*end == '.' || *end == ':')
         return COND_INVALID;  
     len = end - line;  
     k = ohash_interval(line, &end);  
     switch(k % MAGICSLOTS2) {  
     case K_COND_IF % MAGICSLOTS2:  
         if (k == K_COND_IF && len == strlen(COND_IF) &&  
             strncmp(line, COND_IF, len) == 0) {  
             ifp = ifs + COND_IF_INDEX;  
         } else  
             return COND_INVALID;  
         break;  
     case K_COND_IFDEF % MAGICSLOTS2:  
         if (k == K_COND_IFDEF && len == strlen(COND_IFDEF) &&  
             strncmp(line, COND_IFDEF, len) == 0) {  
             ifp = ifs + COND_IFDEF_INDEX;  
         } else  
             return COND_INVALID;  
         break;  
     case K_COND_IFNDEF % MAGICSLOTS2:  
         if (k == K_COND_IFNDEF && len == strlen(COND_IFNDEF) &&  
             strncmp(line, COND_IFNDEF, len) == 0) {  
             ifp = ifs + COND_IFNDEF_INDEX;  
         } else  
             return COND_INVALID;  
         break;  
     case K_COND_IFMAKE % MAGICSLOTS2:  
         if (k == K_COND_IFMAKE && len == strlen(COND_IFMAKE) &&  
             strncmp(line, COND_IFMAKE, len) == 0) {  
             ifp = ifs + COND_IFMAKE_INDEX;  
         } else  
             return COND_INVALID;  
         break;  
     case K_COND_IFNMAKE % MAGICSLOTS2:  
         if (k == K_COND_IFNMAKE && len == strlen(COND_IFNMAKE) &&  
             strncmp(line, COND_IFNMAKE, len) == 0) {  
             ifp = ifs + COND_IFNMAKE_INDEX;  
         } else  
             return COND_INVALID;  
         break;  
     case K_COND_ELIF % MAGICSLOTS2:  
         if (k == K_COND_ELIF && len == strlen(COND_ELIF) &&  
             strncmp(line, COND_ELIF, len) == 0) {  
             ifp = ifs + COND_ELIF_INDEX;  
         } else  
             return COND_INVALID;  
         break;  
     case K_COND_ELIFDEF % MAGICSLOTS2:  
         if (k == K_COND_ELIFDEF && len == strlen(COND_ELIFDEF) &&  
             strncmp(line, COND_ELIFDEF, len) == 0) {  
             ifp = ifs + COND_ELIFDEF_INDEX;  
         } else  
             return COND_INVALID;  
         break;  
     case K_COND_ELIFNDEF % MAGICSLOTS2:  
         if (k == K_COND_ELIFNDEF && len == strlen(COND_ELIFNDEF) &&  
             strncmp(line, COND_ELIFNDEF, len) == 0) {  
             ifp = ifs + COND_ELIFNDEF_INDEX;  
         } else  
             return COND_INVALID;  
         break;  
     case K_COND_ELIFMAKE % MAGICSLOTS2:  
         if (k == K_COND_ELIFMAKE && len == strlen(COND_ELIFMAKE) &&  
             strncmp(line, COND_ELIFMAKE, len) == 0) {  
             ifp = ifs + COND_ELIFMAKE_INDEX;  
         } else  
             return COND_INVALID;  
         break;  
     case K_COND_ELIFNMAKE % MAGICSLOTS2:  
         if (k == K_COND_ELIFNMAKE && len == strlen(COND_ELIFNMAKE) &&  
             strncmp(line, COND_ELIFNMAKE, len) == 0) {  
             ifp = ifs + COND_ELIFNMAKE_INDEX;  
         } else  
             return COND_INVALID;  
         break;  
     case K_COND_ELSE % MAGICSLOTS2:  
         /* valid conditional whose value is the inverse  
          * of the previous if we parsed.  */  
         if (k == K_COND_ELSE && len == strlen(COND_ELSE) &&  
             strncmp(line, COND_ELSE, len) == 0) {  
             if (condTop == MAXIF) {  
                 Parse_Error(level, "if-less else");  
                 return COND_INVALID;                  return COND_INVALID;
             } else if (skipIfLevel == 0) {          len = end - line;
                 value = !condStack[condTop].value;          k = ohash_interval(line, &end);
                 ifp = ifs + COND_ELSE_INDEX;          switch(k % MAGICSLOTS2) {
             } else          case K_COND_IF % MAGICSLOTS2:
                 return COND_SKIP;                  if (k == K_COND_IF && len == strlen(COND_IF) &&
         } else                      strncmp(line, COND_IF, len) == 0) {
             return COND_INVALID;                          ifp = ifs + COND_IF_INDEX;
         break;                  } else
     case K_COND_ENDIF % MAGICSLOTS2:                          return COND_INVALID;
         if (k == K_COND_ENDIF && len == strlen(COND_ENDIF) &&                  break;
             strncmp(line, COND_ENDIF, len) == 0) {          case K_COND_IFDEF % MAGICSLOTS2:
         /* End of a conditional section. If skipIfLevel is non-zero, that                  if (k == K_COND_IFDEF && len == strlen(COND_IFDEF) &&
          * conditional was skipped, so lines following it should also be                      strncmp(line, COND_IFDEF, len) == 0) {
          * skipped. Hence, we return COND_SKIP. Otherwise, the conditional                          ifp = ifs + COND_IFDEF_INDEX;
          * was read so succeeding lines should be parsed (think about it...)                  } else
          * so we return COND_PARSE, unless this endif isn't paired with                          return COND_INVALID;
          * a decent if.  */                  break;
             if (skipIfLevel != 0) {          case K_COND_IFNDEF % MAGICSLOTS2:
                 skipIfLevel--;                  if (k == K_COND_IFNDEF && len == strlen(COND_IFNDEF) &&
                 return COND_SKIP;                      strncmp(line, COND_IFNDEF, len) == 0) {
             } else {                          ifp = ifs + COND_IFNDEF_INDEX;
                 if (condTop == MAXIF) {                  } else
                     Parse_Error(level, "if-less endif");                          return COND_INVALID;
                     return COND_INVALID;                  break;
                 } else {          case K_COND_IFMAKE % MAGICSLOTS2:
                     skipLine = false;                  if (k == K_COND_IFMAKE && len == strlen(COND_IFMAKE) &&
                     condTop++;                      strncmp(line, COND_IFMAKE, len) == 0) {
                     return COND_PARSE;                          ifp = ifs + COND_IFMAKE_INDEX;
                 }                  } else
             }                          return COND_INVALID;
         } else                  break;
             return COND_INVALID;          case K_COND_IFNMAKE % MAGICSLOTS2:
         break;                  if (k == K_COND_IFNMAKE && len == strlen(COND_IFNMAKE) &&
                       strncmp(line, COND_IFNMAKE, len) == 0) {
                           ifp = ifs + COND_IFNMAKE_INDEX;
                   } else
                           return COND_INVALID;
                   break;
           case K_COND_ELIF % MAGICSLOTS2:
                   if (k == K_COND_ELIF && len == strlen(COND_ELIF) &&
                       strncmp(line, COND_ELIF, len) == 0) {
                           ifp = ifs + COND_ELIF_INDEX;
                   } else
                           return COND_INVALID;
                   break;
           case K_COND_ELIFDEF % MAGICSLOTS2:
                   if (k == K_COND_ELIFDEF && len == strlen(COND_ELIFDEF) &&
                       strncmp(line, COND_ELIFDEF, len) == 0) {
                           ifp = ifs + COND_ELIFDEF_INDEX;
                   } else
                           return COND_INVALID;
                   break;
           case K_COND_ELIFNDEF % MAGICSLOTS2:
                   if (k == K_COND_ELIFNDEF && len == strlen(COND_ELIFNDEF) &&
                       strncmp(line, COND_ELIFNDEF, len) == 0) {
                           ifp = ifs + COND_ELIFNDEF_INDEX;
                   } else
                           return COND_INVALID;
                   break;
           case K_COND_ELIFMAKE % MAGICSLOTS2:
                   if (k == K_COND_ELIFMAKE && len == strlen(COND_ELIFMAKE) &&
                       strncmp(line, COND_ELIFMAKE, len) == 0) {
                           ifp = ifs + COND_ELIFMAKE_INDEX;
                   } else
                           return COND_INVALID;
                   break;
           case K_COND_ELIFNMAKE % MAGICSLOTS2:
                   if (k == K_COND_ELIFNMAKE && len == strlen(COND_ELIFNMAKE) &&
                       strncmp(line, COND_ELIFNMAKE, len) == 0) {
                           ifp = ifs + COND_ELIFNMAKE_INDEX;
                   } else
                           return COND_INVALID;
                   break;
           case K_COND_ELSE % MAGICSLOTS2:
                   /* valid conditional whose value is the inverse
                    * of the previous if we parsed.  */
                   if (k == K_COND_ELSE && len == strlen(COND_ELSE) &&
                       strncmp(line, COND_ELSE, len) == 0) {
                           if (condTop == MAXIF) {
                                   Parse_Error(level, "if-less else");
                                   return COND_INVALID;
                           } else if (skipIfLevel == 0) {
                                   value = !condStack[condTop].value;
                                   ifp = ifs + COND_ELSE_INDEX;
                           } else
                                   return COND_SKIP;
                   } else
                           return COND_INVALID;
                   break;
           case K_COND_ENDIF % MAGICSLOTS2:
                   if (k == K_COND_ENDIF && len == strlen(COND_ENDIF) &&
                       strncmp(line, COND_ENDIF, len) == 0) {
                           /* End of a conditional section. If skipIfLevel is
                            * non-zero, that conditional was skipped, so lines
                            * following it should also be skipped. Hence, we
                            * return COND_SKIP. Otherwise, the conditional was
                            * read so succeeding lines should be parsed (think
                            * about it...) so we return COND_PARSE, unless this
                            * endif isn't paired with a decent if.  */
                           if (skipIfLevel != 0) {
                                   skipIfLevel--;
                                   return COND_SKIP;
                           } else {
                                   if (condTop == MAXIF) {
                                           Parse_Error(level, "if-less endif");
                                           return COND_INVALID;
                                   } else {
                                           skipLine = false;
                                           condTop++;
                                           return COND_PARSE;
                                   }
                           }
                   } else
                           return COND_INVALID;
                   break;
   
         /* Recognize other keywords there, to simplify parser's task */          /* Recognize other keywords there, to simplify parser's task */
     case K_COND_FOR % MAGICSLOTS2:          case K_COND_FOR % MAGICSLOTS2:
         if (k == K_COND_FOR && len == strlen(COND_FOR) &&                  if (k == K_COND_FOR && len == strlen(COND_FOR) &&
             strncmp(line, COND_FOR, len) == 0)                      strncmp(line, COND_FOR, len) == 0)
             return COND_ISFOR;                          return COND_ISFOR;
         else                  else
             return COND_INVALID;                          return COND_INVALID;
     case K_COND_UNDEF % MAGICSLOTS2:          case K_COND_UNDEF % MAGICSLOTS2:
         if (k == K_COND_UNDEF && len == strlen(COND_UNDEF) &&                  if (k == K_COND_UNDEF && len == strlen(COND_UNDEF) &&
             strncmp(line, COND_UNDEF, len) == 0)                      strncmp(line, COND_UNDEF, len) == 0)
             return COND_ISUNDEF;                          return COND_ISUNDEF;
         else                  else
             return COND_INVALID;                          return COND_INVALID;
     case K_COND_POISON % MAGICSLOTS2:          case K_COND_POISON % MAGICSLOTS2:
         if (k == K_COND_POISON && len == strlen(COND_POISON) &&                  if (k == K_COND_POISON && len == strlen(COND_POISON) &&
             strncmp(line, COND_POISON, len) == 0)                      strncmp(line, COND_POISON, len) == 0)
             return COND_ISPOISON;                          return COND_ISPOISON;
         else                  else
             return COND_INVALID;                          return COND_INVALID;
     case K_COND_INCLUDE % MAGICSLOTS2:          case K_COND_INCLUDE % MAGICSLOTS2:
         if (k == K_COND_INCLUDE && len == strlen(COND_INCLUDE) &&                  if (k == K_COND_INCLUDE && len == strlen(COND_INCLUDE) &&
             strncmp(line, COND_INCLUDE, len) == 0)                      strncmp(line, COND_INCLUDE, len) == 0)
             return COND_ISINCLUDE;                          return COND_ISINCLUDE;
         else                  else
             return COND_INVALID;                          return COND_INVALID;
     default:          default:
         /* Not a valid conditional type. No error...  */                  /* Not a valid conditional type. No error...  */
         return COND_INVALID;                  return COND_INVALID;
     }          }
   
     if (ifp->isElse) {          if (ifp->isElse) {
         if (condTop == MAXIF) {                  if (condTop == MAXIF) {
             Parse_Error(level, "if-less elif");                          Parse_Error(level, "if-less elif");
             return COND_INVALID;                          return COND_INVALID;
         } else if (skipIfLevel != 0) {                  } else if (skipIfLevel != 0) {
             /* If skipping this conditional, just ignore the whole thing.                          /* If skipping this conditional, just ignore the whole
              * If we don't, the user might be employing a variable that's                           * thing.  If we don't, the user might be employing a
              * undefined, for which there's an enclosing ifdef that                           * variable that's undefined, for which there's an
              * we're skipping...  */                           * enclosing ifdef that we're skipping...  */
             return COND_SKIP;                          return COND_SKIP;
                   }
           } else if (skipLine) {
                   /* Don't even try to evaluate a conditional that's not an else
                    * if we're skipping things...  */
                   skipIfLevel++;
                   return COND_SKIP;
         }          }
     } else if (skipLine) {  
         /* Don't even try to evaluate a conditional that's not an else if  
          * we're skipping things...  */  
         skipIfLevel++;  
         return COND_SKIP;  
     }  
   
     if (ifp->defProc) {          if (ifp->defProc) {
         /* Initialize file-global variables for parsing.  */                  /* Initialize file-global variables for parsing.  */
         condDefProc = ifp->defProc;                  condDefProc = ifp->defProc;
         condInvert = ifp->doNot;                  condInvert = ifp->doNot;
   
         line += len;                  line += len;
   
         while (*line == ' ' || *line == '\t')                  while (*line == ' ' || *line == '\t')
             line++;                          line++;
   
         condExpr = line;                  condExpr = line;
         condPushBack = None;                  condPushBack = None;
   
         switch (CondE(true)) {                  switch (CondE(true)) {
             case True:                  case True:
                 if (CondToken(true) == EndOfFile) {                          if (CondToken(true) == EndOfFile) {
                     value = true;                                  value = true;
                     break;                                  break;
                           }
                           goto err;
                           /* FALLTHROUGH */
                   case False:
                           if (CondToken(true) == EndOfFile) {
                                   value = false;
                                   break;
                           }
                           /* FALLTHROUGH */
                   case Err:
   err:
                           Parse_Error(level, "Malformed conditional (%s)", line);
                           return COND_INVALID;
                   default:
                           break;
                 }                  }
                 goto err;  
                 /* FALLTHROUGH */  
             case False:  
                 if (CondToken(true) == EndOfFile) {  
                     value = false;  
                     break;  
                 }  
                 /* FALLTHROUGH */  
             case Err:  
             err:  
                 Parse_Error(level, "Malformed conditional (%s)", line);  
                 return COND_INVALID;  
             default:  
                 break;  
         }          }
     }  
   
     if (!ifp->isElse)          if (!ifp->isElse)
         condTop--;                  condTop--;
     else if (skipIfLevel != 0 || condStack[condTop].value) {          else if (skipIfLevel != 0 || condStack[condTop].value) {
         /* If this is an else-type conditional, it should only take effect                  /* If this is an else-type conditional, it should only take
          * if its corresponding if was evaluated and false. If its if was                   * effect if its corresponding if was evaluated and false. If
          * true or skipped, we return COND_SKIP (and start skipping in case                   * its if was true or skipped, we return COND_SKIP (and start
          * we weren't already), leaving the stack unmolested so later elif's                   * skipping in case we weren't already), leaving the stack
          * don't screw up...  */                   * unmolested so later elif's don't screw up...  */
         skipLine = true;                  skipLine = true;
         return COND_SKIP;                  return COND_SKIP;
     }          }
   
     if (condTop < 0) {          if (condTop < 0) {
         /* This is the one case where we can definitely proclaim a fatal                  /* This is the one case where we can definitely proclaim a fatal
          * error. If we don't, we're hosed.  */                   * error. If we don't, we're hosed.  */
         Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF);                  Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.",
         condTop = 0;                      MAXIF);
         return COND_INVALID;                  condTop = 0;
     } else {                  return COND_INVALID;
         condStack[condTop].value = value;          } else {
         condStack[condTop].lineno = Parse_Getlineno();                  condStack[condTop].value = value;
         condStack[condTop].filename = Parse_Getfilename();                  condStack[condTop].lineno = Parse_Getlineno();
         skipLine = !value;                  condStack[condTop].filename = Parse_Getfilename();
         return value ? COND_PARSE : COND_SKIP;                  skipLine = !value;
     }                  return value ? COND_PARSE : COND_SKIP;
           }
 }  }
   
 void  void
 Cond_End(void)  Cond_End(void)
 {  {
     int i;          int i;
   
     if (condTop != MAXIF) {          if (condTop != MAXIF) {
         Parse_Error(PARSE_FATAL, "%s%d open conditional%s",                  Parse_Error(PARSE_FATAL, "%s%d open conditional%s",
             condTop == 0 ? "at least ": "", MAXIF-condTop,                      condTop == 0 ? "at least ": "", MAXIF-condTop,
             MAXIF-condTop == 1 ? "" : "s");                      MAXIF-condTop == 1 ? "" : "s");
         for (i = MAXIF-1; i >= condTop; i--) {                  for (i = MAXIF-1; i >= condTop; i--) {
             fprintf(stderr, "\t at line %lu of %s\n", condStack[i].lineno,                          fprintf(stderr, "\t at line %lu of %s\n",
                 condStack[i].filename);                              condStack[i].lineno, condStack[i].filename);
                   }
         }          }
     }          condTop = MAXIF;
     condTop = MAXIF;  
 }  }

Legend:
Removed from v.1.38  
changed lines
  Added in v.1.39