[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.19 and 1.68

version 1.19, 2004/01/06 01:18:52 version 1.68, 2015/05/05 21:51:09
Line 69 
Line 69 
  * SUCH DAMAGE.   * SUCH DAMAGE.
  */   */
   
 #ifdef MAKE_BOOTSTRAP  #ifndef MAKE_NATIVE
 static char rcsid[] = "$NetBSD$";  static char rcsid[] = "$NetBSD$";
 #else  #else
 #include <sys/cdefs.h>  #include <sys/cdefs.h>
Line 92  __RCSID("$NetBSD$");
Line 92  __RCSID("$NetBSD$");
  */   */
   
 #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 114  __RCSID("$NetBSD$");
Line 115  __RCSID("$NetBSD$");
  *      T -> $(varspec) op value   *      T -> $(varspec) op value
  *      T -> $(varspec) == "string"   *      T -> $(varspec) == "string"
  *      T -> $(varspec) != "string"   *      T -> $(varspec) != "string"
    *      T -> "string"
  *      T -> ( E )   *      T -> ( E )
  *      T -> ! T   *      T -> ! T
  *      op -> == | != | > | < | >= | <=   *      op -> == | != | > | < | >= | <=
Line 122  __RCSID("$NetBSD$");
Line 124  __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 138  typedef enum {
Line 145  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           30        /* 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    condStack[MAXIF];     /* Stack of conditionals's values */  /*
 static int        condTop = MAXIF;      /* Top-most conditional */   * Indicate when we should be strict about lhs of comparisons.
 static int        skipIfLevel=0;        /* Depth of skipped conditionals */   * TRUE when Cond_EvalExpression is called from Cond_Eval (.if etc)
 static Boolean    skipLine = FALSE;     /* Whether the parse module is skipping   * FALSE when Cond_EvalExpression is called from var.c:ApplyModifiers
                                          * lines */   * since lhs is already expanded and we cannot tell if
    * it was a variable reference or not.
    */
   static Boolean lhsStrict;
   
   static int
   istoken(const char *str, const char *tok, size_t len)
   {
           return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]);
   }
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
Line 220  CondPushBack(Token t)
Line 237  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 243  CondGetArg(char **linePtr, char **argPtr
Line 257  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 255  CondGetArg(char **linePtr, char **argPtr
Line 269  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 267  CondGetArg(char **linePtr, char **argPtr
Line 287  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, TRUE, &len, &freeIt);
             if (doFree) {              Buf_AddBytes(&buf, strlen(cp2), cp2);
                 free(cp2);              if (freeIt)
             }                  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 != ')') {  
         Parse_Error (PARSE_WARNING, "Missing closing parenthesis for %s()",      if (func != NULL && *cp++ != ')') {
           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 318  CondGetArg(char **linePtr, char **argPtr
Line 336  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)      if (p1)
         free(p1);          free(p1);
     arg[argLen] = savec;  
     return (result);      return (result);
 }  }
   
Line 351  CondDoDefined(int argLen, char *arg)
Line 366  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 370  CondStrMatch(ClientData string, ClientDa
Line 385  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 399  CondDoMake(int argLen, char *arg)
Line 404  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 431  CondDoExists(int argLen, char *arg)
Line 437  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 464  CondDoTarget(int argLen, char *arg)
Line 461  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.   *
    *-----------------------------------------------------------------------
    */
   static Boolean
   CondCvtArg(char *str, double *value)
   {
       char *eptr, ech;
       unsigned long l_val;
       double d_val;
   
       errno = 0;
       l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10);
       ech = *eptr;
       if (ech == 0 && errno != ERANGE) {
           d_val = str[0] == '-' ? -(double)-l_val : (double)l_val;
       } else {
           if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E')
               return FALSE;
           d_val = strtod(str, &eptr);
           if (*eptr)
               return FALSE;
       }
   
       *value = d_val;
       return TRUE;
   }
   
   /*-
    *-----------------------------------------------------------------------
    * CondGetString --
    *      Get a string from a variable reference or an optionally quoted
    *      string.  This is called for the lhs and rhs of string compares.
    *
    * Results:
    *      Sets freeIt if needed,
    *      Sets quoted if string was quoted,
    *      Returns NULL on error,
    *      else returns string - absent any quotes.
  *   *
  * Side Effects:   * Side Effects:
  *      Can change 'value' even if string is not a valid number.   *      Moves condExpr to end of this token.
  *   *
  *   *
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
   /* coverity:[+alloc : arg-*2] */
 static char *  static char *
 CondCvtArg(char *str, double *value)  CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
 {  {
     if ((*str == '0') && (str[1] == 'x')) {      Buffer buf;
         long i;      char *cp;
       char *str;
         for (str += 2, i = 0; *str; str++) {      int len;
             int x;      int qt;
             if (isdigit((unsigned char) *str))      char *start;
                 x  = *str - '0';  
             else if (isxdigit((unsigned char) *str))      Buf_Init(&buf, 0);
                 x = 10 + *str - isupper((unsigned char) *str) ? 'A' : 'a';      str = NULL;
       *freeIt = NULL;
       *quoted = qt = *condExpr == '"' ? 1 : 0;
       if (qt)
           condExpr++;
       for (start = condExpr; *condExpr && str == NULL; condExpr++) {
           switch (*condExpr) {
           case '\\':
               if (condExpr[1] != '\0') {
                   condExpr++;
                   Buf_AddByte(&buf, *condExpr);
               }
               break;
           case '"':
               if (qt) {
                   condExpr++;             /* we don't want the quotes */
                   goto got_str;
               } else
                   Buf_AddByte(&buf, *condExpr); /* likely? */
               break;
           case ')':
           case '!':
           case '=':
           case '>':
           case '<':
           case ' ':
           case '\t':
               if (!qt)
                   goto got_str;
             else              else
                 break;                  Buf_AddByte(&buf, *condExpr);
             i = (i << 4) + x;              break;
           case '$':
               /* if we are in quotes, then an undefined variable is ok */
               str = Var_Parse(condExpr, VAR_CMD, (qt ? 0 : doEval),
                               &len, freeIt);
               if (str == var_Error) {
                   if (*freeIt) {
                       free(*freeIt);
                       *freeIt = NULL;
                   }
                   /*
                    * Even if !doEval, we still report syntax errors, which
                    * is what getting var_Error back with !doEval means.
                    */
                   str = NULL;
                   goto cleanup;
               }
               condExpr += len;
               /*
                * If the '$' was first char (no quotes), and we are
                * followed by space, the operator or end of expression,
                * we are done.
                */
               if ((condExpr == start + len) &&
                   (*condExpr == '\0' ||
                    isspace((unsigned char) *condExpr) ||
                    strchr("!=><)", *condExpr))) {
                   goto cleanup;
               }
               /*
                * Nope, we better copy str to buf
                */
               for (cp = str; *cp; cp++) {
                   Buf_AddByte(&buf, *cp);
               }
               if (*freeIt) {
                   free(*freeIt);
                   *freeIt = NULL;
               }
               str = NULL;                 /* not finished yet */
               condExpr--;                 /* don't skip over next char */
               break;
           default:
               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;
         }          }
         *value = (double) i;  
         return *str ? str : NULL;  
     } else {  
         char *eptr;  
         *value = strtod(str, &eptr);  
         return *eptr ? eptr : NULL;  
     }      }
    got_str:
       str = Buf_GetAll(&buf, NULL);
       *freeIt = str;
    cleanup:
       Buf_Destroy(&buf, FALSE);
       return str;
 }  }
   
 /*-  /*-
Line 534  CondCvtArg(char *str, double *value)
Line 641  CondCvtArg(char *str, double *value)
  *      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))
           condExpr++;
   
       /*
        * Make sure the operator is a valid one. If it isn't a
        * known relational operator, pretend we got a
        * != 0 comparison.
        */
       op = condExpr;
       switch (*condExpr) {
           case '!':
           case '=':
           case '<':
           case '>':
               if (condExpr[1] == '=') {
                   condExpr += 2;
               } else {
                   condExpr += 1;
               }
               break;
           default:
               if (!doEval) {
                   t = TOK_FALSE;
                   goto done;
               }
               /* 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;
       }
   
       while (isspace((unsigned char)*condExpr))
           condExpr++;
   
       if (*condExpr == '\0') {
           Parse_Error(PARSE_WARNING,
                       "Missing right-hand-side of operator");
           goto done;
       }
   
       rhs = CondGetString(doEval, &rhsQuoted, &rhsFree, FALSE);
       if (!rhs)
           goto done;
   
       if (rhsQuoted || lhsQuoted) {
   do_string_compare:
           if (((*op != '!') && (*op != '=')) || (op[1] != '=')) {
               Parse_Error(PARSE_WARNING,
       "String comparison operator should be either == or !=");
               goto done;
         }          }
         switch (*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 '$': {  
                 char    *lhs;  
                 char    *rhs;  
                 char    *op;  
                 int     varSpecLen;  
                 Boolean doFree;  
   
                 /*          if (DEBUG(COND)) {
                  * Parse the variable spec and skip over it, saving its              fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
                  * value in lhs.                     lhs, rhs, op);
                  */          }
                 t = Err;          /*
                 lhs = Var_Parse(condExpr, VAR_CMD, doEval,&varSpecLen,&doFree);           * Null-terminate rhs and perform the comparison.
                 if (lhs == var_Error) {           * t is set to the result.
                     /*           */
                      * Even if !doEval, we still report syntax errors, which          if (*op == '=') {
                      * is what getting var_Error back with !doEval means.              t = strcmp(lhs, rhs) == 0;
                      */          } else {
                     return(Err);              t = strcmp(lhs, rhs) != 0;
                 }          }
                 condExpr += varSpecLen;      } else {
           /*
            * 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;
           }
       }
   
                 if (!isspace((unsigned char) *condExpr) &&  done:
                     strchr("!=><", *condExpr) == NULL) {      if (lhsFree)
                     Buffer buf;          free(lhsFree);
                     char *cp;      if (rhsFree)
           free(rhsFree);
       return t;
   }
   
                     buf = Buf_Init(0);  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;
   
                     for (cp = lhs; *cp; cp++)      /* We do all the work here and return the result as the length */
                         Buf_AddByte(buf, (Byte)*cp);      *argPtr = NULL;
   
                     if (doFree)      val = Var_Parse(cp - 1, VAR_CMD, FALSE, &length, &freeIt);
                         free(lhs);      /*
        * Advance *linePtr to beyond the closing ). Note that
        * we subtract one because 'length' is calculated from 'cp - 1'.
        */
       *linePtr = cp - 1 + length;
   
                     for (;*condExpr && !isspace((unsigned char) *condExpr);      if (val == var_Error) {
                          condExpr++)          free(freeIt);
                         Buf_AddByte(buf, (Byte)*condExpr);          return -1;
       }
   
                     Buf_AddByte(buf, (Byte)'\0');      /* A variable is empty when it just contains spaces... 4/15/92, christos */
                     lhs = (char *)Buf_GetAll(buf, &varSpecLen);      while (isspace(*(unsigned char *)val))
                     Buf_Destroy(buf, FALSE);          val++;
   
                     doFree = TRUE;      /*
                 }       * For consistency with the other functions we can't generate the
        * true/false here.
        */
       length = *val ? 2 : 1;
       if (freeIt)
           free(freeIt);
       return length;
   }
   
                 /*  static Boolean
                  * Skip whitespace to get to the operator  CondDoEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED)
                  */  {
                 while (isspace((unsigned char) *condExpr))      return arglen == 1;
                     condExpr++;  }
   
                 /*  static Token
                  * Make sure the operator is a valid one. If it isn't a  compare_function(Boolean doEval)
                  * known relational operator, pretend we got a  {
                  * != 0 comparison.      static const struct fn_def {
                  */          const char  *fn_name;
                 op = condExpr;          int         fn_name_len;
                 switch (*condExpr) {          int         (*fn_getarg)(char **, char **, const char *);
                     case '!':          Boolean     (*fn_proc)(int, const char *);
                     case '=':      } fn_defs[] = {
                     case '<':          { "defined",   7, CondGetArg, CondDoDefined },
                     case '>':          { "make",      4, CondGetArg, CondDoMake },
                         if (condExpr[1] == '=') {          { "exists",    6, CondGetArg, CondDoExists },
                             condExpr += 2;          { "empty",     5, get_mpt_arg, CondDoEmpty },
                         } else {          { "target",    6, CondGetArg, CondDoTarget },
                             condExpr += 1;          { "commands",  8, CondGetArg, CondDoCommands },
                         }          { NULL,        0, NULL, NULL },
                         break;      };
                     default:      const struct fn_def *fn_def;
                         op = UNCONST("!=");      Token       t;
                         rhs = UNCONST("0");      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;
   
                         goto do_compare;          arglen = fn_def->fn_getarg(&cp, &arg, fn_def->fn_name);
                 }          if (arglen <= 0) {
                 while (isspace((unsigned char) *condExpr)) {              condExpr = cp;
                     condExpr++;              return arglen < 0 ? TOK_ERROR : TOK_FALSE;
                 }          }
                 if (*condExpr == '\0') {          /* Evaluate the argument using the required function. */
                     Parse_Error(PARSE_WARNING,          t = !doEval || fn_def->fn_proc(arglen, arg);
                                 "Missing right-hand-side of operator");          if (arg)
                     goto error;              free(arg);
                 }          condExpr = cp;
                 rhs = condExpr;          return t;
 do_compare:      }
                 if (*rhs == '"') {  
                     /*  
                      * Doing a string comparison. Only allow == and != for  
                      * operators.  
                      */  
                     char    *string;  
                     char    *cp, *cp2;  
                     int     qt;  
                     Buffer  buf;  
   
 do_string_compare:      /* Push anything numeric through the compare expression */
                     if (((*op != '!') && (*op != '=')) || (op[1] != '=')) {      cp = condExpr;
                         Parse_Error(PARSE_WARNING,      if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0]))
                 "String comparison operator should be either == or !=");          return compare_expression(doEval);
                         goto error;  
                     }  
   
                     buf = Buf_Init(0);  
                     qt = *rhs == '"' ? 1 : 0;  
   
                     for (cp = &rhs[qt];  
                          ((qt && (*cp != '"')) ||  
                           (!qt && strchr(" \t)", *cp) == NULL)) &&  
                          (*cp != '\0'); cp++) {  
                         if ((*cp == '\\') && (cp[1] != '\0')) {  
                             /*  
                              * Backslash escapes things -- skip over next  
                              * character, if it exists.  
                              */  
                             cp++;  
                             Buf_AddByte(buf, (Byte)*cp);  
                         } else if (*cp == '$') {  
                             int len;  
                             Boolean freeIt;  
   
                             cp2 = Var_Parse(cp, VAR_CMD, doEval,&len, &freeIt);  
                             if (cp2 != var_Error) {  
                                 Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);  
                                 if (freeIt) {  
                                     free(cp2);  
                                 }  
                                 cp += len - 1;  
                             } else {  
                                 Buf_AddByte(buf, (Byte)*cp);  
                             }  
                         } else {  
                             Buf_AddByte(buf, (Byte)*cp);  
                         }  
                     }  
   
                     Buf_AddByte(buf, (Byte)0);  
   
                     string = (char *)Buf_GetAll(buf, (int *)0);  
                     Buf_Destroy(buf, FALSE);  
   
                     if (DEBUG(COND)) {  
                         printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n",  
                                lhs, string, op);  
                     }  
                     /*  
                      * Null-terminate rhs and perform the comparison.  
                      * t is set to the result.  
                      */  
                     if (*op == '=') {  
                         t = strcmp(lhs, string) ? False : True;  
                     } else {  
                         t = strcmp(lhs, string) ? True : False;  
                     }  
                     free(string);  
                     if (rhs == condExpr) {  
                         if (!qt && *cp == ')')  
                             condExpr = cp;  
                         else  
                             condExpr = cp + 1;  
                     }  
                 } 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        *string;  
   
                     if (CondCvtArg(lhs, &left))  
                         goto do_string_compare;  
                     if (*rhs == '$') {  
                         int     len;  
                         Boolean freeIt;  
   
                         string = Var_Parse(rhs, VAR_CMD, doEval,&len,&freeIt);  
                         if (string == var_Error) {  
                             right = 0.0;  
                         } else {  
                             if (CondCvtArg(string, &right)) {  
                                 if (freeIt)  
                                     free(string);  
                                 goto do_string_compare;  
                             }  
                             if (freeIt)  
                                 free(string);  
                             if (rhs == condExpr)  
                                 condExpr += len;  
                         }  
                     } else {  
                         char *cp;  
   
                         if ((cp = CondCvtArg(rhs, &right)) &&  
                             cp == rhs)  
                             goto do_string_compare;  
                         if (rhs == condExpr) {  
                             /*  
                              * Skip over the right-hand side  
                              */  
                             if (cp)  
                                 condExpr = cp;  
                             else  
                                 condExpr = strchr(rhs, '\0');  
                         }  
                     }  
   
                     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 (doFree)  
                     free(lhs);  
                 break;  
             }  
             default: {  
                 Boolean (*evalProc)(int, char *);  
                 Boolean invert = FALSE;  
                 char    *arg;  
                 int     arglen;  
   
                 if (strncmp (condExpr, "defined", 7) == 0) {  
                     /*  
                      * 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 (strncmp (condExpr, "make", 4) == 0) {  
                     /*  
                      * 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 (strncmp (condExpr, "exists", 6) == 0) {  
                     /*  
                      * 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 (strncmp(condExpr, "empty", 5) == 0) {  
                     /*  
                      * 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 (strncmp (condExpr, "target", 6) == 0) {  
                     /*  
                      * 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 (strncmp (condExpr, "commands", 8) == 0) {  
                     /*  
                      * 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);  
                 }  
   
                 /*      /*
                  * Evaluate the argument using the set function. If invert       * Most likely we have a naked token to apply the default function to.
                  * is TRUE, we invert the sense of the function.       * However ".if a == b" gets here when the "a" is unquoted and doesn't
                  */       * start with a '$'. This surprises people.
                 t = (!doEval || (* evalProc) (arglen, arg) ?       * If what follows the function argument is a '=' or '!' then the syntax
                      (invert ? False : True) :       * would be invalid if we did "defined(a)" - so instead treat as an
                      (invert ? True : False));       * expression.
                 free(arg);       */
                 break;      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;
       if (arg)
           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++;
         }          }
     } else {          condExpr++;
         t = condPushBack;          return TOK_OR;
         condPushBack = None;  
       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);
     }      }
     return (t);  
 }  }
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  * 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 998  CondT(Boolean doEval)
Line 1015  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 1032  CondT(Boolean doEval)
Line 1049  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 1045  CondF(Boolean doEval)
Line 1062  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 {
             /*              /*
              * F -> T               * F -> T
              */               */
             CondPushBack (o);              CondPushBack(o);
         }          }
     }      }
     return (l);      return (l);
Line 1078  CondF(Boolean doEval)
Line 1095  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 1091  CondE(Boolean doEval)
Line 1108  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 {
             /*              /*
              * E -> F               * E -> F
              */               */
             CondPushBack (o);              CondPushBack(o);
         }          }
     }      }
     return (l);      return (l);
Line 1137  CondE(Boolean doEval)
Line 1154  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;
       }
   
       if_info = info != NULL ? info : ifs + 4;
     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 1183  err:
Line 1222  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 1200  err:
Line 1239  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;
               /* End of conditional section */
               if (cond_depth == cond_min_depth) {
                   Parse_Error(level, "if-less endif");
                   return COND_PARSE;
               }
               /* Return state for previous conditional */
               cond_depth--;
               return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
           }
   
           /* Quite likely this is 'else' or 'elif' */
         line += 2;          line += 2;
         isElse = TRUE;          if (istoken(line, "se", 2)) {
     } else if (strncmp (line, "endif", 5) == 0) {              /* It is else... */
         /*              if (cond_depth == cond_min_depth) {
          * End of a conditional section. If skipIfLevel is non-zero, that                  Parse_Error(level, "if-less else");
          * conditional was skipped, so lines following it should also be                  return COND_PARSE;
          * 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              state = cond_state[cond_depth];
          * a decent if.              switch (state) {
          */              case SEARCH_FOR_ELIF:
         if (skipIfLevel != 0) {                  state = ELSE_ACTIVE;
             skipIfLevel -= 1;                  break;
             return (COND_SKIP);              case ELSE_ACTIVE:
         } else {              case SKIP_TO_ENDIF:
             if (condTop == MAXIF) {                  Parse_Error(PARSE_WARNING, "extra else");
                 Parse_Error (level, "if-less endif");                  /* FALLTHROUGH */
                 return (COND_INVALID);              default:
             } else {              case IF_ACTIVE:
                 skipLine = FALSE;              case SKIP_TO_ELSE:
                 condTop += 1;                  state = SKIP_TO_ENDIF;
                 return (COND_PARSE);                  break;
             }              }
               cond_state[cond_depth] = state;
               return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
         }          }
     } else {          /* Assume for now it is an elif */
         isElse = FALSE;          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;
         if (strncmp (ifp->form, line, ifp->formlen) == 0) {      for (ifp = ifs; ; ifp++) {
           if (ifp->form == NULL)
               return COND_INVALID;
           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 && (line[0] == 's') && (line[1] == 'e')) {          }
             if (condTop == MAXIF) {          state = cond_state[cond_depth];
                 Parse_Error (level, "if-less else");          if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) {
                 return (COND_INVALID);              Parse_Error(PARSE_WARNING, "extra elif");
             } else if (skipIfLevel == 0) {              cond_state[cond_depth] = SKIP_TO_ENDIF;
                 value = !condStack[condTop];              return COND_SKIP;
             } else {          }
                 return (COND_SKIP);          if (state != SEARCH_FOR_ELIF) {
             }              /* Either just finished the 'true' block, or already SKIP_TO_ELSE */
         } else {              cond_state[cond_depth] = SKIP_TO_ELSE;
             /*              return COND_SKIP;
              * 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;
             return(COND_SKIP);              cond_state = bmake_realloc(cond_state, max_if_depth *
                   sizeof(*cond_state));
           }
           state = cond_state[cond_depth];
           cond_depth++;
           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 */
     } else if ((skipIfLevel != 0) || condStack[condTop]) {      if (Cond_EvalExpression(ifp, line, &value, 1, TRUE) == COND_INVALID) {
         /*          /* Syntax error in conditional, error message already output. */
          * If this is an else-type conditional, it should only take effect          /* Skip everything to matching .endif */
          * if its corresponding if was evaluated and FALSE. If its if was          cond_state[cond_depth] = SKIP_TO_ELSE;
          * TRUE or skipped, we return COND_SKIP (and start skipping in case          return COND_SKIP;
          * 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 1357  Cond_Eval(char *line)
Line 1411  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.19  
changed lines
  Added in v.1.68

CVSweb <webmaster@jp.NetBSD.org>