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

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /src/usr.bin/make/cond.c between version 1.26 and 1.75

version 1.26, 2005/03/01 04:34:55 version 1.75, 2017/04/16 20:59:04
Line 91  __RCSID("$NetBSD$");
Line 91  __RCSID("$NetBSD$");
  *   *
  */   */
   
   #include    <assert.h>
 #include    <ctype.h>  #include    <ctype.h>
   #include    <errno.h>    /* For strtoul() error checking */
   
 #include    "make.h"  #include    "make.h"
 #include    "hash.h"  #include    "hash.h"
Line 123  __RCSID("$NetBSD$");
Line 125  __RCSID("$NetBSD$");
  * is applied.   * is applied.
  *   *
  * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)   * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
  * will return And for '&' and '&&', Or for '|' and '||', Not for '!',   * will return TOK_AND for '&' and '&&', TOK_OR for '|' and '||',
  * LParen for '(', RParen for ')' and will evaluate the other terminal   * TOK_NOT for '!', TOK_LPAREN for '(', TOK_RPAREN for ')' and will evaluate
  * symbols, using either the default function or the function given in the   * the other terminal symbols, using either the default function or the
  * terminal, and return the result as either True or False.   * function given in the terminal, and return the result as either TOK_TRUE
    * or TOK_FALSE.
  *   *
  * All Non-Terminal functions (CondE, CondF and CondT) return Err on error.   * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons.
    *
    * All Non-Terminal functions (CondE, CondF and CondT) return TOK_ERROR on
    * error.
  */   */
 typedef enum {  typedef enum {
     And, Or, Not, True, False, LParen, RParen, EndOfFile, None, Err      TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT,
       TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
 } Token;  } Token;
   
 /*-  /*-
Line 139  typedef enum {
Line 146  typedef enum {
  * last two fields are stored in condInvert and condDefProc, respectively.   * last two fields are stored in condInvert and condDefProc, respectively.
  */   */
 static void CondPushBack(Token);  static void CondPushBack(Token);
 static int CondGetArg(char **, char **, const char *, Boolean);  static int CondGetArg(char **, char **, const char *);
 static Boolean CondDoDefined(int, char *);  static Boolean CondDoDefined(int, const char *);
 static int CondStrMatch(ClientData, ClientData);  static int CondStrMatch(const void *, const void *);
 static Boolean CondDoMake(int, char *);  static Boolean CondDoMake(int, const char *);
 static Boolean CondDoExists(int, char *);  static Boolean CondDoExists(int, const char *);
 static Boolean CondDoTarget(int, char *);  static Boolean CondDoTarget(int, const char *);
 static Boolean CondDoCommands(int, char *);  static Boolean CondDoCommands(int, const char *);
 static char * CondCvtArg(char *, double *);  static Boolean CondCvtArg(char *, double *);
 static Token CondToken(Boolean);  static Token CondToken(Boolean);
 static Token CondT(Boolean);  static Token CondT(Boolean);
 static Token CondF(Boolean);  static Token CondF(Boolean);
 static Token CondE(Boolean);  static Token CondE(Boolean);
   static int do_Cond_EvalExpression(Boolean *);
   
 static struct If {  static const struct If {
     const char  *form;        /* Form of if */      const char  *form;        /* Form of if */
     int         formlen;      /* Length of form */      int         formlen;      /* Length of form */
     Boolean     doNot;        /* TRUE if default function should be negated */      Boolean     doNot;        /* TRUE if default function should be negated */
     Boolean     (*defProc)(int, char *); /* Default function to apply */      Boolean     (*defProc)(int, const char *); /* Default function to apply */
 } ifs[] = {  } ifs[] = {
     { "ifdef",    5,      FALSE,  CondDoDefined },      { "def",      3,      FALSE,  CondDoDefined },
     { "ifndef",   6,      TRUE,   CondDoDefined },      { "ndef",     4,      TRUE,   CondDoDefined },
     { "ifmake",   6,      FALSE,  CondDoMake },      { "make",     4,      FALSE,  CondDoMake },
     { "ifnmake",  7,      TRUE,   CondDoMake },      { "nmake",    5,      TRUE,   CondDoMake },
     { "if",       2,      FALSE,  CondDoDefined },      { "",         0,      FALSE,  CondDoDefined },
     { NULL,       0,      FALSE,  NULL }      { NULL,       0,      FALSE,  NULL }
 };  };
   
 static Boolean    condInvert;           /* Invert the default function */  static const struct If *if_info;        /* Info for current statement */
 static Boolean    (*condDefProc)(int, char *);  /* Default function to apply */  
 static char       *condExpr;            /* The expression to parse */  static char       *condExpr;            /* The expression to parse */
 static Token      condPushBack=None;    /* Single push-back token used in  static Token      condPushBack=TOK_NONE;        /* Single push-back token used in
                                          * parsing */                                           * parsing */
   
 #define MAXIF           64        /* greatest depth of #if'ing */  static unsigned int     cond_depth = 0;         /* current .if nesting level */
   static unsigned int     cond_min_depth = 0;     /* depth at makefile open */
   
 static Boolean    finalElse[MAXIF+1][MAXIF+1]; /* Seen final else (stack) */  /*
 static Boolean    condStack[MAXIF];     /* Stack of conditionals's values */   * Indicate when we should be strict about lhs of comparisons.
 static int        condTop = MAXIF;      /* Top-most conditional */   * TRUE when Cond_EvalExpression is called from Cond_Eval (.if etc)
 static int        skipIfLevel=0;        /* Depth of skipped conditionals */   * FALSE when Cond_EvalExpression is called from var.c:ApplyModifiers
 static Boolean    skipLine = FALSE;     /* Whether the parse module is skipping   * since lhs is already expanded and we cannot tell if
                                          * lines */   * it was a variable reference or not.
    */
   static Boolean lhsStrict;
   
 static int  static int
 istoken(const char *str, const char *tok, size_t len)  istoken(const char *str, const char *tok, size_t len)
Line 228  CondPushBack(Token t)
Line 238  CondPushBack(Token t)
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static int  static int
 CondGetArg(char **linePtr, char **argPtr, const char *func, Boolean parens)  CondGetArg(char **linePtr, char **argPtr, const char *func)
 {  {
     char          *cp;      char          *cp;
     int           argLen;      int           argLen;
     Buffer        buf;      Buffer        buf;
       int           paren_depth;
       char          ch;
   
     cp = *linePtr;      cp = *linePtr;
     if (parens) {      if (func != NULL)
         while (*cp != '(' && *cp != '\0') {          /* Skip opening '(' - verfied by caller */
             cp++;          cp++;
         }  
         if (*cp == '(') {  
             cp++;  
         }  
     }  
   
     if (*cp == '\0') {      if (*cp == '\0') {
         /*          /*
Line 251  CondGetArg(char **linePtr, char **argPtr
Line 258  CondGetArg(char **linePtr, char **argPtr
          * than hitting the user with a warning message every time s/he uses           * than hitting the user with a warning message every time s/he uses
          * the word 'make' or 'defined' at the beginning of a symbol...           * the word 'make' or 'defined' at the beginning of a symbol...
          */           */
         *argPtr = cp;          *argPtr = NULL;
         return (0);          return (0);
     }      }
   
Line 263  CondGetArg(char **linePtr, char **argPtr
Line 270  CondGetArg(char **linePtr, char **argPtr
      * Create a buffer for the argument and start it out at 16 characters       * Create a buffer for the argument and start it out at 16 characters
      * long. Why 16? Why not?       * long. Why 16? Why not?
      */       */
     buf = Buf_Init(16);      Buf_Init(&buf, 16);
   
     while ((strchr(" \t)&|", *cp) == (char *)NULL) && (*cp != '\0')) {      paren_depth = 0;
       for (;;) {
           ch = *cp;
           if (ch == 0 || ch == ' ' || ch == '\t')
               break;
           if ((ch == '&' || ch == '|') && paren_depth == 0)
               break;
         if (*cp == '$') {          if (*cp == '$') {
             /*              /*
              * Parse the variable spec and install it as part of the argument               * Parse the variable spec and install it as part of the argument
Line 275  CondGetArg(char **linePtr, char **argPtr
Line 288  CondGetArg(char **linePtr, char **argPtr
              */               */
             char        *cp2;              char        *cp2;
             int         len;              int         len;
             Boolean     doFree;              void        *freeIt;
   
             cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree);  
   
             Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);              cp2 = Var_Parse(cp, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES,
             if (doFree) {                              &len, &freeIt);
                 free(cp2);              Buf_AddBytes(&buf, strlen(cp2), cp2);
             }              free(freeIt);
             cp += len;              cp += len;
         } else {              continue;
             Buf_AddByte(buf, (Byte)*cp);  
             cp++;  
         }          }
           if (ch == '(')
               paren_depth++;
           else
               if (ch == ')' && --paren_depth < 0)
                   break;
           Buf_AddByte(&buf, *cp);
           cp++;
     }      }
   
     Buf_AddByte(buf, (Byte)'\0');      *argPtr = Buf_GetAll(&buf, &argLen);
     *argPtr = (char *)Buf_GetAll(buf, &argLen);      Buf_Destroy(&buf, FALSE);
     Buf_Destroy(buf, FALSE);  
   
     while (*cp == ' ' || *cp == '\t') {      while (*cp == ' ' || *cp == '\t') {
         cp++;          cp++;
     }      }
     if (parens && *cp != ')') {  
       if (func != NULL && *cp++ != ')') {
         Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",          Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
                      func);                       func);
         return (0);          return (0);
     } else if (parens) {  
         /*  
          * Advance pointer past close parenthesis.  
          */  
         cp++;  
     }      }
   
     *linePtr = cp;      *linePtr = cp;
Line 326  CondGetArg(char **linePtr, char **argPtr
Line 337  CondGetArg(char **linePtr, char **argPtr
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static Boolean  static Boolean
 CondDoDefined(int argLen, char *arg)  CondDoDefined(int argLen MAKE_ATTR_UNUSED, const char *arg)
 {  {
     char    savec = arg[argLen];  
     char    *p1;      char    *p1;
     Boolean result;      Boolean result;
   
     arg[argLen] = '\0';      if (Var_Value(arg, VAR_CMD, &p1) != NULL) {
     if (Var_Value(arg, VAR_CMD, &p1) != (char *)NULL) {  
         result = TRUE;          result = TRUE;
     } else {      } else {
         result = FALSE;          result = FALSE;
     }      }
     if (p1)  
         free(p1);      free(p1);
     arg[argLen] = savec;  
     return (result);      return (result);
 }  }
   
Line 359  CondDoDefined(int argLen, char *arg)
Line 367  CondDoDefined(int argLen, char *arg)
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static int  static int
 CondStrMatch(ClientData string, ClientData pattern)  CondStrMatch(const void *string, const void *pattern)
 {  {
     return(!Str_Match((char *) string,(char *) pattern));      return(!Str_Match(string, pattern));
 }  }
   
 /*-  /*-
Line 378  CondStrMatch(ClientData string, ClientDa
Line 386  CondStrMatch(ClientData string, ClientDa
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static Boolean  static Boolean
 CondDoMake(int argLen, char *arg)  CondDoMake(int argLen MAKE_ATTR_UNUSED, const char *arg)
 {  {
     char    savec = arg[argLen];      return Lst_Find(create, arg, CondStrMatch) != NULL;
     Boolean result;  
   
     arg[argLen] = '\0';  
     if (Lst_Find(create, (ClientData)arg, CondStrMatch) == NILLNODE) {  
         result = FALSE;  
     } else {  
         result = TRUE;  
     }  
     arg[argLen] = savec;  
     return (result);  
 }  }
   
 /*-  /*-
Line 407  CondDoMake(int argLen, char *arg)
Line 405  CondDoMake(int argLen, char *arg)
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static Boolean  static Boolean
 CondDoExists(int argLen, char *arg)  CondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg)
 {  {
     char    savec = arg[argLen];  
     Boolean result;      Boolean result;
     char    *path;      char    *path;
   
     arg[argLen] = '\0';  
     path = Dir_FindFile(arg, dirSearchPath);      path = Dir_FindFile(arg, dirSearchPath);
     if (path != (char *)NULL) {      if (DEBUG(COND)) {
           fprintf(debug_file, "exists(%s) result is \"%s\"\n",
                  arg, path ? path : "");
       }
       if (path != NULL) {
         result = TRUE;          result = TRUE;
         free(path);          free(path);
     } else {      } else {
         result = FALSE;          result = FALSE;
     }      }
     arg[argLen] = savec;  
     return (result);      return (result);
 }  }
   
Line 439  CondDoExists(int argLen, char *arg)
Line 438  CondDoExists(int argLen, char *arg)
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static Boolean  static Boolean
 CondDoTarget(int argLen, char *arg)  CondDoTarget(int argLen MAKE_ATTR_UNUSED, const char *arg)
 {  {
     char    savec = arg[argLen];  
     Boolean result;  
     GNode   *gn;      GNode   *gn;
   
     arg[argLen] = '\0';  
     gn = Targ_FindNode(arg, TARG_NOCREATE);      gn = Targ_FindNode(arg, TARG_NOCREATE);
     if ((gn != NILGNODE) && !OP_NOP(gn->type)) {      return (gn != NULL) && !OP_NOP(gn->type);
         result = TRUE;  
     } else {  
         result = FALSE;  
     }  
     arg[argLen] = savec;  
     return (result);  
 }  }
   
 /*-  /*-
Line 472  CondDoTarget(int argLen, char *arg)
Line 462  CondDoTarget(int argLen, char *arg)
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static Boolean  static Boolean
 CondDoCommands(int argLen, char *arg)  CondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg)
 {  {
     char    savec = arg[argLen];  
     Boolean result;  
     GNode   *gn;      GNode   *gn;
   
     arg[argLen] = '\0';  
     gn = Targ_FindNode(arg, TARG_NOCREATE);      gn = Targ_FindNode(arg, TARG_NOCREATE);
     if ((gn != NILGNODE) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands)) {      return (gn != NULL) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands);
         result = TRUE;  
     } else {  
         result = FALSE;  
     }  
     arg[argLen] = savec;  
     return (result);  
 }  }
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  * CondCvtArg --   * CondCvtArg --
  *      Convert the given number into a double. If the number begins   *      Convert the given number into a double.
  *      with 0x, it is interpreted as a hexadecimal integer   *      We try a base 10 or 16 integer conversion first, if that fails
  *      and converted to a double from there. All other strings just have   *      then we try a floating point conversion instead.
  *      strtod called on them.  
  *   *
  * Results:   * Results:
  *      Sets 'value' to double value of string.   *      Sets 'value' to double value of string.
  *      Returns NULL if string was fully consumed,   *      Returns 'true' if the convertion suceeded
  *      else returns remaining input.  
  *  
  * Side Effects:  
  *      Can change 'value' even if string is not a valid number.  
  *  
  *   *
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static char *  static Boolean
 CondCvtArg(char *str, double *value)  CondCvtArg(char *str, double *value)
 {  {
     if ((*str == '0') && (str[1] == 'x')) {      char *eptr, ech;
         long i;      unsigned long l_val;
       double d_val;
         for (str += 2, i = 0; *str; str++) {  
             int x;      errno = 0;
             if (isdigit((unsigned char) *str))      if (!*str) {
                 x  = *str - '0';          *value = (double)0;
             else if (isxdigit((unsigned char) *str))          return TRUE;
                 x = 10 + *str - isupper((unsigned char) *str) ? 'A' : 'a';      }
             else      l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10);
                 break;      ech = *eptr;
             i = (i << 4) + x;      if (ech == 0 && errno != ERANGE) {
         }          d_val = str[0] == '-' ? -(double)-l_val : (double)l_val;
         *value = (double) i;  
         return *str ? str : NULL;  
     } else {      } else {
         char *eptr;          if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E')
         *value = strtod(str, &eptr);              return FALSE;
         return *eptr ? eptr : NULL;          d_val = strtod(str, &eptr);
           if (*eptr)
               return FALSE;
     }      }
   
       *value = d_val;
       return TRUE;
 }  }
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  * CondGetString --   * CondGetString --
Line 540  CondCvtArg(char *str, double *value)
Line 518  CondCvtArg(char *str, double *value)
  *      string.  This is called for the lhs and rhs of string compares.   *      string.  This is called for the lhs and rhs of string compares.
  *   *
  * Results:   * Results:
  *      Sets doFree if needed,   *      Sets freeIt if needed,
  *      Sets quoted if string was quoted,   *      Sets quoted if string was quoted,
  *      Returns NULL on error,   *      Returns NULL on error,
  *      else returns string - absent any quotes.   *      else returns string - absent any quotes.
Line 551  CondCvtArg(char *str, double *value)
Line 529  CondCvtArg(char *str, double *value)
  *   *
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
   /* coverity:[+alloc : arg-*2] */
 static char *  static char *
 CondGetString(Boolean doEval, Boolean *quoted, Boolean *doFree)  CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
 {  {
     Buffer buf;      Buffer buf;
     char *cp;      char *cp;
Line 561  CondGetString(Boolean doEval, Boolean *q
Line 540  CondGetString(Boolean doEval, Boolean *q
     int qt;      int qt;
     char *start;      char *start;
   
     buf = Buf_Init(0);      Buf_Init(&buf, 0);
     str = NULL;      str = NULL;
       *freeIt = NULL;
     *quoted = qt = *condExpr == '"' ? 1 : 0;      *quoted = qt = *condExpr == '"' ? 1 : 0;
     if (qt)      if (qt)
         condExpr++;          condExpr++;
Line 571  CondGetString(Boolean doEval, Boolean *q
Line 551  CondGetString(Boolean doEval, Boolean *q
         case '\\':          case '\\':
             if (condExpr[1] != '\0') {              if (condExpr[1] != '\0') {
                 condExpr++;                  condExpr++;
                 Buf_AddByte(buf, (Byte)*condExpr);                  Buf_AddByte(&buf, *condExpr);
             }              }
             break;              break;
         case '"':          case '"':
Line 579  CondGetString(Boolean doEval, Boolean *q
Line 559  CondGetString(Boolean doEval, Boolean *q
                 condExpr++;             /* we don't want the quotes */                  condExpr++;             /* we don't want the quotes */
                 goto got_str;                  goto got_str;
             } else              } else
                 Buf_AddByte(buf, (Byte)*condExpr); /* likely? */                  Buf_AddByte(&buf, *condExpr); /* likely? */
             break;              break;
         case ')':          case ')':
         case '!':          case '!':
Line 591  CondGetString(Boolean doEval, Boolean *q
Line 571  CondGetString(Boolean doEval, Boolean *q
             if (!qt)              if (!qt)
                 goto got_str;                  goto got_str;
             else              else
                 Buf_AddByte(buf, (Byte)*condExpr);                  Buf_AddByte(&buf, *condExpr);
             break;              break;
         case '$':          case '$':
             /* if we are in quotes, then an undefined variable is ok */              /* if we are in quotes, then an undefined variable is ok */
             str = Var_Parse(condExpr, VAR_CMD, (qt ? 0 : doEval),              str = Var_Parse(condExpr, VAR_CMD,
                             &len, doFree);                              ((!qt && doEval) ? VARF_UNDEFERR : 0) |
                               VARF_WANTRES, &len, freeIt);
             if (str == var_Error) {              if (str == var_Error) {
                   if (*freeIt) {
                       free(*freeIt);
                       *freeIt = NULL;
                   }
                 /*                  /*
                  * 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.
Line 621  CondGetString(Boolean doEval, Boolean *q
Line 606  CondGetString(Boolean doEval, Boolean *q
              * Nope, we better copy str to buf               * Nope, we better copy str to buf
              */               */
             for (cp = str; *cp; cp++) {              for (cp = str; *cp; cp++) {
                 Buf_AddByte(buf, (Byte)*cp);                  Buf_AddByte(&buf, *cp);
               }
               if (*freeIt) {
                   free(*freeIt);
                   *freeIt = NULL;
             }              }
             if (*doFree)  
                 free(str);  
             *doFree = FALSE;  
             str = NULL;                 /* not finished yet */              str = NULL;                 /* not finished yet */
             condExpr--;                 /* don't skip over next char */              condExpr--;                 /* don't skip over next char */
             break;              break;
         default:          default:
             Buf_AddByte(buf, (Byte)*condExpr);              if (strictLHS && !qt && *start != '$' &&
                   !isdigit((unsigned char) *start)) {
                   /* lhs must be quoted, a variable reference or number */
                   if (*freeIt) {
                       free(*freeIt);
                       *freeIt = NULL;
                   }
                   str = NULL;
                   goto cleanup;
               }
               Buf_AddByte(&buf, *condExpr);
             break;              break;
         }          }
     }      }
  got_str:   got_str:
     Buf_AddByte(buf, (Byte)'\0');      str = Buf_GetAll(&buf, NULL);
     str = (char *)Buf_GetAll(buf, NULL);      *freeIt = str;
     *doFree = TRUE;  
  cleanup:   cleanup:
     Buf_Destroy(buf, FALSE);      Buf_Destroy(&buf, FALSE);
     return str;      return str;
 }  }
   
Line 652  CondGetString(Boolean doEval, Boolean *q
Line 647  CondGetString(Boolean doEval, Boolean *q
  *      A Token for the next lexical token in the stream.   *      A Token for the next lexical token in the stream.
  *   *
  * Side Effects:   * Side Effects:
  *      condPushback will be set back to None if it is used.   *      condPushback will be set back to TOK_NONE if it is used.
  *   *
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static Token  static Token
 CondToken(Boolean doEval)  compare_expression(Boolean doEval)
 {  {
     Token         t;      Token       t;
       char        *lhs;
       char        *rhs;
       char        *op;
       void        *lhsFree;
       void        *rhsFree;
       Boolean lhsQuoted;
       Boolean rhsQuoted;
       double      left, right;
   
       t = TOK_ERROR;
       rhs = NULL;
       lhsFree = rhsFree = FALSE;
       lhsQuoted = rhsQuoted = FALSE;
   
       /*
        * Parse the variable spec and skip over it, saving its
        * value in lhs.
        */
       lhs = CondGetString(doEval, &lhsQuoted, &lhsFree, lhsStrict);
       if (!lhs)
           goto done;
   
     if (condPushBack == None) {      /*
         while (*condExpr == ' ' || *condExpr == '\t') {       * Skip whitespace to get to the operator
             condExpr++;       */
         }      while (isspace((unsigned char) *condExpr))
         switch (*condExpr) {          condExpr++;
             case '(':  
                 t = LParen;  
                 condExpr++;  
                 break;  
             case ')':  
                 t = RParen;  
                 condExpr++;  
                 break;  
             case '|':  
                 if (condExpr[1] == '|') {  
                     condExpr++;  
                 }  
                 condExpr++;  
                 t = Or;  
                 break;  
             case '&':  
                 if (condExpr[1] == '&') {  
                     condExpr++;  
                 }  
                 condExpr++;  
                 t = And;  
                 break;  
             case '!':  
                 t = Not;  
                 condExpr++;  
                 break;  
             case '#':  
             case '\n':  
             case '\0':  
                 t = EndOfFile;  
                 break;  
             case '"':  
             case '$': {  
                 char    *lhs;  
                 char    *rhs;  
                 char    *op;  
                 Boolean lhsFree;  
                 Boolean rhsFree;  
                 Boolean lhsQuoted;  
                 Boolean rhsQuoted;  
   
                 lhsFree = rhsFree = FALSE;  
                 lhsQuoted = rhsQuoted = FALSE;  
   
                 /*  
                  * Parse the variable spec and skip over it, saving its  
                  * value in lhs.  
                  */  
                 t = Err;  
                 lhs = CondGetString(doEval, &lhsQuoted, &lhsFree);  
                 if (!lhs)  
                     return Err;  
                 /*  
                  * Skip whitespace to get to the operator  
                  */  
                 while (isspace((unsigned char) *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 = UNCONST("!=");              if (!doEval) {
                         if (lhsQuoted)                  t = TOK_FALSE;
                             rhs = UNCONST("");                  goto done;
                         else              }
                             rhs = UNCONST("0");              /* For .ifxxx "..." check for non-empty string. */
               if (lhsQuoted) {
                   t = lhs[0] != 0;
                   goto done;
               }
               /* For .ifxxx <number> compare against zero */
               if (CondCvtArg(lhs, &left)) {
                   t = left != 0.0;
                   goto done;
               }
               /* For .if ${...} check for non-empty string (defProc is ifdef). */
               if (if_info->form[0] == 0) {
                   t = lhs[0] != 0;
                   goto done;
               }
               /* Otherwise action default test ... */
               t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot;
               goto done;
       }
   
                         goto do_compare;      while (isspace((unsigned char)*condExpr))
                 }          condExpr++;
                 while (isspace((unsigned char) *condExpr)) {  
                     condExpr++;      if (*condExpr == '\0') {
                 }          Parse_Error(PARSE_WARNING,
                 if (*condExpr == '\0') {                      "Missing right-hand-side of operator");
                     Parse_Error(PARSE_WARNING,          goto done;
                                 "Missing right-hand-side of operator");      }
                     goto error;  
                 }      rhs = CondGetString(doEval, &rhsQuoted, &rhsFree, FALSE);
                 rhs = CondGetString(doEval, &rhsQuoted, &rhsFree);      if (!rhs)
                 if (!rhs)          goto done;
                     return Err;  
 do_compare:      if (rhsQuoted || lhsQuoted) {
                 if (rhsQuoted || lhsQuoted) {  
 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 done;
                     }          }
   
                     if (DEBUG(COND)) {  
                         printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n",  
                                lhs, rhs, op);  
                     }  
                     /*  
                      * Null-terminate rhs and perform the comparison.  
                      * t is set to the result.  
                      */  
                     if (*op == '=') {  
                         t = strcmp(lhs, rhs) ? False : True;  
                     } else {  
                         t = strcmp(lhs, rhs) ? True : False;  
                     }  
                 } else {  
                     /*  
                      * rhs is either a float or an integer. Convert both the  
                      * lhs and the rhs to a double and compare the two.  
                      */  
                     double      left, right;  
                     char        *cp;  
   
                     if (CondCvtArg(lhs, &left))  
                         goto do_string_compare;  
                     if ((cp = CondCvtArg(rhs, &right)) &&  
                             cp == rhs)  
                         goto do_string_compare;  
   
                     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:  
                 if (lhsFree)  
                     free(lhs);  
                 if (rhsFree)  
                     free(rhs);  
                 break;  
             }  
             default: {  
                 Boolean (*evalProc)(int, char *);  
                 Boolean invert = FALSE;  
                 char    *arg;  
                 int     arglen;  
   
                 if (istoken(condExpr, "defined", 7)) {  
                     /*  
                      * Use CondDoDefined to evaluate the argument and  
                      * CondGetArg to extract the argument from the 'function  
                      * call'.  
                      */  
                     evalProc = CondDoDefined;  
                     condExpr += 7;  
                     arglen = CondGetArg(&condExpr, &arg, "defined", TRUE);  
                     if (arglen == 0) {  
                         condExpr -= 7;  
                         goto use_default;  
                     }  
                 } else if (istoken(condExpr, "make", 4)) {  
                     /*  
                      * Use CondDoMake to evaluate the argument and  
                      * CondGetArg to extract the argument from the 'function  
                      * call'.  
                      */  
                     evalProc = CondDoMake;  
                     condExpr += 4;  
                     arglen = CondGetArg(&condExpr, &arg, "make", TRUE);  
                     if (arglen == 0) {  
                         condExpr -= 4;  
                         goto use_default;  
                     }  
                 } else if (istoken(condExpr, "exists", 6)) {  
                     /*  
                      * Use CondDoExists to evaluate the argument and  
                      * CondGetArg to extract the argument from the  
                      * 'function call'.  
                      */  
                     evalProc = CondDoExists;  
                     condExpr += 6;  
                     arglen = CondGetArg(&condExpr, &arg, "exists", TRUE);  
                     if (arglen == 0) {  
                         condExpr -= 6;  
                         goto use_default;  
                     }  
                 } else if (istoken(condExpr, "empty", 5)) {  
                     /*  
                      * Use Var_Parse to parse the spec in parens and return  
                      * True if the resulting string is empty.  
                      */  
                     int     length;  
                     Boolean doFree;  
                     char    *val;  
   
                     condExpr += 5;  
   
                     for (arglen = 0;  
                          condExpr[arglen] != '(' && condExpr[arglen] != '\0';  
                          arglen += 1)  
                         continue;  
   
                     if (condExpr[arglen] != '\0') {  
                         val = Var_Parse(&condExpr[arglen - 1], VAR_CMD,  
                                         FALSE, &length, &doFree);  
                         if (val == var_Error) {  
                             t = Err;  
                         } else {  
                             /*  
                              * A variable is empty when it just contains  
                              * spaces... 4/15/92, christos  
                              */  
                             char *p;  
                             for (p = val; *p && isspace((unsigned char)*p); p++)  
                                 continue;  
                             t = (*p == '\0') ? True : False;  
                         }  
                         if (doFree) {  
                             free(val);  
                         }  
                         /*  
                          * Advance condExpr to beyond the closing ). Note that  
                          * we subtract one from arglen + length b/c length  
                          * is calculated from condExpr[arglen - 1].  
                          */  
                         condExpr += arglen + length - 1;  
                     } else {  
                         condExpr -= 5;  
                         goto use_default;  
                     }  
                     break;  
                 } else if (istoken(condExpr, "target", 6)) {  
                     /*  
                      * Use CondDoTarget to evaluate the argument and  
                      * CondGetArg to extract the argument from the  
                      * 'function call'.  
                      */  
                     evalProc = CondDoTarget;  
                     condExpr += 6;  
                     arglen = CondGetArg(&condExpr, &arg, "target", TRUE);  
                     if (arglen == 0) {  
                         condExpr -= 6;  
                         goto use_default;  
                     }  
                 } else if (istoken(condExpr, "commands", 8)) {  
                     /*  
                      * Use CondDoCommands to evaluate the argument and  
                      * CondGetArg to extract the argument from the  
                      * 'function call'.  
                      */  
                     evalProc = CondDoCommands;  
                     condExpr += 8;  
                     arglen = CondGetArg(&condExpr, &arg, "commands", TRUE);  
                     if (arglen == 0) {  
                         condExpr -= 8;  
                         goto use_default;  
                     }  
                 } else {  
                     /*  
                      * The symbol is itself the argument to the default  
                      * function. We advance condExpr to the end of the symbol  
                      * by hand (the next whitespace, closing paren or  
                      * binary operator) and set to invert the evaluation  
                      * function if condInvert is TRUE.  
                      */  
                 use_default:  
                     invert = condInvert;  
                     evalProc = condDefProc;  
                     arglen = CondGetArg(&condExpr, &arg, "", FALSE);  
                 }  
   
                 /*          if (DEBUG(COND)) {
                  * Evaluate the argument using the set function. If invert              fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
                  * is TRUE, we invert the sense of the function.                     lhs, rhs, op);
                  */          }
                 t = (!doEval || (* evalProc) (arglen, arg) ?          /*
                      (invert ? False : True) :           * Null-terminate rhs and perform the comparison.
                      (invert ? True : False));           * t is set to the result.
                 free(arg);           */
                 break;          if (*op == '=') {
             }              t = strcmp(lhs, rhs) == 0;
           } else {
               t = strcmp(lhs, rhs) != 0;
         }          }
     } else {      } else {
         t = condPushBack;          /*
         condPushBack = None;           * rhs is either a float or an integer. Convert both the
            * lhs and the rhs to a double and compare the two.
            */
   
           if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right))
               goto do_string_compare;
   
           if (DEBUG(COND)) {
               fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left,
                      right, op);
           }
           switch(op[0]) {
           case '!':
               if (op[1] != '=') {
                   Parse_Error(PARSE_WARNING,
                               "Unknown operator");
                   goto done;
               }
               t = (left != right);
               break;
           case '=':
               if (op[1] != '=') {
                   Parse_Error(PARSE_WARNING,
                               "Unknown operator");
                   goto done;
               }
               t = (left == right);
               break;
           case '<':
               if (op[1] == '=') {
                   t = (left <= right);
               } else {
                   t = (left < right);
               }
               break;
           case '>':
               if (op[1] == '=') {
                   t = (left >= right);
               } else {
                   t = (left > right);
               }
               break;
           }
     }      }
     return (t);  
   done:
       free(lhsFree);
       free(rhsFree);
       return t;
 }  }
   
   static int
   get_mpt_arg(char **linePtr, char **argPtr, const char *func MAKE_ATTR_UNUSED)
   {
       /*
        * Use Var_Parse to parse the spec in parens and return
        * TOK_TRUE if the resulting string is empty.
        */
       int     length;
       void    *freeIt;
       char    *val;
       char    *cp = *linePtr;
   
       /* We do all the work here and return the result as the length */
       *argPtr = NULL;
   
       val = Var_Parse(cp - 1, VAR_CMD, VARF_WANTRES, &length, &freeIt);
       /*
        * Advance *linePtr to beyond the closing ). Note that
        * we subtract one because 'length' is calculated from 'cp - 1'.
        */
       *linePtr = cp - 1 + length;
   
       if (val == var_Error) {
           free(freeIt);
           return -1;
       }
   
       /* A variable is empty when it just contains spaces... 4/15/92, christos */
       while (isspace(*(unsigned char *)val))
           val++;
   
       /*
        * For consistency with the other functions we can't generate the
        * true/false here.
        */
       length = *val ? 2 : 1;
       free(freeIt);
       return length;
   }
   
   static Boolean
   CondDoEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED)
   {
       return arglen == 1;
   }
   
   static Token
   compare_function(Boolean doEval)
   {
       static const struct fn_def {
           const char  *fn_name;
           int         fn_name_len;
           int         (*fn_getarg)(char **, char **, const char *);
           Boolean     (*fn_proc)(int, const char *);
       } fn_defs[] = {
           { "defined",   7, CondGetArg, CondDoDefined },
           { "make",      4, CondGetArg, CondDoMake },
           { "exists",    6, CondGetArg, CondDoExists },
           { "empty",     5, get_mpt_arg, CondDoEmpty },
           { "target",    6, CondGetArg, CondDoTarget },
           { "commands",  8, CondGetArg, CondDoCommands },
           { NULL,        0, NULL, NULL },
       };
       const struct fn_def *fn_def;
       Token       t;
       char        *arg = NULL;
       int arglen;
       char *cp = condExpr;
       char *cp1;
   
       for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) {
           if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len))
               continue;
           cp += fn_def->fn_name_len;
           /* There can only be whitespace before the '(' */
           while (isspace(*(unsigned char *)cp))
               cp++;
           if (*cp != '(')
               break;
   
           arglen = fn_def->fn_getarg(&cp, &arg, fn_def->fn_name);
           if (arglen <= 0) {
               condExpr = cp;
               return arglen < 0 ? TOK_ERROR : TOK_FALSE;
           }
           /* Evaluate the argument using the required function. */
           t = !doEval || fn_def->fn_proc(arglen, arg);
           free(arg);
           condExpr = cp;
           return t;
       }
   
       /* Push anything numeric through the compare expression */
       cp = condExpr;
       if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0]))
           return compare_expression(doEval);
   
       /*
        * Most likely we have a naked token to apply the default function to.
        * However ".if a == b" gets here when the "a" is unquoted and doesn't
        * start with a '$'. This surprises people.
        * If what follows the function argument is a '=' or '!' then the syntax
        * would be invalid if we did "defined(a)" - so instead treat as an
        * expression.
        */
       arglen = CondGetArg(&cp, &arg, NULL);
       for (cp1 = cp; isspace(*(unsigned char *)cp1); cp1++)
           continue;
       if (*cp1 == '=' || *cp1 == '!')
           return compare_expression(doEval);
       condExpr = cp;
   
       /*
        * Evaluate the argument using the default function.
        * This path always treats .if as .ifdef. To get here the character
        * after .if must have been taken literally, so the argument cannot
        * be empty - even if it contained a variable expansion.
        */
       t = !doEval || if_info->defProc(arglen, arg) != if_info->doNot;
       free(arg);
       return t;
   }
   
   static Token
   CondToken(Boolean doEval)
   {
       Token t;
   
       t = condPushBack;
       if (t != TOK_NONE) {
           condPushBack = TOK_NONE;
           return t;
       }
   
       while (*condExpr == ' ' || *condExpr == '\t') {
           condExpr++;
       }
   
       switch (*condExpr) {
   
       case '(':
           condExpr++;
           return TOK_LPAREN;
   
       case ')':
           condExpr++;
           return TOK_RPAREN;
   
       case '|':
           if (condExpr[1] == '|') {
               condExpr++;
           }
           condExpr++;
           return TOK_OR;
   
       case '&':
           if (condExpr[1] == '&') {
               condExpr++;
           }
           condExpr++;
           return TOK_AND;
   
       case '!':
           condExpr++;
           return TOK_NOT;
   
       case '#':
       case '\n':
       case '\0':
           return TOK_EOF;
   
       case '"':
       case '$':
           return compare_expression(doEval);
   
       default:
           return compare_function(doEval);
       }
   }
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  * CondT --   * CondT --
  *      Parse a single term in the expression. This consists of a terminal   *      Parse a single term in the expression. This consists of a terminal
  *      symbol or Not and a terminal symbol (not including the binary   *      symbol or TOK_NOT and a terminal symbol (not including the binary
  *      operators):   *      operators):
  *          T -> defined(variable) | make(target) | exists(file) | symbol   *          T -> defined(variable) | make(target) | exists(file) | symbol
  *          T -> ! T | ( E )   *          T -> ! T | ( E )
  *   *
  * Results:   * Results:
  *      True, False or Err.   *      TOK_TRUE, TOK_FALSE or TOK_ERROR.
  *   *
  * Side Effects:   * Side Effects:
  *      Tokens are consumed.   *      Tokens are consumed.
Line 1013  CondT(Boolean doEval)
Line 1016  CondT(Boolean doEval)
   
     t = CondToken(doEval);      t = CondToken(doEval);
   
     if (t == EndOfFile) {      if (t == TOK_EOF) {
         /*          /*
          * 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;          t = TOK_ERROR;
     } else if (t == LParen) {      } else if (t == TOK_LPAREN) {
         /*          /*
          * T -> ( E )           * T -> ( E )
          */           */
         t = CondE(doEval);          t = CondE(doEval);
         if (t != Err) {          if (t != TOK_ERROR) {
             if (CondToken(doEval) != RParen) {              if (CondToken(doEval) != TOK_RPAREN) {
                 t = Err;                  t = TOK_ERROR;
             }              }
         }          }
     } else if (t == Not) {      } else if (t == TOK_NOT) {
         t = CondT(doEval);          t = CondT(doEval);
         if (t == True) {          if (t == TOK_TRUE) {
             t = False;              t = TOK_FALSE;
         } else if (t == False) {          } else if (t == TOK_FALSE) {
             t = True;              t = TOK_TRUE;
         }          }
     }      }
     return (t);      return (t);
Line 1047  CondT(Boolean doEval)
Line 1050  CondT(Boolean doEval)
  *          F -> T && F | T   *          F -> T && F | T
  *   *
  * Results:   * Results:
  *      True, False or Err   *      TOK_TRUE, TOK_FALSE or TOK_ERROR
  *   *
  * Side Effects:   * Side Effects:
  *      Tokens are consumed.   *      Tokens are consumed.
Line 1060  CondF(Boolean doEval)
Line 1063  CondF(Boolean doEval)
     Token   l, o;      Token   l, o;
   
     l = CondT(doEval);      l = CondT(doEval);
     if (l != Err) {      if (l != TOK_ERROR) {
         o = CondToken(doEval);          o = CondToken(doEval);
   
         if (o == And) {          if (o == TOK_AND) {
             /*              /*
              * F -> T && F               * F -> T && F
              *               *
              * If T is False, the whole thing will be False, but we have to               * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we have to
              * parse the r.h.s. anyway (to throw it away).               * parse the r.h.s. anyway (to throw it away).
              * If T is True, the result is the r.h.s., be it an Err or no.               * If T is TOK_TRUE, the result is the r.h.s., be it an TOK_ERROR or no.
              */               */
             if (l == True) {              if (l == TOK_TRUE) {
                 l = CondF(doEval);                  l = CondF(doEval);
             } else {              } else {
                 (void) CondF(FALSE);                  (void)CondF(FALSE);
             }              }
         } else {          } else {
             /*              /*
Line 1093  CondF(Boolean doEval)
Line 1096  CondF(Boolean doEval)
  *          E -> F || E | F   *          E -> F || E | F
  *   *
  * Results:   * Results:
  *      True, False or Err.   *      TOK_TRUE, TOK_FALSE or TOK_ERROR.
  *   *
  * Side Effects:   * Side Effects:
  *      Tokens are, of course, consumed.   *      Tokens are, of course, consumed.
Line 1106  CondE(Boolean doEval)
Line 1109  CondE(Boolean doEval)
     Token   l, o;      Token   l, o;
   
     l = CondF(doEval);      l = CondF(doEval);
     if (l != Err) {      if (l != TOK_ERROR) {
         o = CondToken(doEval);          o = CondToken(doEval);
   
         if (o == Or) {          if (o == TOK_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 make sure
              * the l.h.s. is False before we bother to evaluate the r.h.s.               * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s.
              * Once again, if l is False, the result is the r.h.s. and once               * Once again, if l is TOK_FALSE, the result is the r.h.s. and once
              * again if l is True, we parse the r.h.s. to throw it away.               * again if l is TOK_TRUE, we parse the r.h.s. to throw it away.
              */               */
             if (l == False) {              if (l == TOK_FALSE) {
                 l = CondE(doEval);                  l = CondE(doEval);
             } else {              } else {
                 (void) CondE(FALSE);                  (void)CondE(FALSE);
             }              }
         } else {          } else {
             /*              /*
Line 1152  CondE(Boolean doEval)
Line 1155  CondE(Boolean doEval)
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 int  int
 Cond_EvalExpression(int dosetup, char *line, Boolean *value, int eprint)  Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprint, Boolean strictLHS)
 {  {
     if (dosetup) {      static const struct If *dflt_info;
         condDefProc = CondDoDefined;      const struct If *sv_if_info = if_info;
         condInvert = 0;      char *sv_condExpr = condExpr;
     }      Token sv_condPushBack = condPushBack;
       int rval;
   
       lhsStrict = strictLHS;
   
     while (*line == ' ' || *line == '\t')      while (*line == ' ' || *line == '\t')
         line++;          line++;
   
       if (info == NULL && (info = dflt_info) == NULL) {
           /* Scan for the entry for .if - it can't be first */
           for (info = ifs; ; info++)
               if (info->form[0] == 0)
                   break;
           dflt_info = info;
       }
       assert(info != NULL);
   
       if_info = info;
     condExpr = line;      condExpr = line;
     condPushBack = None;      condPushBack = TOK_NONE;
   
       rval = do_Cond_EvalExpression(value);
   
       if (rval == COND_INVALID && eprint)
           Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line);
   
       if_info = sv_if_info;
       condExpr = sv_condExpr;
       condPushBack = sv_condPushBack;
   
       return rval;
   }
   
   static int
   do_Cond_EvalExpression(Boolean *value)
   {
   
     switch (CondE(TRUE)) {      switch (CondE(TRUE)) {
     case True:      case TOK_TRUE:
         if (CondToken(TRUE) == EndOfFile) {          if (CondToken(TRUE) == TOK_EOF) {
             *value = TRUE;              *value = TRUE;
             break;              return COND_PARSE;
         }          }
         goto err;          break;
         /*FALLTHRU*/      case TOK_FALSE:
     case False:          if (CondToken(TRUE) == TOK_EOF) {
         if (CondToken(TRUE) == EndOfFile) {  
             *value = FALSE;              *value = FALSE;
             break;              return COND_PARSE;
         }          }
         /*FALLTHRU*/          break;
     case Err:  
 err:  
         if (eprint)  
             Parse_Error(PARSE_FATAL, "Malformed conditional (%s)",  
                          line);  
         return (COND_INVALID);  
     default:      default:
       case TOK_ERROR:
         break;          break;
     }      }
   
     return COND_PARSE;      return COND_INVALID;
 }  }
   
   
Line 1198  err:
Line 1224  err:
  * Cond_Eval --   * Cond_Eval --
  *      Evaluate the conditional in the passed line. The line   *      Evaluate the conditional in the passed line. The line
  *      looks like this:   *      looks like this:
  *          #<cond-type> <expr>   *          .<cond-type> <expr>
  *      where <cond-type> is any of if, ifmake, ifnmake, ifdef,   *      where <cond-type> is any of if, ifmake, ifnmake, ifdef,
  *      ifndef, elif, elifmake, elifnmake, elifdef, elifndef   *      ifndef, elif, elifmake, elifnmake, elifdef, elifndef
  *      and <expr> consists of &&, ||, !, make(target), defined(variable)   *      and <expr> consists of &&, ||, !, make(target), defined(variable)
Line 1215  err:
Line 1241  err:
  * Side Effects:   * Side Effects:
  *      None.   *      None.
  *   *
    * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order
    * to detect splurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF)
    * otherwise .else could be treated as '.elif 1'.
    *
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 int  int
 Cond_Eval(char *line)  Cond_Eval(char *line)
 {  {
     struct If       *ifp;  #define     MAXIF      128      /* maximum depth of .if'ing */
     Boolean         isElse;  #define     MAXIF_BUMP  32      /* how much to grow by */
     Boolean         value = FALSE;      enum if_states {
           IF_ACTIVE,              /* .if or .elif part active */
           ELSE_ACTIVE,            /* .else part active */
           SEARCH_FOR_ELIF,        /* searching for .elif/else to execute */
           SKIP_TO_ELSE,           /* has been true, but not seen '.else' */
           SKIP_TO_ENDIF           /* nothing else to execute */
       };
       static enum if_states *cond_state = NULL;
       static unsigned int max_if_depth = MAXIF;
   
       const struct If *ifp;
       Boolean         isElif;
       Boolean         value;
     int             level;      /* Level at which to report errors. */      int             level;      /* Level at which to report errors. */
       enum if_states  state;
   
     level = PARSE_FATAL;      level = PARSE_FATAL;
       if (!cond_state) {
     for (line++; *line == ' ' || *line == '\t'; line++) {          cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state));
         continue;          cond_state[0] = IF_ACTIVE;
     }      }
       /* skip leading character (the '.') and any whitespace */
       for (line++; *line == ' ' || *line == '\t'; line++)
           continue;
   
     /*      /* Find what type of if we're dealing with.  */
      * Find what type of if we're dealing with. The result is left      if (line[0] == 'e') {
      * in ifp and isElse is set TRUE if it's an elif line.          if (line[1] != 'l') {
      */              if (!istoken(line + 1, "ndif", 4))
     if (line[0] == 'e' && line[1] == 'l') {                  return COND_INVALID;
         line += 2;              /* End of conditional section */
         isElse = TRUE;              if (cond_depth == cond_min_depth) {
     } else if (istoken(line, "endif", 5)) {  
         /*  
          * 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.  
          */  
         finalElse[condTop][skipIfLevel] = FALSE;  
         if (skipIfLevel != 0) {  
             skipIfLevel -= 1;  
             return (COND_SKIP);  
         } else {  
             if (condTop == MAXIF) {  
                 Parse_Error(level, "if-less endif");                  Parse_Error(level, "if-less endif");
                 return (COND_INVALID);                  return COND_PARSE;
             } else {  
                 skipLine = FALSE;  
                 condTop += 1;  
                 return (COND_PARSE);  
             }              }
               /* Return state for previous conditional */
               cond_depth--;
               return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
         }          }
     } else {  
         isElse = FALSE;          /* Quite likely this is 'else' or 'elif' */
     }          line += 2;
           if (istoken(line, "se", 2)) {
               /* It is else... */
               if (cond_depth == cond_min_depth) {
                   Parse_Error(level, "if-less else");
                   return COND_PARSE;
               }
   
               state = cond_state[cond_depth];
               switch (state) {
               case SEARCH_FOR_ELIF:
                   state = ELSE_ACTIVE;
                   break;
               case ELSE_ACTIVE:
               case SKIP_TO_ENDIF:
                   Parse_Error(PARSE_WARNING, "extra else");
                   /* FALLTHROUGH */
               default:
               case IF_ACTIVE:
               case SKIP_TO_ELSE:
                   state = SKIP_TO_ENDIF;
                   break;
               }
               cond_state[cond_depth] = state;
               return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
           }
           /* Assume for now it is an elif */
           isElif = TRUE;
       } else
           isElif = FALSE;
   
       if (line[0] != 'i' || line[1] != 'f')
           /* Not an ifxxx or elifxxx line */
           return COND_INVALID;
   
     /*      /*
      * Figure out what sort of conditional it is -- what its default       * Figure out what sort of conditional it is -- what its default
      * function is, etc. -- by looking in the table of valid "ifs"       * function is, etc. -- by looking in the table of valid "ifs"
      */       */
     for (ifp = ifs; ifp->form != (char *)0; ifp++) {      line += 2;
       for (ifp = ifs; ; ifp++) {
           if (ifp->form == NULL)
               return COND_INVALID;
         if (istoken(ifp->form, line, ifp->formlen)) {          if (istoken(ifp->form, line, ifp->formlen)) {
               line += ifp->formlen;
             break;              break;
         }          }
     }      }
   
     if (ifp->form == (char *) 0) {      /* Now we know what sort of 'if' it is... */
         /*  
          * Nothing fit. If the first word on the line is actually      if (isElif) {
          * "else", it's a valid conditional whose value is the inverse          if (cond_depth == cond_min_depth) {
          * of the previous if we parsed.              Parse_Error(level, "if-less elif");
          */              return COND_PARSE;
         if (isElse && istoken(line, "se", 2)) {          }
             if (finalElse[condTop][skipIfLevel]) {          state = cond_state[cond_depth];
                 Parse_Error(PARSE_WARNING, "extra else");          if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) {
             } else {              Parse_Error(PARSE_WARNING, "extra elif");
                 finalElse[condTop][skipIfLevel] = TRUE;              cond_state[cond_depth] = SKIP_TO_ENDIF;
             }              return COND_SKIP;
             if (condTop == MAXIF) {          }
                 Parse_Error(level, "if-less else");          if (state != SEARCH_FOR_ELIF) {
                 return (COND_INVALID);              /* Either just finished the 'true' block, or already SKIP_TO_ELSE */
             } else if (skipIfLevel == 0) {              cond_state[cond_depth] = SKIP_TO_ELSE;
                 value = !condStack[condTop];              return COND_SKIP;
             } else {  
                 return (COND_SKIP);  
             }  
         } else {  
             /*  
              * Not a valid conditional type. No error...  
              */  
             return (COND_INVALID);  
         }          }
     } else {      } else {
         if (isElse) {          /* Normal .if */
             if (condTop == MAXIF) {          if (cond_depth + 1 >= max_if_depth) {
                 Parse_Error(level, "if-less elif");  
                 return (COND_INVALID);  
             } else if (skipIfLevel != 0) {  
                 /*  
                  * If skipping this conditional, just ignore the whole thing.  
                  * If we don't, the user might be employing a variable that's  
                  * undefined, for which there's an enclosing ifdef that  
                  * we're skipping...  
                  */  
                 return(COND_SKIP);  
             }  
         } else if (skipLine) {  
             /*              /*
              * Don't even try to evaluate a conditional that's not an else if               * This is rare, but not impossible.
              * we're skipping things...               * In meta mode, dirdeps.mk (only runs at level 0)
                * can need more than the default.
              */               */
             skipIfLevel += 1;              max_if_depth += MAXIF_BUMP;
             if (skipIfLevel >= MAXIF) {              cond_state = bmake_realloc(cond_state, max_if_depth *
                 Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF);                  sizeof(*cond_state));
                 return (COND_INVALID);          }
             }          state = cond_state[cond_depth];
             finalElse[condTop][skipIfLevel] = FALSE;          cond_depth++;
             return(COND_SKIP);          if (state > ELSE_ACTIVE) {
               /* If we aren't parsing the data, treat as always false */
               cond_state[cond_depth] = SKIP_TO_ELSE;
               return COND_SKIP;
         }          }
   
         /*  
          * Initialize file-global variables for parsing  
          */  
         condDefProc = ifp->defProc;  
         condInvert = ifp->doNot;  
   
         line += ifp->formlen;  
         if (Cond_EvalExpression(0, line, &value, 1) == COND_INVALID)  
                 return COND_INVALID;  
     }      }
     if (!isElse) {  
         condTop -= 1;      /* And evaluate the conditional expresssion */
         finalElse[condTop][skipIfLevel] = FALSE;      if (Cond_EvalExpression(ifp, line, &value, 1, TRUE) == COND_INVALID) {
     } else if ((skipIfLevel != 0) || condStack[condTop]) {          /* Syntax error in conditional, error message already output. */
         /*          /* Skip everything to matching .endif */
          * If this is an else-type conditional, it should only take effect          cond_state[cond_depth] = SKIP_TO_ELSE;
          * if its corresponding if was evaluated and FALSE. If its if was          return COND_SKIP;
          * TRUE or skipped, we return COND_SKIP (and start skipping in case  
          * we weren't already), leaving the stack unmolested so later elif's  
          * don't screw up...  
          */  
         skipLine = TRUE;  
         return (COND_SKIP);  
     }      }
   
     if (condTop < 0) {      if (!value) {
         /*          cond_state[cond_depth] = SEARCH_FOR_ELIF;
          * This is the one case where we can definitely proclaim a fatal          return COND_SKIP;
          * error. If we don't, we're hosed.  
          */  
         Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF);  
         return (COND_INVALID);  
     } else {  
         condStack[condTop] = value;  
         skipLine = !value;  
         return (value ? COND_PARSE : COND_SKIP);  
     }      }
       cond_state[cond_depth] = IF_ACTIVE;
       return COND_PARSE;
 }  }
   
   
Line 1384  Cond_Eval(char *line)
Line 1413  Cond_Eval(char *line)
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 void  void
 Cond_End(void)  Cond_restore_depth(unsigned int saved_depth)
 {  {
     if (condTop != MAXIF) {      int open_conds = cond_depth - cond_min_depth;
         Parse_Error(PARSE_FATAL, "%d open conditional%s", MAXIF-condTop,  
                     MAXIF-condTop == 1 ? "" : "s");      if (open_conds != 0 || saved_depth > cond_depth) {
           Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds,
                       open_conds == 1 ? "" : "s");
           cond_depth = cond_min_depth;
     }      }
     condTop = MAXIF;  
       cond_min_depth = saved_depth;
   }
   
   unsigned int
   Cond_save_depth(void)
   {
       int depth = cond_min_depth;
   
       cond_min_depth = cond_depth;
       return depth;
 }  }

Legend:
Removed from v.1.26  
changed lines
  Added in v.1.75

CVSweb <webmaster@jp.NetBSD.org>