version 1.33, 2006/07/28 17:07:33 |
version 1.69, 2015/10/11 04:51:24 |
Line 92 __RCSID("$NetBSD$"); |
|
Line 92 __RCSID("$NetBSD$"); |
|
*/ |
*/ |
|
|
#include <ctype.h> |
#include <ctype.h> |
|
#include <errno.h> /* For strtoul() error checking */ |
|
|
#include "make.h" |
#include "make.h" |
#include "hash.h" |
#include "hash.h" |
Line 123 __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; |
|
|
/*- |
/*- |
|
|
* last two fields are stored in condInvert and condDefProc, respectively. |
* last two fields are stored in condInvert and condDefProc, respectively. |
*/ |
*/ |
static void CondPushBack(Token); |
static void CondPushBack(Token); |
static int CondGetArg(char **, char **, const char *, Boolean); |
static int CondGetArg(char **, char **, const char *); |
static Boolean CondDoDefined(int, char *); |
static Boolean CondDoDefined(int, const char *); |
static int CondStrMatch(ClientData, ClientData); |
static int CondStrMatch(const void *, const void *); |
static Boolean CondDoMake(int, char *); |
static Boolean CondDoMake(int, const char *); |
static Boolean CondDoExists(int, char *); |
static Boolean CondDoExists(int, const char *); |
static Boolean CondDoTarget(int, char *); |
static Boolean CondDoTarget(int, const char *); |
static Boolean CondDoCommands(int, char *); |
static Boolean CondDoCommands(int, const char *); |
static char * CondCvtArg(char *, double *); |
static Boolean CondCvtArg(char *, double *); |
static Token CondToken(Boolean); |
static Token CondToken(Boolean); |
static Token CondT(Boolean); |
static Token CondT(Boolean); |
static Token CondF(Boolean); |
static Token CondF(Boolean); |
static Token CondE(Boolean); |
static Token CondE(Boolean); |
|
static int do_Cond_EvalExpression(Boolean *); |
|
|
static struct If { |
static const struct If { |
const char *form; /* Form of if */ |
const char *form; /* Form of if */ |
int formlen; /* Length of form */ |
int formlen; /* Length of form */ |
Boolean doNot; /* TRUE if default function should be negated */ |
Boolean doNot; /* TRUE if default function should be negated */ |
Boolean (*defProc)(int, char *); /* Default function to apply */ |
Boolean (*defProc)(int, const char *); /* Default function to apply */ |
} ifs[] = { |
} ifs[] = { |
{ "ifdef", 5, FALSE, CondDoDefined }, |
{ "def", 3, FALSE, CondDoDefined }, |
{ "ifndef", 6, TRUE, CondDoDefined }, |
{ "ndef", 4, TRUE, CondDoDefined }, |
{ "ifmake", 6, FALSE, CondDoMake }, |
{ "make", 4, FALSE, CondDoMake }, |
{ "ifnmake", 7, TRUE, CondDoMake }, |
{ "nmake", 5, TRUE, CondDoMake }, |
{ "if", 2, FALSE, CondDoDefined }, |
{ "", 0, FALSE, CondDoDefined }, |
{ NULL, 0, FALSE, NULL } |
{ NULL, 0, FALSE, NULL } |
}; |
}; |
|
|
static Boolean condInvert; /* Invert the default function */ |
static const struct If *if_info; /* Info for current statement */ |
static Boolean (*condDefProc)(int, char *); /* Default function to apply */ |
|
static char *condExpr; /* The expression to parse */ |
static char *condExpr; /* The expression to parse */ |
static Token condPushBack=None; /* Single push-back token used in |
static Token condPushBack=TOK_NONE; /* Single push-back token used in |
* parsing */ |
* parsing */ |
|
|
#define MAXIF 64 /* greatest depth of #if'ing */ |
static unsigned int cond_depth = 0; /* current .if nesting level */ |
|
static unsigned int cond_min_depth = 0; /* depth at makefile open */ |
|
|
static Boolean finalElse[MAXIF+1][MAXIF+1]; /* Seen final else (stack) */ |
/* |
static Boolean condStack[MAXIF]; /* Stack of conditionals's values */ |
* Indicate when we should be strict about lhs of comparisons. |
static int condTop = MAXIF; /* Top-most conditional */ |
* TRUE when Cond_EvalExpression is called from Cond_Eval (.if etc) |
static int skipIfLevel=0; /* Depth of skipped conditionals */ |
* FALSE when Cond_EvalExpression is called from var.c:ApplyModifiers |
static Boolean skipLine = FALSE; /* Whether the parse module is skipping |
* since lhs is already expanded and we cannot tell if |
* lines */ |
* it was a variable reference or not. |
|
*/ |
|
static Boolean lhsStrict; |
|
|
static int |
static int |
istoken(const char *str, const char *tok, size_t len) |
istoken(const char *str, const char *tok, size_t len) |
Line 228 CondPushBack(Token t) |
|
Line 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 263 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 277 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 324 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 338 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 357 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 376 CondStrMatch(ClientData string, ClientDa |
|
Line 385 CondStrMatch(ClientData string, ClientDa |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
static Boolean |
CondDoMake(int argLen, char *arg) |
CondDoMake(int argLen MAKE_ATTR_UNUSED, const char *arg) |
{ |
{ |
char savec = arg[argLen]; |
return Lst_Find(create, arg, CondStrMatch) != NULL; |
Boolean result; |
|
|
|
arg[argLen] = '\0'; |
|
if (Lst_Find(create, (ClientData)arg, CondStrMatch) == NILLNODE) { |
|
result = FALSE; |
|
} else { |
|
result = TRUE; |
|
} |
|
arg[argLen] = savec; |
|
return (result); |
|
} |
} |
|
|
/*- |
/*- |
Line 405 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)) { |
|
printf("exists(%s) result is \"%s\"\n", |
|
arg, path ? path : ""); |
|
} |
|
return (result); |
return (result); |
} |
} |
|
|
Line 441 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 474 CondDoTarget(int argLen, char *arg) |
|
Line 461 CondDoTarget(int argLen, char *arg) |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
static Boolean |
CondDoCommands(int argLen, char *arg) |
CondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg) |
{ |
{ |
char savec = arg[argLen]; |
|
Boolean result; |
|
GNode *gn; |
GNode *gn; |
|
|
arg[argLen] = '\0'; |
|
gn = Targ_FindNode(arg, TARG_NOCREATE); |
gn = Targ_FindNode(arg, TARG_NOCREATE); |
if ((gn != NILGNODE) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands)) { |
return (gn != NULL) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands); |
result = TRUE; |
|
} else { |
|
result = FALSE; |
|
} |
|
arg[argLen] = savec; |
|
return (result); |
|
} |
} |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* CondCvtArg -- |
* CondCvtArg -- |
* Convert the given number into a double. If the number begins |
* Convert the given number into a double. |
* with 0x, it is interpreted as a hexadecimal integer |
* We try a base 10 or 16 integer conversion first, if that fails |
* and converted to a double from there. All other strings just have |
* then we try a floating point conversion instead. |
* strtod called on them. |
|
* |
* |
* Results: |
* Results: |
* Sets 'value' to double value of string. |
* Sets 'value' to double value of string. |
* Returns NULL if string was fully consumed, |
* Returns 'true' if the convertion suceeded |
* else returns remaining input. |
|
* |
|
* Side Effects: |
|
* Can change 'value' even if string is not a valid number. |
|
* |
|
* |
* |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static char * |
static Boolean |
CondCvtArg(char *str, double *value) |
CondCvtArg(char *str, double *value) |
{ |
{ |
if ((*str == '0') && (str[1] == 'x')) { |
char *eptr, ech; |
long i; |
unsigned long l_val; |
|
double d_val; |
for (str += 2, i = 0; *str; str++) { |
|
int x; |
errno = 0; |
if (isdigit((unsigned char) *str)) |
l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10); |
x = *str - '0'; |
ech = *eptr; |
else if (isxdigit((unsigned char) *str)) |
if (ech == 0 && errno != ERANGE) { |
x = 10 + *str - isupper((unsigned char) *str) ? 'A' : 'a'; |
d_val = str[0] == '-' ? -(double)-l_val : (double)l_val; |
else |
|
break; |
|
i = (i << 4) + x; |
|
} |
|
*value = (double) i; |
|
return *str ? str : NULL; |
|
} else { |
} else { |
char *eptr; |
if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E') |
*value = strtod(str, &eptr); |
return FALSE; |
return *eptr ? eptr : NULL; |
d_val = strtod(str, &eptr); |
|
if (*eptr) |
|
return FALSE; |
} |
} |
|
|
|
*value = d_val; |
|
return TRUE; |
} |
} |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* CondGetString -- |
* CondGetString -- |
Line 555 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 564 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 575 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 583 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 595 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 629 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 639 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 661 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. |
* |
* |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Token |
static Token |
CondToken(Boolean doEval) |
compare_expression(Boolean doEval) |
{ |
{ |
Token t; |
Token t; |
|
char *lhs; |
|
char *rhs; |
|
char *op; |
|
void *lhsFree; |
|
void *rhsFree; |
|
Boolean lhsQuoted; |
|
Boolean rhsQuoted; |
|
double left, right; |
|
|
|
t = TOK_ERROR; |
|
rhs = NULL; |
|
lhsFree = rhsFree = FALSE; |
|
lhsQuoted = rhsQuoted = FALSE; |
|
|
|
/* |
|
* Parse the variable spec and skip over it, saving its |
|
* value in lhs. |
|
*/ |
|
lhs = CondGetString(doEval, &lhsQuoted, &lhsFree, lhsStrict); |
|
if (!lhs) |
|
goto done; |
|
|
if (condPushBack == None) { |
/* |
while (*condExpr == ' ' || *condExpr == '\t') { |
* Skip whitespace to get to the operator |
condExpr++; |
*/ |
} |
while (isspace((unsigned char) *condExpr)) |
switch (*condExpr) { |
condExpr++; |
case '(': |
|
t = LParen; |
|
condExpr++; |
|
break; |
|
case ')': |
|
t = RParen; |
|
condExpr++; |
|
break; |
|
case '|': |
|
if (condExpr[1] == '|') { |
|
condExpr++; |
|
} |
|
condExpr++; |
|
t = Or; |
|
break; |
|
case '&': |
|
if (condExpr[1] == '&') { |
|
condExpr++; |
|
} |
|
condExpr++; |
|
t = And; |
|
break; |
|
case '!': |
|
t = Not; |
|
condExpr++; |
|
break; |
|
case '#': |
|
case '\n': |
|
case '\0': |
|
t = EndOfFile; |
|
break; |
|
case '"': |
|
case '$': { |
|
char *lhs; |
|
char *rhs; |
|
char *op; |
|
void *lhsFree; |
|
void *rhsFree; |
|
Boolean lhsQuoted; |
|
Boolean rhsQuoted; |
|
|
|
rhs = NULL; |
|
lhsFree = rhsFree = FALSE; |
|
lhsQuoted = rhsQuoted = FALSE; |
|
|
|
/* |
|
* Parse the variable spec and skip over it, saving its |
|
* value in lhs. |
|
*/ |
|
t = Err; |
|
lhs = CondGetString(doEval, &lhsQuoted, &lhsFree); |
|
if (!lhs) { |
|
if (lhsFree) |
|
free(lhsFree); |
|
return Err; |
|
} |
|
/* |
|
* Skip whitespace to get to the operator |
|
*/ |
|
while (isspace((unsigned char) *condExpr)) |
|
condExpr++; |
|
|
|
/* |
/* |
* Make sure the operator is a valid one. If it isn't a |
* Make sure the operator is a valid one. If it isn't a |
* known relational operator, pretend we got a |
* known relational operator, pretend we got a |
* != 0 comparison. |
* != 0 comparison. |
*/ |
*/ |
op = condExpr; |
op = condExpr; |
switch (*condExpr) { |
switch (*condExpr) { |
case '!': |
case '!': |
case '=': |
case '=': |
case '<': |
case '<': |
case '>': |
case '>': |
if (condExpr[1] == '=') { |
if (condExpr[1] == '=') { |
condExpr += 2; |
condExpr += 2; |
} else { |
} else { |
condExpr += 1; |
condExpr += 1; |
} |
} |
break; |
break; |
default: |
default: |
op = UNCONST("!="); |
if (!doEval) { |
if (lhsQuoted) |
t = TOK_FALSE; |
rhs = UNCONST(""); |
goto done; |
else |
} |
rhs = UNCONST("0"); |
/* For .ifxxx "..." check for non-empty string. */ |
|
if (lhsQuoted) { |
|
t = lhs[0] != 0; |
|
goto done; |
|
} |
|
/* For .ifxxx <number> compare against zero */ |
|
if (CondCvtArg(lhs, &left)) { |
|
t = left != 0.0; |
|
goto done; |
|
} |
|
/* For .if ${...} check for non-empty string (defProc is ifdef). */ |
|
if (if_info->form[0] == 0) { |
|
t = lhs[0] != 0; |
|
goto done; |
|
} |
|
/* Otherwise action default test ... */ |
|
t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot; |
|
goto done; |
|
} |
|
|
goto do_compare; |
while (isspace((unsigned char)*condExpr)) |
} |
condExpr++; |
while (isspace((unsigned char) *condExpr)) { |
|
condExpr++; |
if (*condExpr == '\0') { |
} |
Parse_Error(PARSE_WARNING, |
if (*condExpr == '\0') { |
"Missing right-hand-side of operator"); |
Parse_Error(PARSE_WARNING, |
goto done; |
"Missing right-hand-side of operator"); |
} |
goto error; |
|
} |
rhs = CondGetString(doEval, &rhsQuoted, &rhsFree, FALSE); |
rhs = CondGetString(doEval, &rhsQuoted, &rhsFree); |
if (!rhs) |
if (!rhs) { |
goto done; |
if (lhsFree) |
|
free(lhsFree); |
if (rhsQuoted || lhsQuoted) { |
if (rhsFree) |
|
free(rhsFree); |
|
return Err; |
|
} |
|
do_compare: |
|
if (rhsQuoted || lhsQuoted) { |
|
do_string_compare: |
do_string_compare: |
if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { |
if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { |
Parse_Error(PARSE_WARNING, |
Parse_Error(PARSE_WARNING, |
"String comparison operator should be either == or !="); |
"String comparison operator should be either == or !="); |
goto error; |
goto done; |
} |
} |
|
|
if (DEBUG(COND)) { |
|
printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n", |
|
lhs, rhs, op); |
|
} |
|
/* |
|
* Null-terminate rhs and perform the comparison. |
|
* t is set to the result. |
|
*/ |
|
if (*op == '=') { |
|
t = strcmp(lhs, rhs) ? False : True; |
|
} else { |
|
t = strcmp(lhs, rhs) ? True : False; |
|
} |
|
} else { |
|
/* |
|
* rhs is either a float or an integer. Convert both the |
|
* lhs and the rhs to a double and compare the two. |
|
*/ |
|
double left, right; |
|
char *cp; |
|
|
|
if (CondCvtArg(lhs, &left)) |
|
goto do_string_compare; |
|
if ((cp = CondCvtArg(rhs, &right)) && |
|
cp == rhs) |
|
goto do_string_compare; |
|
|
|
if (DEBUG(COND)) { |
|
printf("left = %f, right = %f, op = %.2s\n", left, |
|
right, op); |
|
} |
|
switch(op[0]) { |
|
case '!': |
|
if (op[1] != '=') { |
|
Parse_Error(PARSE_WARNING, |
|
"Unknown operator"); |
|
goto error; |
|
} |
|
t = (left != right ? True : False); |
|
break; |
|
case '=': |
|
if (op[1] != '=') { |
|
Parse_Error(PARSE_WARNING, |
|
"Unknown operator"); |
|
goto error; |
|
} |
|
t = (left == right ? True : False); |
|
break; |
|
case '<': |
|
if (op[1] == '=') { |
|
t = (left <= right ? True : False); |
|
} else { |
|
t = (left < right ? True : False); |
|
} |
|
break; |
|
case '>': |
|
if (op[1] == '=') { |
|
t = (left >= right ? True : False); |
|
} else { |
|
t = (left > right ? True : False); |
|
} |
|
break; |
|
} |
|
} |
|
error: |
|
if (lhsFree) |
|
free(lhsFree); |
|
if (rhsFree) |
|
free(rhsFree); |
|
break; |
|
} |
|
default: { |
|
Boolean (*evalProc)(int, char *); |
|
Boolean invert = FALSE; |
|
char *arg = NULL; |
|
int arglen = 0; |
|
|
|
if (istoken(condExpr, "defined", 7)) { |
|
/* |
|
* Use CondDoDefined to evaluate the argument and |
|
* CondGetArg to extract the argument from the 'function |
|
* call'. |
|
*/ |
|
evalProc = CondDoDefined; |
|
condExpr += 7; |
|
arglen = CondGetArg(&condExpr, &arg, "defined", TRUE); |
|
if (arglen == 0) { |
|
condExpr -= 7; |
|
goto use_default; |
|
} |
|
} else if (istoken(condExpr, "make", 4)) { |
|
/* |
|
* Use CondDoMake to evaluate the argument and |
|
* CondGetArg to extract the argument from the 'function |
|
* call'. |
|
*/ |
|
evalProc = CondDoMake; |
|
condExpr += 4; |
|
arglen = CondGetArg(&condExpr, &arg, "make", TRUE); |
|
if (arglen == 0) { |
|
condExpr -= 4; |
|
goto use_default; |
|
} |
|
} else if (istoken(condExpr, "exists", 6)) { |
|
/* |
|
* Use CondDoExists to evaluate the argument and |
|
* CondGetArg to extract the argument from the |
|
* 'function call'. |
|
*/ |
|
evalProc = CondDoExists; |
|
condExpr += 6; |
|
arglen = CondGetArg(&condExpr, &arg, "exists", TRUE); |
|
if (arglen == 0) { |
|
condExpr -= 6; |
|
goto use_default; |
|
} |
|
} else if (istoken(condExpr, "empty", 5)) { |
|
/* |
|
* Use Var_Parse to parse the spec in parens and return |
|
* True if the resulting string is empty. |
|
*/ |
|
int length; |
|
void *freeIt; |
|
char *val; |
|
|
|
condExpr += 5; |
|
|
|
for (arglen = 0; |
|
condExpr[arglen] != '(' && condExpr[arglen] != '\0'; |
|
arglen += 1) |
|
continue; |
|
|
|
if (condExpr[arglen] != '\0') { |
|
val = Var_Parse(&condExpr[arglen - 1], VAR_CMD, |
|
FALSE, &length, &freeIt); |
|
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 (freeIt) { |
|
free(freeIt); |
|
} |
|
/* |
|
* Advance condExpr to beyond the closing ). Note that |
|
* we subtract one from arglen + length b/c length |
|
* is calculated from condExpr[arglen - 1]. |
|
*/ |
|
condExpr += arglen + length - 1; |
|
} else { |
|
condExpr -= 5; |
|
goto use_default; |
|
} |
|
break; |
|
} else if (istoken(condExpr, "target", 6)) { |
|
/* |
|
* Use CondDoTarget to evaluate the argument and |
|
* CondGetArg to extract the argument from the |
|
* 'function call'. |
|
*/ |
|
evalProc = CondDoTarget; |
|
condExpr += 6; |
|
arglen = CondGetArg(&condExpr, &arg, "target", TRUE); |
|
if (arglen == 0) { |
|
condExpr -= 6; |
|
goto use_default; |
|
} |
|
} else if (istoken(condExpr, "commands", 8)) { |
|
/* |
|
* Use CondDoCommands to evaluate the argument and |
|
* CondGetArg to extract the argument from the |
|
* 'function call'. |
|
*/ |
|
evalProc = CondDoCommands; |
|
condExpr += 8; |
|
arglen = CondGetArg(&condExpr, &arg, "commands", TRUE); |
|
if (arglen == 0) { |
|
condExpr -= 8; |
|
goto use_default; |
|
} |
|
} else { |
|
/* |
|
* The symbol is itself the argument to the default |
|
* function. We advance condExpr to the end of the symbol |
|
* by hand (the next whitespace, closing paren or |
|
* binary operator) and set to invert the evaluation |
|
* function if condInvert is TRUE. |
|
*/ |
|
use_default: |
|
invert = condInvert; |
|
evalProc = condDefProc; |
|
arglen = CondGetArg(&condExpr, &arg, "", FALSE); |
|
} |
|
|
|
/* |
if (DEBUG(COND)) { |
* Evaluate the argument using the set function. If invert |
fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", |
* is TRUE, we invert the sense of the function. |
lhs, rhs, op); |
*/ |
} |
t = (!doEval || (* evalProc) (arglen, arg) ? |
/* |
(invert ? False : True) : |
* Null-terminate rhs and perform the comparison. |
(invert ? True : False)); |
* t is set to the result. |
if (arg) |
*/ |
free(arg); |
if (*op == '=') { |
break; |
t = strcmp(lhs, rhs) == 0; |
} |
} else { |
|
t = strcmp(lhs, rhs) != 0; |
} |
} |
} else { |
} else { |
t = condPushBack; |
/* |
condPushBack = None; |
* rhs is either a float or an integer. Convert both the |
|
* lhs and the rhs to a double and compare the two. |
|
*/ |
|
|
|
if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right)) |
|
goto do_string_compare; |
|
|
|
if (DEBUG(COND)) { |
|
fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left, |
|
right, op); |
|
} |
|
switch(op[0]) { |
|
case '!': |
|
if (op[1] != '=') { |
|
Parse_Error(PARSE_WARNING, |
|
"Unknown operator"); |
|
goto done; |
|
} |
|
t = (left != right); |
|
break; |
|
case '=': |
|
if (op[1] != '=') { |
|
Parse_Error(PARSE_WARNING, |
|
"Unknown operator"); |
|
goto done; |
|
} |
|
t = (left == right); |
|
break; |
|
case '<': |
|
if (op[1] == '=') { |
|
t = (left <= right); |
|
} else { |
|
t = (left < right); |
|
} |
|
break; |
|
case '>': |
|
if (op[1] == '=') { |
|
t = (left >= right); |
|
} else { |
|
t = (left > right); |
|
} |
|
break; |
|
} |
} |
} |
return (t); |
|
|
done: |
|
if (lhsFree) |
|
free(lhsFree); |
|
if (rhsFree) |
|
free(rhsFree); |
|
return t; |
} |
} |
|
|
|
static int |
|
get_mpt_arg(char **linePtr, char **argPtr, const char *func MAKE_ATTR_UNUSED) |
|
{ |
|
/* |
|
* Use Var_Parse to parse the spec in parens and return |
|
* TOK_TRUE if the resulting string is empty. |
|
*/ |
|
int length; |
|
void *freeIt; |
|
char *val; |
|
char *cp = *linePtr; |
|
|
|
/* We do all the work here and return the result as the length */ |
|
*argPtr = NULL; |
|
|
|
val = Var_Parse(cp - 1, VAR_CMD, FALSE, TRUE, &length, &freeIt); |
|
/* |
|
* Advance *linePtr to beyond the closing ). Note that |
|
* we subtract one because 'length' is calculated from 'cp - 1'. |
|
*/ |
|
*linePtr = cp - 1 + length; |
|
|
|
if (val == var_Error) { |
|
free(freeIt); |
|
return -1; |
|
} |
|
|
|
/* A variable is empty when it just contains spaces... 4/15/92, christos */ |
|
while (isspace(*(unsigned char *)val)) |
|
val++; |
|
|
|
/* |
|
* For consistency with the other functions we can't generate the |
|
* true/false here. |
|
*/ |
|
length = *val ? 2 : 1; |
|
if (freeIt) |
|
free(freeIt); |
|
return length; |
|
} |
|
|
|
static Boolean |
|
CondDoEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED) |
|
{ |
|
return arglen == 1; |
|
} |
|
|
|
static Token |
|
compare_function(Boolean doEval) |
|
{ |
|
static const struct fn_def { |
|
const char *fn_name; |
|
int fn_name_len; |
|
int (*fn_getarg)(char **, char **, const char *); |
|
Boolean (*fn_proc)(int, const char *); |
|
} fn_defs[] = { |
|
{ "defined", 7, CondGetArg, CondDoDefined }, |
|
{ "make", 4, CondGetArg, CondDoMake }, |
|
{ "exists", 6, CondGetArg, CondDoExists }, |
|
{ "empty", 5, get_mpt_arg, CondDoEmpty }, |
|
{ "target", 6, CondGetArg, CondDoTarget }, |
|
{ "commands", 8, CondGetArg, CondDoCommands }, |
|
{ NULL, 0, NULL, NULL }, |
|
}; |
|
const struct fn_def *fn_def; |
|
Token t; |
|
char *arg = NULL; |
|
int arglen; |
|
char *cp = condExpr; |
|
char *cp1; |
|
|
|
for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) { |
|
if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len)) |
|
continue; |
|
cp += fn_def->fn_name_len; |
|
/* There can only be whitespace before the '(' */ |
|
while (isspace(*(unsigned char *)cp)) |
|
cp++; |
|
if (*cp != '(') |
|
break; |
|
|
|
arglen = fn_def->fn_getarg(&cp, &arg, fn_def->fn_name); |
|
if (arglen <= 0) { |
|
condExpr = cp; |
|
return arglen < 0 ? TOK_ERROR : TOK_FALSE; |
|
} |
|
/* Evaluate the argument using the required function. */ |
|
t = !doEval || fn_def->fn_proc(arglen, arg); |
|
if (arg) |
|
free(arg); |
|
condExpr = cp; |
|
return t; |
|
} |
|
|
|
/* Push anything numeric through the compare expression */ |
|
cp = condExpr; |
|
if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0])) |
|
return compare_expression(doEval); |
|
|
|
/* |
|
* Most likely we have a naked token to apply the default function to. |
|
* However ".if a == b" gets here when the "a" is unquoted and doesn't |
|
* start with a '$'. This surprises people. |
|
* If what follows the function argument is a '=' or '!' then the syntax |
|
* would be invalid if we did "defined(a)" - so instead treat as an |
|
* expression. |
|
*/ |
|
arglen = CondGetArg(&cp, &arg, NULL); |
|
for (cp1 = cp; isspace(*(unsigned char *)cp1); cp1++) |
|
continue; |
|
if (*cp1 == '=' || *cp1 == '!') |
|
return compare_expression(doEval); |
|
condExpr = cp; |
|
|
|
/* |
|
* Evaluate the argument using the default function. |
|
* This path always treats .if as .ifdef. To get here the character |
|
* after .if must have been taken literally, so the argument cannot |
|
* be empty - even if it contained a variable expansion. |
|
*/ |
|
t = !doEval || if_info->defProc(arglen, arg) != if_info->doNot; |
|
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++; |
|
} |
|
condExpr++; |
|
return TOK_OR; |
|
|
|
case '&': |
|
if (condExpr[1] == '&') { |
|
condExpr++; |
|
} |
|
condExpr++; |
|
return TOK_AND; |
|
|
|
case '!': |
|
condExpr++; |
|
return TOK_NOT; |
|
|
|
case '#': |
|
case '\n': |
|
case '\0': |
|
return TOK_EOF; |
|
|
|
case '"': |
|
case '$': |
|
return compare_expression(doEval); |
|
|
|
default: |
|
return compare_function(doEval); |
|
} |
|
} |
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* CondT -- |
* CondT -- |
* Parse a single term in the expression. This consists of a terminal |
* Parse a single term in the expression. This consists of a terminal |
* symbol or Not and a terminal symbol (not including the binary |
* symbol or TOK_NOT and a terminal symbol (not including the binary |
* operators): |
* operators): |
* T -> defined(variable) | make(target) | exists(file) | symbol |
* T -> defined(variable) | make(target) | exists(file) | symbol |
* T -> ! T | ( E ) |
* T -> ! T | ( E ) |
* |
* |
* Results: |
* Results: |
* True, False or Err. |
* TOK_TRUE, TOK_FALSE or TOK_ERROR. |
* |
* |
* Side Effects: |
* Side Effects: |
* Tokens are consumed. |
* Tokens are consumed. |
Line 1032 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 1066 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 1079 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 1112 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 1125 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 1171 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; |
} |
} |
|
|
|
|
|
|
* 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) |
|
|
* Side Effects: |
* Side Effects: |
* None. |
* None. |
* |
* |
|
* Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order |
|
* to detect splurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF) |
|
* otherwise .else could be treated as '.elif 1'. |
|
* |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
int |
int |
Cond_Eval(char *line) |
Cond_Eval(char *line) |
{ |
{ |
struct If *ifp; |
#define MAXIF 128 /* maximum depth of .if'ing */ |
Boolean isElse; |
#define MAXIF_BUMP 32 /* how much to grow by */ |
Boolean value = FALSE; |
enum if_states { |
|
IF_ACTIVE, /* .if or .elif part active */ |
|
ELSE_ACTIVE, /* .else part active */ |
|
SEARCH_FOR_ELIF, /* searching for .elif/else to execute */ |
|
SKIP_TO_ELSE, /* has been true, but not seen '.else' */ |
|
SKIP_TO_ENDIF /* nothing else to execute */ |
|
}; |
|
static enum if_states *cond_state = NULL; |
|
static unsigned int max_if_depth = MAXIF; |
|
|
|
const struct If *ifp; |
|
Boolean isElif; |
|
Boolean value; |
int level; /* Level at which to report errors. */ |
int level; /* Level at which to report errors. */ |
|
enum if_states state; |
|
|
level = PARSE_FATAL; |
level = PARSE_FATAL; |
|
if (!cond_state) { |
for (line++; *line == ' ' || *line == '\t'; line++) { |
cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state)); |
continue; |
cond_state[0] = IF_ACTIVE; |
} |
} |
|
/* skip leading character (the '.') and any whitespace */ |
|
for (line++; *line == ' ' || *line == '\t'; line++) |
|
continue; |
|
|
/* |
/* Find what type of if we're dealing with. */ |
* Find what type of if we're dealing with. The result is left |
if (line[0] == 'e') { |
* in ifp and isElse is set TRUE if it's an elif line. |
if (line[1] != 'l') { |
*/ |
if (!istoken(line + 1, "ndif", 4)) |
if (line[0] == 'e' && line[1] == 'l') { |
return COND_INVALID; |
line += 2; |
/* End of conditional section */ |
isElse = TRUE; |
if (cond_depth == cond_min_depth) { |
} else if (istoken(line, "endif", 5)) { |
|
/* |
|
* End of a conditional section. If skipIfLevel is non-zero, that |
|
* conditional was skipped, so lines following it should also be |
|
* skipped. Hence, we return COND_SKIP. Otherwise, the conditional |
|
* was read so succeeding lines should be parsed (think about it...) |
|
* so we return COND_PARSE, unless this endif isn't paired with |
|
* a decent if. |
|
*/ |
|
finalElse[condTop][skipIfLevel] = FALSE; |
|
if (skipIfLevel != 0) { |
|
skipIfLevel -= 1; |
|
return (COND_SKIP); |
|
} else { |
|
if (condTop == MAXIF) { |
|
Parse_Error(level, "if-less endif"); |
Parse_Error(level, "if-less endif"); |
return (COND_INVALID); |
return COND_PARSE; |
} else { |
|
skipLine = FALSE; |
|
condTop += 1; |
|
return (COND_PARSE); |
|
} |
} |
|
/* Return state for previous conditional */ |
|
cond_depth--; |
|
return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; |
} |
} |
} else { |
|
isElse = FALSE; |
/* Quite likely this is 'else' or 'elif' */ |
} |
line += 2; |
|
if (istoken(line, "se", 2)) { |
|
/* It is else... */ |
|
if (cond_depth == cond_min_depth) { |
|
Parse_Error(level, "if-less else"); |
|
return COND_PARSE; |
|
} |
|
|
|
state = cond_state[cond_depth]; |
|
switch (state) { |
|
case SEARCH_FOR_ELIF: |
|
state = ELSE_ACTIVE; |
|
break; |
|
case ELSE_ACTIVE: |
|
case SKIP_TO_ENDIF: |
|
Parse_Error(PARSE_WARNING, "extra else"); |
|
/* FALLTHROUGH */ |
|
default: |
|
case IF_ACTIVE: |
|
case SKIP_TO_ELSE: |
|
state = SKIP_TO_ENDIF; |
|
break; |
|
} |
|
cond_state[cond_depth] = state; |
|
return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; |
|
} |
|
/* Assume for now it is an elif */ |
|
isElif = TRUE; |
|
} else |
|
isElif = FALSE; |
|
|
|
if (line[0] != 'i' || line[1] != 'f') |
|
/* Not an ifxxx or elifxxx line */ |
|
return COND_INVALID; |
|
|
/* |
/* |
* Figure out what sort of conditional it is -- what its default |
* Figure out what sort of conditional it is -- what its default |
* function is, etc. -- by looking in the table of valid "ifs" |
* function is, etc. -- by looking in the table of valid "ifs" |
*/ |
*/ |
for (ifp = ifs; ifp->form != NULL; ifp++) { |
line += 2; |
|
for (ifp = ifs; ; ifp++) { |
|
if (ifp->form == NULL) |
|
return COND_INVALID; |
if (istoken(ifp->form, line, ifp->formlen)) { |
if (istoken(ifp->form, line, ifp->formlen)) { |
|
line += ifp->formlen; |
break; |
break; |
} |
} |
} |
} |
|
|
if (ifp->form == NULL) { |
/* Now we know what sort of 'if' it is... */ |
/* |
|
* Nothing fit. If the first word on the line is actually |
if (isElif) { |
* "else", it's a valid conditional whose value is the inverse |
if (cond_depth == cond_min_depth) { |
* of the previous if we parsed. |
Parse_Error(level, "if-less elif"); |
*/ |
return COND_PARSE; |
if (isElse && istoken(line, "se", 2)) { |
} |
if (finalElse[condTop][skipIfLevel]) { |
state = cond_state[cond_depth]; |
Parse_Error(PARSE_WARNING, "extra else"); |
if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) { |
} else { |
Parse_Error(PARSE_WARNING, "extra elif"); |
finalElse[condTop][skipIfLevel] = TRUE; |
cond_state[cond_depth] = SKIP_TO_ENDIF; |
} |
return COND_SKIP; |
if (condTop == MAXIF) { |
} |
Parse_Error(level, "if-less else"); |
if (state != SEARCH_FOR_ELIF) { |
return (COND_INVALID); |
/* Either just finished the 'true' block, or already SKIP_TO_ELSE */ |
} else if (skipIfLevel == 0) { |
cond_state[cond_depth] = SKIP_TO_ELSE; |
value = !condStack[condTop]; |
return COND_SKIP; |
} else { |
|
return (COND_SKIP); |
|
} |
|
} else { |
|
/* |
|
* Not a valid conditional type. No error... |
|
*/ |
|
return (COND_INVALID); |
|
} |
} |
} else { |
} else { |
if (isElse) { |
/* Normal .if */ |
if (condTop == MAXIF) { |
if (cond_depth + 1 >= max_if_depth) { |
Parse_Error(level, "if-less elif"); |
|
return (COND_INVALID); |
|
} else if (skipIfLevel != 0) { |
|
/* |
|
* If skipping this conditional, just ignore the whole thing. |
|
* If we don't, the user might be employing a variable that's |
|
* undefined, for which there's an enclosing ifdef that |
|
* we're skipping... |
|
*/ |
|
return(COND_SKIP); |
|
} |
|
} else if (skipLine) { |
|
/* |
/* |
* Don't even try to evaluate a conditional that's not an else if |
* This is rare, but not impossible. |
* we're skipping things... |
* In meta mode, dirdeps.mk (only runs at level 0) |
|
* can need more than the default. |
*/ |
*/ |
skipIfLevel += 1; |
max_if_depth += MAXIF_BUMP; |
if (skipIfLevel >= MAXIF) { |
cond_state = bmake_realloc(cond_state, max_if_depth * |
Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF); |
sizeof(*cond_state)); |
return (COND_INVALID); |
|
} |
|
finalElse[condTop][skipIfLevel] = FALSE; |
|
return(COND_SKIP); |
|
} |
} |
|
state = cond_state[cond_depth]; |
/* |
cond_depth++; |
* Initialize file-global variables for parsing |
if (state > ELSE_ACTIVE) { |
*/ |
/* If we aren't parsing the data, treat as always false */ |
condDefProc = ifp->defProc; |
cond_state[cond_depth] = SKIP_TO_ELSE; |
condInvert = ifp->doNot; |
return COND_SKIP; |
|
|
line += ifp->formlen; |
|
if (Cond_EvalExpression(0, line, &value, 1) == COND_INVALID) |
|
return COND_INVALID; |
|
} |
|
if (!isElse) { |
|
condTop -= 1; |
|
if (condTop < 0) { |
|
/* |
|
* This is the one case where we can definitely proclaim a fatal |
|
* error. If we don't, we're hosed. |
|
*/ |
|
Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF); |
|
return (COND_INVALID); |
|
} |
} |
finalElse[condTop][skipIfLevel] = FALSE; |
|
} else if ((skipIfLevel != 0) || condStack[condTop]) { |
|
/* |
|
* If this is an else-type conditional, it should only take effect |
|
* if its corresponding if was evaluated and FALSE. If its if was |
|
* TRUE or skipped, we return COND_SKIP (and start skipping in case |
|
* we weren't already), leaving the stack unmolested so later elif's |
|
* don't screw up... |
|
*/ |
|
skipLine = TRUE; |
|
return (COND_SKIP); |
|
} |
} |
|
|
condStack[condTop] = value; |
/* And evaluate the conditional expresssion */ |
skipLine = !value; |
if (Cond_EvalExpression(ifp, line, &value, 1, TRUE) == COND_INVALID) { |
return (value ? COND_PARSE : COND_SKIP); |
/* Syntax error in conditional, error message already output. */ |
|
/* Skip everything to matching .endif */ |
|
cond_state[cond_depth] = SKIP_TO_ELSE; |
|
return COND_SKIP; |
|
} |
|
|
|
if (!value) { |
|
cond_state[cond_depth] = SEARCH_FOR_ELIF; |
|
return COND_SKIP; |
|
} |
|
cond_state[cond_depth] = IF_ACTIVE; |
|
return COND_PARSE; |
} |
} |
|
|
|
|
Line 1402 Cond_Eval(char *line) |
|
Line 1411 Cond_Eval(char *line) |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
Cond_End(void) |
Cond_restore_depth(unsigned int saved_depth) |
{ |
{ |
if (condTop != MAXIF) { |
int open_conds = cond_depth - cond_min_depth; |
Parse_Error(PARSE_FATAL, "%d open conditional%s", MAXIF-condTop, |
|
MAXIF-condTop == 1 ? "" : "s"); |
if (open_conds != 0 || saved_depth > cond_depth) { |
|
Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds, |
|
open_conds == 1 ? "" : "s"); |
|
cond_depth = cond_min_depth; |
} |
} |
condTop = MAXIF; |
|
|
cond_min_depth = saved_depth; |
|
} |
|
|
|
unsigned int |
|
Cond_save_depth(void) |
|
{ |
|
int depth = cond_min_depth; |
|
|
|
cond_min_depth = cond_depth; |
|
return depth; |
} |
} |