version 1.11, 1998/09/18 20:35:11 |
version 1.65, 2012/11/03 02:25:13 |
|
|
|
|
/* |
/* |
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. |
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. |
|
* All rights reserved. |
|
* |
|
* This code is derived from software contributed to Berkeley by |
|
* Adam de Boor. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* 3. Neither the name of the University nor the names of its contributors |
|
* may be used to endorse or promote products derived from this software |
|
* without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
* SUCH DAMAGE. |
|
*/ |
|
|
|
/* |
* Copyright (c) 1988, 1989 by Adam de Boor |
* Copyright (c) 1988, 1989 by Adam de Boor |
* Copyright (c) 1989 by Berkeley Softworks |
* Copyright (c) 1989 by Berkeley Softworks |
* All rights reserved. |
* All rights reserved. |
|
|
* 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 61 __RCSID("$NetBSD$"); |
|
Line 92 __RCSID("$NetBSD$"); |
|
*/ |
*/ |
|
|
#include <ctype.h> |
#include <ctype.h> |
#include <math.h> |
#include <errno.h> /* For strtoul() error checking */ |
|
|
#include "make.h" |
#include "make.h" |
#include "hash.h" |
#include "hash.h" |
#include "dir.h" |
#include "dir.h" |
Line 78 __RCSID("$NetBSD$"); |
|
Line 110 __RCSID("$NetBSD$"); |
|
* T -> exists(file) |
* T -> exists(file) |
* T -> empty(varspec) |
* T -> empty(varspec) |
* T -> target(name) |
* T -> target(name) |
|
* T -> commands(name) |
* T -> symbol |
* T -> symbol |
* 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 90 __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. |
|
* |
|
* TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons. |
* |
* |
* All Non-Terminal functions (CondE, CondF and CondT) return Err on error. |
* 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; |
|
|
/*- |
/*- |
* Structures to handle elegantly the different forms of #if's. The |
* Structures to handle elegantly the different forms of #if's. The |
* last two fields are stored in condInvert and condDefProc, respectively. |
* last two fields are stored in condInvert and condDefProc, respectively. |
*/ |
*/ |
static void CondPushBack __P((Token)); |
static void CondPushBack(Token); |
static int CondGetArg __P((char **, char **, char *, Boolean)); |
static int CondGetArg(char **, char **, const char *); |
static Boolean CondDoDefined __P((int, char *)); |
static Boolean CondDoDefined(int, const char *); |
static int CondStrMatch __P((ClientData, ClientData)); |
static int CondStrMatch(const void *, const void *); |
static Boolean CondDoMake __P((int, char *)); |
static Boolean CondDoMake(int, const char *); |
static Boolean CondDoExists __P((int, char *)); |
static Boolean CondDoExists(int, const char *); |
static Boolean CondDoTarget __P((int, char *)); |
static Boolean CondDoTarget(int, const char *); |
static Boolean CondCvtArg __P((char *, double *)); |
static Boolean CondDoCommands(int, const char *); |
static Token CondToken __P((Boolean)); |
static Boolean CondCvtArg(char *, double *); |
static Token CondT __P((Boolean)); |
static Token CondToken(Boolean); |
static Token CondF __P((Boolean)); |
static Token CondT(Boolean); |
static Token CondE __P((Boolean)); |
static Token CondF(Boolean); |
|
static Token CondE(Boolean); |
|
static int do_Cond_EvalExpression(Boolean *); |
|
|
static struct If { |
static const struct If { |
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) __P((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) /* Default function to apply */ |
|
__P((int, char *)); |
|
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 |
static int condTop = MAXIF; /* Top-most conditional */ |
istoken(const char *str, const char *tok, size_t len) |
static int skipIfLevel=0; /* Depth of skipped conditionals */ |
{ |
static Boolean skipLine = FALSE; /* Whether the parse module is skipping |
return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]); |
* lines */ |
} |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
Line 153 static Boolean skipLine = FALSE; /* W |
|
Line 193 static Boolean skipLine = FALSE; /* W |
|
* Push back the most recent token read. We only need one level of |
* Push back the most recent token read. We only need one level of |
* this, so the thing is just stored in 'condPushback'. |
* this, so the thing is just stored in 'condPushback'. |
* |
* |
|
* Input: |
|
* t Token to push back into the "stream" |
|
* |
* Results: |
* Results: |
* None. |
* None. |
* |
* |
Line 162 static Boolean skipLine = FALSE; /* W |
|
Line 205 static Boolean skipLine = FALSE; /* W |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static void |
static void |
CondPushBack (t) |
CondPushBack(Token t) |
Token t; /* Token to push back into the "stream" */ |
|
{ |
{ |
condPushBack = t; |
condPushBack = t; |
} |
} |
Line 173 CondPushBack (t) |
|
Line 215 CondPushBack (t) |
|
* CondGetArg -- |
* CondGetArg -- |
* Find the argument of a built-in function. |
* Find the argument of a built-in function. |
* |
* |
|
* Input: |
|
* parens TRUE if arg should be bounded by parens |
|
* |
* Results: |
* Results: |
* The length of the argument and the address of the argument. |
* The length of the argument and the address of the argument. |
* |
* |
Line 183 CondPushBack (t) |
|
Line 228 CondPushBack (t) |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static int |
static int |
CondGetArg (linePtr, argPtr, func, parens) |
CondGetArg(char **linePtr, char **argPtr, const char *func) |
char **linePtr; |
|
char **argPtr; |
|
char *func; |
|
Boolean parens; /* TRUE if arg should be bounded by parens */ |
|
{ |
{ |
register char *cp; |
char *cp; |
int argLen; |
int argLen; |
register 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 210 CondGetArg (linePtr, argPtr, func, paren |
|
Line 248 CondGetArg (linePtr, argPtr, func, paren |
|
* 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 222 CondGetArg (linePtr, argPtr, func, paren |
|
Line 260 CondGetArg (linePtr, argPtr, func, paren |
|
* 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 234 CondGetArg (linePtr, argPtr, func, paren |
|
Line 278 CondGetArg (linePtr, argPtr, func, paren |
|
*/ |
*/ |
char *cp2; |
char *cp2; |
int len; |
int len; |
Boolean doFree; |
void *freeIt; |
|
|
cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree); |
cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &freeIt); |
|
Buf_AddBytes(&buf, strlen(cp2), cp2); |
Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); |
if (freeIt) |
if (doFree) { |
free(freeIt); |
free(cp2); |
|
} |
|
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 285 CondGetArg (linePtr, argPtr, func, paren |
|
Line 327 CondGetArg (linePtr, argPtr, func, paren |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
static Boolean |
CondDoDefined (argLen, arg) |
CondDoDefined(int argLen MAKE_ATTR_UNUSED, const char *arg) |
int argLen; |
|
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 320 CondDoDefined (argLen, arg) |
|
Line 357 CondDoDefined (argLen, arg) |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static int |
static int |
CondStrMatch(string, pattern) |
CondStrMatch(const void *string, const void *pattern) |
ClientData string; |
|
ClientData pattern; |
|
{ |
{ |
return(!Str_Match((char *) string,(char *) pattern)); |
return(!Str_Match(string, pattern)); |
} |
} |
|
|
/*- |
/*- |
Line 341 CondStrMatch(string, pattern) |
|
Line 376 CondStrMatch(string, pattern) |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
static Boolean |
CondDoMake (argLen, arg) |
CondDoMake(int argLen MAKE_ATTR_UNUSED, const char *arg) |
int argLen; |
|
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 372 CondDoMake (argLen, arg) |
|
Line 395 CondDoMake (argLen, arg) |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
static Boolean |
CondDoExists (argLen, arg) |
CondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg) |
int argLen; |
|
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 406 CondDoExists (argLen, arg) |
|
Line 428 CondDoExists (argLen, arg) |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
static Boolean |
CondDoTarget (argLen, arg) |
CondDoTarget(int argLen MAKE_ATTR_UNUSED, const char *arg) |
int argLen; |
|
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); |
|
} |
} |
|
|
|
/*- |
|
*----------------------------------------------------------------------- |
|
* CondDoCommands -- |
|
* See if the given node exists and is an actual target with commands |
|
* associated with it. |
|
* |
|
* Results: |
|
* TRUE if the node exists as a target and has commands associated with |
|
* it and FALSE if it does not. |
|
* |
|
* Side Effects: |
|
* None. |
|
* |
|
*----------------------------------------------------------------------- |
|
*/ |
|
static Boolean |
|
CondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg) |
|
{ |
|
GNode *gn; |
|
|
|
gn = Targ_FindNode(arg, TARG_NOCREATE); |
|
return (gn != NULL) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands); |
|
} |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* 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 true if the string was a valid number, false o.w. |
* Returns 'true' if the convertion suceeded |
|
* |
|
*----------------------------------------------------------------------- |
|
*/ |
|
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. |
* |
* |
* |
* |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
/* coverity:[+alloc : arg-*2] */ |
CondCvtArg(str, value) |
static char * |
register char *str; |
CondGetString(Boolean doEval, Boolean *quoted, void **freeIt) |
double *value; |
{ |
{ |
Buffer buf; |
if ((*str == '0') && (str[1] == 'x')) { |
char *cp; |
register long i; |
char *str; |
|
int len; |
for (str += 2, i = 0; *str; str++) { |
int qt; |
int x; |
char *start; |
if (isdigit((unsigned char) *str)) |
|
x = *str - '0'; |
Buf_Init(&buf, 0); |
else if (isxdigit((unsigned char) *str)) |
str = NULL; |
x = 10 + *str - isupper((unsigned char) *str) ? 'A' : 'a'; |
*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 |
return FALSE; |
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: |
|
Buf_AddByte(&buf, *condExpr); |
|
break; |
} |
} |
*value = (double) i; |
|
return TRUE; |
|
} |
|
else { |
|
char *eptr; |
|
*value = strtod(str, &eptr); |
|
return *eptr == '\0'; |
|
} |
} |
|
got_str: |
|
str = Buf_GetAll(&buf, NULL); |
|
*freeIt = str; |
|
cleanup: |
|
Buf_Destroy(&buf, FALSE); |
|
return str; |
} |
} |
|
|
/*- |
/*- |
Line 481 CondCvtArg(str, value) |
|
Line 622 CondCvtArg(str, 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(doEval) |
compare_expression(Boolean doEval) |
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); |
|
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); |
|
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 '\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 = "!="; |
Token t; |
rhs = "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 { |
|
if (!CondCvtArg(rhs, &right)) |
|
goto do_string_compare; |
|
if (rhs == condExpr) { |
|
/* |
|
* Skip over the right-hand side |
|
*/ |
|
while(!isspace((unsigned char) *condExpr) && |
|
(*condExpr != '\0')) { |
|
condExpr++; |
|
} |
|
} |
|
} |
|
|
|
if (DEBUG(COND)) { |
|
printf("left = %f, right = %f, op = %.2s\n", left, |
|
right, op); |
|
} |
|
switch(op[0]) { |
|
case '!': |
|
if (op[1] != '=') { |
|
Parse_Error(PARSE_WARNING, |
|
"Unknown operator"); |
|
goto error; |
|
} |
|
t = (left != right ? True : False); |
|
break; |
|
case '=': |
|
if (op[1] != '=') { |
|
Parse_Error(PARSE_WARNING, |
|
"Unknown operator"); |
|
goto error; |
|
} |
|
t = (left == right ? True : False); |
|
break; |
|
case '<': |
|
if (op[1] == '=') { |
|
t = (left <= right ? True : False); |
|
} else { |
|
t = (left < right ? True : False); |
|
} |
|
break; |
|
case '>': |
|
if (op[1] == '=') { |
|
t = (left >= right ? True : False); |
|
} else { |
|
t = (left > right ? True : False); |
|
} |
|
break; |
|
} |
|
} |
|
error: |
|
if (doFree) |
|
free(lhs); |
|
break; |
|
} |
|
default: { |
|
Boolean (*evalProc) __P((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, |
|
doEval, &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 { |
|
/* |
|
* 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. |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Token |
static Token |
CondT(doEval) |
CondT(Boolean doEval) |
Boolean doEval; |
|
{ |
{ |
Token t; |
Token t; |
|
|
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); |
|
|
* 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. |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Token |
static Token |
CondF(doEval) |
CondF(Boolean doEval) |
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); |
|
|
* 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. |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Token |
static Token |
CondE(doEval) |
CondE(Boolean doEval) |
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); |
|
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
int |
int |
Cond_EvalExpression(dosetup, line, value, eprint) |
Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprint) |
int dosetup; |
{ |
char *line; |
static const struct If *dflt_info; |
Boolean *value; |
const struct If *sv_if_info = if_info; |
int eprint; |
char *sv_condExpr = condExpr; |
{ |
Token sv_condPushBack = condPushBack; |
if (dosetup) { |
int rval; |
condDefProc = CondDoDefined; |
|
condInvert = 0; |
|
} |
|
|
|
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; |
} |
} |
|
|
|
|
|
|
* 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) |
* and parenthetical groupings thereof. |
* and parenthetical groupings thereof. |
* |
* |
|
* Input: |
|
* line Line to parse |
|
* |
* Results: |
* Results: |
* COND_PARSE if should parse lines after the conditional |
* COND_PARSE if should parse lines after the conditional |
* COND_SKIP if should skip lines after the conditional |
* COND_SKIP if should skip lines after the conditional |
|
|
* 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 (line) |
Cond_Eval(char *line) |
char *line; /* Line to parse */ |
|
{ |
{ |
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 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 >= 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) == 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 1293 Cond_Eval (line) |
|
Line 1390 Cond_Eval (line) |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
Cond_End() |
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; |
} |
} |