[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.47 and 1.69

version 1.47, 2008/11/29 14:12:48 version 1.69, 2015/10/11 04:51:24
Line 124  __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 140  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 Boolean 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 const 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[] = {
     { "def",      3,      FALSE,  CondDoDefined },      { "def",      3,      FALSE,  CondDoDefined },
     { "ndef",     4,      TRUE,   CondDoDefined },      { "ndef",     4,      TRUE,   CondDoDefined },
Line 167  static const struct If {
Line 173  static const struct If {
     { 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 */
   
 static unsigned int     cond_depth = 0;         /* current .if nesting level */  static unsigned int     cond_depth = 0;         /* current .if nesting level */
 static unsigned int     cond_min_depth = 0;     /* depth at makefile open */  static unsigned int     cond_min_depth = 0;     /* depth at makefile open */
   
   /*
    * Indicate when we should be strict about lhs of comparisons.
    * TRUE when Cond_EvalExpression is called from Cond_Eval (.if etc)
    * FALSE when Cond_EvalExpression is called from var.c:ApplyModifiers
    * since lhs is already expanded and we cannot tell if
    * 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 223  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 258  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) == 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 272  CondGetArg(char **linePtr, char **argPtr
Line 289  CondGetArg(char **linePtr, char **argPtr
             int         len;              int         len;
             void        *freeIt;              void        *freeIt;
   
             cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &freeIt);              cp2 = Var_Parse(cp, VAR_CMD, TRUE, TRUE, &len, &freeIt);
             Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);              Buf_AddBytes(&buf, strlen(cp2), cp2);
             if (freeIt)              if (freeIt)
                 free(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 != ')') {  
       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 319  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) != NULL) {
         result = TRUE;          result = TRUE;
     } else {      } else {
Line 333  CondDoDefined(int argLen, char *arg)
Line 348  CondDoDefined(int argLen, char *arg)
     }      }
     if (p1)      if (p1)
         free(p1);          free(p1);
     arg[argLen] = savec;  
     return (result);      return (result);
 }  }
   
Line 352  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 371  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, arg, CondStrMatch) == NILLNODE) {  
         result = FALSE;  
     } else {  
         result = TRUE;  
     }  
     arg[argLen] = savec;  
     return (result);  
 }  }
   
 /*-  /*-
Line 400  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 (DEBUG(COND)) {
           fprintf(debug_file, "exists(%s) result is \"%s\"\n",
                  arg, path ? path : "");
       }
     if (path != NULL) {      if (path != NULL) {
         result = TRUE;          result = TRUE;
         free(path);          free(path);
     } else {      } else {
         result = FALSE;          result = FALSE;
     }      }
     arg[argLen] = savec;  
     if (DEBUG(COND)) {  
         fprintf(debug_file, "exists(%s) result is \"%s\"\n",  
                arg, path ? path : "");  
     }  
     return (result);      return (result);
 }  }
   
Line 436  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 469  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);  
 }  }
   
 /*-  /*-
Line 543  CondCvtArg(char *str, double *value)
Line 526  CondCvtArg(char *str, double *value)
  */   */
 /* coverity:[+alloc : arg-*2] */  /* coverity:[+alloc : arg-*2] */
 static char *  static char *
 CondGetString(Boolean doEval, Boolean *quoted, void **freeIt)  CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
 {  {
     Buffer buf;      Buffer buf;
     char *cp;      char *cp;
Line 552  CondGetString(Boolean doEval, Boolean *q
Line 535  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;      *freeIt = NULL;
     *quoted = qt = *condExpr == '"' ? 1 : 0;      *quoted = qt = *condExpr == '"' ? 1 : 0;
Line 563  CondGetString(Boolean doEval, Boolean *q
Line 546  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 571  CondGetString(Boolean doEval, Boolean *q
Line 554  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 583  CondGetString(Boolean doEval, Boolean *q
Line 566  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, (qt ? 0 : doEval),
                             &len, freeIt);                              TRUE, &len, freeIt);
             if (str == var_Error) {              if (str == var_Error) {
                 if (*freeIt) {                  if (*freeIt) {
                     free(*freeIt);                      free(*freeIt);
Line 617  CondGetString(Boolean doEval, Boolean *q
Line 600  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) {              if (*freeIt) {
                 free(*freeIt);                  free(*freeIt);
Line 627  CondGetString(Boolean doEval, Boolean *q
Line 610  CondGetString(Boolean doEval, Boolean *q
             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;      *freeIt = str;
  cleanup:   cleanup:
     Buf_Destroy(buf, FALSE);      Buf_Destroy(&buf, FALSE);
     return str;      return str;
 }  }
   
Line 649  CondGetString(Boolean doEval, Boolean *q
Line 641  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.
  *   *
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
Line 664  compare_expression(Boolean doEval)
Line 656  compare_expression(Boolean doEval)
     void        *rhsFree;      void        *rhsFree;
     Boolean lhsQuoted;      Boolean lhsQuoted;
     Boolean rhsQuoted;      Boolean rhsQuoted;
       double      left, right;
   
       t = TOK_ERROR;
     rhs = NULL;      rhs = NULL;
     lhsFree = rhsFree = FALSE;      lhsFree = rhsFree = FALSE;
     lhsQuoted = rhsQuoted = FALSE;      lhsQuoted = rhsQuoted = FALSE;
Line 673  compare_expression(Boolean doEval)
Line 667  compare_expression(Boolean doEval)
      * Parse the variable spec and skip over it, saving its       * Parse the variable spec and skip over it, saving its
      * value in lhs.       * value in lhs.
      */       */
     t = Err;      lhs = CondGetString(doEval, &lhsQuoted, &lhsFree, lhsStrict);
     lhs = CondGetString(doEval, &lhsQuoted, &lhsFree);      if (!lhs)
     if (!lhs) {          goto done;
         if (lhsFree)  
             free(lhsFree);  
         return Err;  
     }  
     /*      /*
      * Skip whitespace to get to the operator       * Skip whitespace to get to the operator
      */       */
Line 704  compare_expression(Boolean doEval)
Line 695  compare_expression(Boolean doEval)
             }              }
             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) {
             goto do_compare;                  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)) {  
       while (isspace((unsigned char)*condExpr))
         condExpr++;          condExpr++;
     }  
     if (*condExpr == '\0') {      if (*condExpr == '\0') {
         Parse_Error(PARSE_WARNING,          Parse_Error(PARSE_WARNING,
                     "Missing right-hand-side of operator");                      "Missing right-hand-side of operator");
         goto error;          goto done;
     }      }
     rhs = CondGetString(doEval, &rhsQuoted, &rhsFree);  
     if (!rhs) {      rhs = CondGetString(doEval, &rhsQuoted, &rhsFree, FALSE);
         if (lhsFree)      if (!rhs)
             free(lhsFree);          goto done;
         if (rhsFree)  
             free(rhsFree);  
         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)) {          if (DEBUG(COND)) {
Line 746  do_string_compare:
Line 749  do_string_compare:
          * t is set to the result.           * t is set to the result.
          */           */
         if (*op == '=') {          if (*op == '=') {
             t = strcmp(lhs, rhs) ? False : True;              t = strcmp(lhs, rhs) == 0;
         } else {          } else {
             t = strcmp(lhs, rhs) ? True : False;              t = strcmp(lhs, rhs) != 0;
         }          }
     } else {      } else {
         /*          /*
          * rhs is either a float or an integer. Convert both the           * rhs is either a float or an integer. Convert both the
          * lhs and the rhs to a double and compare the two.           * lhs and the rhs to a double and compare the two.
          */           */
         double          left, right;  
   
         if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right))          if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right))
             goto do_string_compare;              goto do_string_compare;
Line 769  do_string_compare:
Line 771  do_string_compare:
             if (op[1] != '=') {              if (op[1] != '=') {
                 Parse_Error(PARSE_WARNING,                  Parse_Error(PARSE_WARNING,
                             "Unknown operator");                              "Unknown operator");
                 goto error;                  goto done;
             }              }
             t = (left != right ? True : False);              t = (left != right);
             break;              break;
         case '=':          case '=':
             if (op[1] != '=') {              if (op[1] != '=') {
                 Parse_Error(PARSE_WARNING,                  Parse_Error(PARSE_WARNING,
                             "Unknown operator");                              "Unknown operator");
                 goto error;                  goto done;
             }              }
             t = (left == right ? True : False);              t = (left == right);
             break;              break;
         case '<':          case '<':
             if (op[1] == '=') {              if (op[1] == '=') {
                 t = (left <= right ? True : False);                  t = (left <= right);
             } else {              } else {
                 t = (left < right ? True : False);                  t = (left < right);
             }              }
             break;              break;
         case '>':          case '>':
             if (op[1] == '=') {              if (op[1] == '=') {
                 t = (left >= right ? True : False);                  t = (left >= right);
             } else {              } else {
                 t = (left > right ? True : False);                  t = (left > right);
             }              }
             break;              break;
         }          }
     }      }
 error:  
   done:
     if (lhsFree)      if (lhsFree)
         free(lhsFree);          free(lhsFree);
     if (rhsFree)      if (rhsFree)
Line 806  error:
Line 809  error:
 }  }
   
 static int  static int
 get_mpt_arg(char **linePtr, char **argPtr, const char *func, Boolean parens)  get_mpt_arg(char **linePtr, char **argPtr, const char *func MAKE_ATTR_UNUSED)
 {  {
     /*      /*
      * Use Var_Parse to parse the spec in parens and return       * Use Var_Parse to parse the spec in parens and return
      * True if the resulting string is empty.       * TOK_TRUE if the resulting string is empty.
      */       */
     int     length;      int     length;
     void    *freeIt;      void    *freeIt;
Line 820  get_mpt_arg(char **linePtr, char **argPt
Line 823  get_mpt_arg(char **linePtr, char **argPt
     /* We do all the work here and return the result as the length */      /* We do all the work here and return the result as the length */
     *argPtr = NULL;      *argPtr = NULL;
   
     val = Var_Parse(cp - 1, VAR_CMD, FALSE, &length, &freeIt);      val = Var_Parse(cp - 1, VAR_CMD, FALSE, TRUE, &length, &freeIt);
     /*      /*
      * Advance *linePtr to beyond the closing ). Note that       * Advance *linePtr to beyond the closing ). Note that
      * we subtract one because 'length' is calculated from 'cp - 1'.       * we subtract one because 'length' is calculated from 'cp - 1'.
Line 847  get_mpt_arg(char **linePtr, char **argPt
Line 850  get_mpt_arg(char **linePtr, char **argPt
 }  }
   
 static Boolean  static Boolean
 CondDoEmpty(int arglen, char *arg)  CondDoEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED)
 {  {
     return arglen == 1;      return arglen == 1;
 }  }
Line 858  compare_function(Boolean doEval)
Line 861  compare_function(Boolean doEval)
     static const struct fn_def {      static const struct fn_def {
         const char  *fn_name;          const char  *fn_name;
         int         fn_name_len;          int         fn_name_len;
         int         (*fn_getarg)(char **, char **, const char *, Boolean);          int         (*fn_getarg)(char **, char **, const char *);
         Boolean     (*fn_proc)(int, char *);          Boolean     (*fn_proc)(int, const char *);
     } fn_defs[] = {      } fn_defs[] = {
         { "defined",   7, CondGetArg, CondDoDefined },          { "defined",   7, CondGetArg, CondDoDefined },
         { "make",      4, CondGetArg, CondDoMake },          { "make",      4, CondGetArg, CondDoMake },
Line 874  compare_function(Boolean doEval)
Line 877  compare_function(Boolean doEval)
     char        *arg = NULL;      char        *arg = NULL;
     int arglen;      int arglen;
     char *cp = condExpr;      char *cp = condExpr;
       char *cp1;
   
     for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) {      for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) {
         if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len))          if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len))
Line 885  compare_function(Boolean doEval)
Line 889  compare_function(Boolean doEval)
         if (*cp != '(')          if (*cp != '(')
             break;              break;
   
         arglen = fn_def->fn_getarg(&cp, &arg, fn_def->fn_name, TRUE);          arglen = fn_def->fn_getarg(&cp, &arg, fn_def->fn_name);
         if (arglen <= 0) {          if (arglen <= 0) {
             if (arglen < 0) {              condExpr = cp;
                 condExpr = cp;              return arglen < 0 ? TOK_ERROR : TOK_FALSE;
                 return Err;  
             }  
             break;  
         }          }
         /* Evaluate the argument using the required function. */          /* Evaluate the argument using the required function. */
         t = !doEval || fn_def->fn_proc(arglen, arg) ? True : False;          t = !doEval || fn_def->fn_proc(arglen, arg);
         if (arg)          if (arg)
             free(arg);              free(arg);
         condExpr = cp;          condExpr = cp;
Line 902  compare_function(Boolean doEval)
Line 903  compare_function(Boolean doEval)
     }      }
   
     /* Push anything numeric through the compare expression */      /* Push anything numeric through the compare expression */
     if (isdigit((unsigned char)condExpr[0]) || strchr("+-", condExpr[0]))      cp = condExpr;
       if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0]))
         return compare_expression(doEval);          return compare_expression(doEval);
   
     /*      /*
      * Evaluate the argument using the default function. If invert       * Most likely we have a naked token to apply the default function to.
      * is TRUE, we invert the sense of the result.       * 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(&condExpr, &arg, "", FALSE);      arglen = CondGetArg(&cp, &arg, NULL);
     t = !doEval || (* condDefProc)(arglen, arg) != condInvert ? True : False;      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)      if (arg)
         free(arg);          free(arg);
     return t;      return t;
Line 922  CondToken(Boolean doEval)
Line 940  CondToken(Boolean doEval)
     Token t;      Token t;
   
     t = condPushBack;      t = condPushBack;
     if (t != None) {      if (t != TOK_NONE) {
         condPushBack = None;          condPushBack = TOK_NONE;
         return t;          return t;
     }      }
   
Line 935  CondToken(Boolean doEval)
Line 953  CondToken(Boolean doEval)
   
     case '(':      case '(':
         condExpr++;          condExpr++;
         return LParen;          return TOK_LPAREN;
   
     case ')':      case ')':
         condExpr++;          condExpr++;
         return RParen;          return TOK_RPAREN;
   
     case '|':      case '|':
         if (condExpr[1] == '|') {          if (condExpr[1] == '|') {
             condExpr++;              condExpr++;
         }          }
         condExpr++;          condExpr++;
         return Or;          return TOK_OR;
   
     case '&':      case '&':
         if (condExpr[1] == '&') {          if (condExpr[1] == '&') {
             condExpr++;              condExpr++;
         }          }
         condExpr++;          condExpr++;
         return And;          return TOK_AND;
   
     case '!':      case '!':
         condExpr++;          condExpr++;
         return Not;          return TOK_NOT;
   
     case '#':      case '#':
     case '\n':      case '\n':
     case '\0':      case '\0':
         return EndOfFile;          return TOK_EOF;
   
     case '"':      case '"':
     case '$':      case '$':
Line 977  CondToken(Boolean doEval)
Line 995  CondToken(Boolean 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 997  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 1031  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 1044  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);
Line 1077  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 1090  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);
Line 1136  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 1208  err:
Line 1248  err:
 int  int
 Cond_Eval(char *line)  Cond_Eval(char *line)
 {  {
     #define         MAXIF       64      /* maximum depth of .if'ing */  #define     MAXIF      128      /* maximum depth of .if'ing */
   #define     MAXIF_BUMP  32      /* how much to grow by */
     enum if_states {      enum if_states {
         IF_ACTIVE,              /* .if or .elif part active */          IF_ACTIVE,              /* .if or .elif part active */
         ELSE_ACTIVE,            /* .else part active */          ELSE_ACTIVE,            /* .else part active */
Line 1216  Cond_Eval(char *line)
Line 1257  Cond_Eval(char *line)
         SKIP_TO_ELSE,           /* has been true, but not seen '.else' */          SKIP_TO_ELSE,           /* has been true, but not seen '.else' */
         SKIP_TO_ENDIF           /* nothing else to execute */          SKIP_TO_ENDIF           /* nothing else to execute */
     };      };
     static enum if_states cond_state[MAXIF + 1] = { IF_ACTIVE };      static enum if_states *cond_state = NULL;
       static unsigned int max_if_depth = MAXIF;
   
     const struct If *ifp;      const struct If *ifp;
     Boolean         isElif;      Boolean         isElif;
Line 1225  Cond_Eval(char *line)
Line 1267  Cond_Eval(char *line)
     enum if_states  state;      enum if_states  state;
   
     level = PARSE_FATAL;      level = PARSE_FATAL;
       if (!cond_state) {
           cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state));
           cond_state[0] = IF_ACTIVE;
       }
     /* skip leading character (the '.') and any whitespace */      /* skip leading character (the '.') and any whitespace */
     for (line++; *line == ' ' || *line == '\t'; line++)      for (line++; *line == ' ' || *line == '\t'; line++)
         continue;          continue;
Line 1242  Cond_Eval(char *line)
Line 1287  Cond_Eval(char *line)
             }              }
             /* Return state for previous conditional */              /* Return state for previous conditional */
             cond_depth--;              cond_depth--;
             if (cond_depth > MAXIF)  
                 return COND_SKIP;  
             return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;              return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
         }          }
   
Line 1256  Cond_Eval(char *line)
Line 1299  Cond_Eval(char *line)
                 return COND_PARSE;                  return COND_PARSE;
             }              }
   
             if (cond_depth > MAXIF)  
                 return COND_SKIP;  
             state = cond_state[cond_depth];              state = cond_state[cond_depth];
             switch (state) {              switch (state) {
             case SEARCH_FOR_ELIF:              case SEARCH_FOR_ELIF:
Line 1306  Cond_Eval(char *line)
Line 1347  Cond_Eval(char *line)
             Parse_Error(level, "if-less elif");              Parse_Error(level, "if-less elif");
             return COND_PARSE;              return COND_PARSE;
         }          }
         if (cond_depth > MAXIF)  
             /* Error reported when we saw the .if ... */  
             return COND_SKIP;  
         state = cond_state[cond_depth];          state = cond_state[cond_depth];
         if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) {          if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) {
             Parse_Error(PARSE_WARNING, "extra elif");              Parse_Error(PARSE_WARNING, "extra elif");
Line 1322  Cond_Eval(char *line)
Line 1360  Cond_Eval(char *line)
         }          }
     } else {      } else {
         /* Normal .if */          /* Normal .if */
         if (cond_depth >= MAXIF) {          if (cond_depth + 1 >= max_if_depth) {
             cond_depth++;              /*
             Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF);               * This is rare, but not impossible.
             return COND_SKIP;               * In meta mode, dirdeps.mk (only runs at level 0)
                * can need more than the default.
                */
               max_if_depth += MAXIF_BUMP;
               cond_state = bmake_realloc(cond_state, max_if_depth *
                   sizeof(*cond_state));
         }          }
         state = cond_state[cond_depth];          state = cond_state[cond_depth];
         cond_depth++;          cond_depth++;
Line 1336  Cond_Eval(char *line)
Line 1379  Cond_Eval(char *line)
         }          }
     }      }
   
     /* Initialize file-global variables for parsing the expression */  
     condDefProc = ifp->defProc;  
     condInvert = ifp->doNot;  
   
     /* And evaluate the conditional expresssion */      /* And evaluate the conditional expresssion */
     if (Cond_EvalExpression(0, line, &value, 1) == COND_INVALID) {      if (Cond_EvalExpression(ifp, line, &value, 1, TRUE) == COND_INVALID) {
         /* Syntax error in conditional, error message already output. */          /* Syntax error in conditional, error message already output. */
         /* Skip everything to matching .endif */          /* Skip everything to matching .endif */
         cond_state[cond_depth] = SKIP_TO_ELSE;          cond_state[cond_depth] = SKIP_TO_ELSE;

Legend:
Removed from v.1.47  
changed lines
  Added in v.1.69

CVSweb <webmaster@jp.NetBSD.org>