aboutsummaryrefslogtreecommitdiff
path: root/contrib/bmake/cond.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bmake/cond.c')
-rw-r--r--contrib/bmake/cond.c655
1 files changed, 335 insertions, 320 deletions
diff --git a/contrib/bmake/cond.c b/contrib/bmake/cond.c
index 1a8aba637fe9..8f36fda22f12 100644
--- a/contrib/bmake/cond.c
+++ b/contrib/bmake/cond.c
@@ -1,4 +1,4 @@
-/* $NetBSD: cond.c,v 1.235 2021/01/10 21:20:46 rillig Exp $ */
+/* $NetBSD: cond.c,v 1.256 2021/02/05 05:15:12 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -95,53 +95,65 @@
#include "dir.h"
/* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: cond.c,v 1.235 2021/01/10 21:20:46 rillig Exp $");
+MAKE_RCSID("$NetBSD: cond.c,v 1.256 2021/02/05 05:15:12 rillig Exp $");
/*
* The parsing of conditional expressions is based on this grammar:
- * E -> F || E
- * E -> F
- * F -> T && F
- * F -> T
- * T -> defined(variable)
- * T -> make(target)
- * T -> exists(file)
- * T -> empty(varspec)
- * T -> target(name)
- * T -> commands(name)
- * T -> symbol
- * T -> $(varspec) op value
- * T -> $(varspec) == "string"
- * T -> $(varspec) != "string"
- * T -> "string"
- * T -> ( E )
- * T -> ! T
- * op -> == | != | > | < | >= | <=
+ * Or -> And '||' Or
+ * Or -> And
+ * And -> Term '&&' And
+ * And -> Term
+ * Term -> Function '(' Argument ')'
+ * Term -> Leaf Operator Leaf
+ * Term -> Leaf
+ * Term -> '(' Or ')'
+ * Term -> '!' Term
+ * Leaf -> "string"
+ * Leaf -> Number
+ * Leaf -> VariableExpression
+ * Leaf -> Symbol
+ * Operator -> '==' | '!=' | '>' | '<' | '>=' | '<='
*
- * 'symbol' is some other symbol to which the default function is applied.
+ * 'Symbol' is an unquoted string literal to which the default function is
+ * applied.
*
* The tokens are scanned by CondToken, which returns:
- * TOK_AND for '&' or '&&'
- * TOK_OR for '|' or '||'
+ * TOK_AND for '&&'
+ * TOK_OR for '||'
* TOK_NOT for '!'
* TOK_LPAREN for '('
* TOK_RPAREN for ')'
+ *
* Other terminal symbols are evaluated using either the default function or
* the function given in the terminal, they return 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 (CondParser_Expr, CondParser_Factor and
- * CondParser_Term) return either TOK_FALSE, TOK_TRUE, or TOK_ERROR on error.
*/
typedef enum Token {
- TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT,
+ TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT,
TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
} Token;
+typedef enum CondResult {
+ CR_FALSE, CR_TRUE, CR_ERROR
+} CondResult;
+
+typedef enum ComparisonOp {
+ LT, LE, GT, GE, EQ, NE
+} ComparisonOp;
+
typedef struct CondParser {
- const struct If *if_info; /* Info for current statement */
+
+ /*
+ * The plain '.if ${VAR}' evaluates to true if the value of the
+ * expression has length > 0. The other '.if' variants delegate
+ * to evalBare instead.
+ */
+ Boolean plain;
+
+ /* The function to apply on unquoted bare words. */
+ Boolean (*evalBare)(size_t, const char *);
+ Boolean negateEvalBare;
+
const char *p; /* The remaining condition to parse */
Token curr; /* Single push-back token used in parsing */
@@ -154,11 +166,13 @@ typedef struct CondParser {
Boolean printedError;
} CondParser;
-static Token CondParser_Expr(CondParser *par, Boolean);
+static CondResult CondParser_Or(CondParser *par, Boolean);
static unsigned int cond_depth = 0; /* current .if nesting level */
static unsigned int cond_min_depth = 0; /* depth at makefile open */
+static const char *opname[] = { "<", "<=", ">", ">=", "==", "!=" };
+
/*
* Indicate when we should be strict about lhs of comparisons.
* In strict mode, the lhs must be a variable expression or a string literal
@@ -214,7 +228,7 @@ CondParser_SkipWhitespace(CondParser *par)
* Return the length of the argument, or 0 on error.
*/
static size_t
-ParseFuncArg(const char **pp, Boolean doEval, const char *func,
+ParseFuncArg(CondParser *par, const char **pp, Boolean doEval, const char *func,
char **out_arg)
{
const char *p = *pp;
@@ -254,7 +268,7 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
? VARE_WANTRES | VARE_UNDEFERR
: VARE_NONE;
FStr nestedVal;
- (void)Var_Parse(&p, VAR_CMDLINE, eflags, &nestedVal);
+ (void)Var_Parse(&p, SCOPE_CMDLINE, eflags, &nestedVal);
/* TODO: handle errors */
Buf_AddStr(&argBuf, nestedVal.str);
FStr_Done(&nestedVal);
@@ -268,16 +282,15 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
p++;
}
- *out_arg = Buf_GetAll(&argBuf, &argLen);
- Buf_Destroy(&argBuf, FALSE);
+ argLen = argBuf.len;
+ *out_arg = Buf_DoneData(&argBuf);
cpp_skip_hspace(&p);
if (func != NULL && *p++ != ')') {
- Parse_Error(PARSE_WARNING,
- "Missing closing parenthesis for %s()",
- func);
- /* The PARSE_FATAL follows in CondEvalExpression. */
+ Parse_Error(PARSE_FATAL,
+ "Missing closing parenthesis for %s()", func);
+ par->printedError = TRUE;
return 0;
}
@@ -290,7 +303,7 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
static Boolean
FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
- FStr value = Var_Value(arg, VAR_CMDLINE);
+ FStr value = Var_Value(SCOPE_CMDLINE, arg);
Boolean result = value.str != NULL;
FStr_Done(&value);
return result;
@@ -390,7 +403,70 @@ is_separator(char ch)
return ch == '\0' || ch_isspace(ch) || strchr("!=><)", ch) != NULL;
}
-/*-
+/*
+ * In a quoted or unquoted string literal or a number, parse a variable
+ * expression.
+ *
+ * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX}
+ */
+static Boolean
+CondParser_StringExpr(CondParser *par, const char *start,
+ Boolean const doEval, Boolean const quoted,
+ Buffer *buf, FStr *const inout_str)
+{
+ VarEvalFlags eflags;
+ const char *nested_p;
+ Boolean atStart;
+ VarParseResult parseResult;
+
+ /* if we are in quotes, an undefined variable is ok */
+ eflags = doEval && !quoted ? VARE_WANTRES | VARE_UNDEFERR
+ : doEval ? VARE_WANTRES
+ : VARE_NONE;
+
+ nested_p = par->p;
+ atStart = nested_p == start;
+ parseResult = Var_Parse(&nested_p, SCOPE_CMDLINE, eflags, inout_str);
+ /* TODO: handle errors */
+ if (inout_str->str == var_Error) {
+ if (parseResult == VPR_ERR) {
+ /*
+ * FIXME: Even if an error occurs, there is no
+ * guarantee that it is reported.
+ *
+ * See cond-token-plain.mk $$$$$$$$.
+ */
+ par->printedError = TRUE;
+ }
+ /*
+ * XXX: Can there be any situation in which a returned
+ * var_Error requires freeIt?
+ */
+ FStr_Done(inout_str);
+ /*
+ * Even if !doEval, we still report syntax errors, which is
+ * what getting var_Error back with !doEval means.
+ */
+ *inout_str = FStr_InitRefer(NULL);
+ return FALSE;
+ }
+ par->p = nested_p;
+
+ /*
+ * If the '$' started the string literal (which means no quotes), and
+ * the variable expression is followed by a space, looks like a
+ * comparison operator or is the end of the expression, we are done.
+ */
+ if (atStart && is_separator(par->p[0]))
+ return FALSE;
+
+ Buf_AddStr(buf, inout_str->str);
+ FStr_Done(inout_str);
+ *inout_str = FStr_InitRefer(NULL); /* not finished yet */
+ return TRUE;
+}
+
+/*
* Parse a string from a variable reference or an optionally quoted
* string. This is called for the lhs and rhs of string comparisons.
*
@@ -399,19 +475,14 @@ is_separator(char ch)
* Sets out_quoted if the string was quoted.
* Sets out_freeIt.
*/
-/* coverity:[+alloc : arg-*4] */
static void
CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
FStr *out_str, Boolean *out_quoted)
{
Buffer buf;
FStr str;
- Boolean atStart;
- const char *nested_p;
Boolean quoted;
const char *start;
- VarEvalFlags eflags;
- VarParseResult parseResult;
Buf_Init(&buf);
str = FStr_InitRefer(NULL);
@@ -430,12 +501,10 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
}
continue;
case '"':
- if (quoted) {
- par->p++; /* skip the closing quote */
- goto got_str;
- }
- Buf_AddByte(&buf, par->p[0]); /* likely? */
par->p++;
+ if (quoted)
+ goto got_str; /* skip the closing quote */
+ Buf_AddByte(&buf, '"');
continue;
case ')': /* see is_separator */
case '!':
@@ -450,47 +519,9 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
par->p++;
continue;
case '$':
- /* if we are in quotes, an undefined variable is ok */
- eflags =
- doEval && !quoted ? VARE_WANTRES | VARE_UNDEFERR :
- doEval ? VARE_WANTRES :
- VARE_NONE;
-
- nested_p = par->p;
- atStart = nested_p == start;
- parseResult = Var_Parse(&nested_p, VAR_CMDLINE, eflags,
- &str);
- /* TODO: handle errors */
- if (str.str == var_Error) {
- if (parseResult == VPR_ERR)
- par->printedError = TRUE;
- /*
- * XXX: Can there be any situation in which
- * a returned var_Error requires freeIt?
- */
- FStr_Done(&str);
- /*
- * Even if !doEval, we still report syntax
- * errors, which is what getting var_Error
- * back with !doEval means.
- */
- str = FStr_InitRefer(NULL);
- goto cleanup;
- }
- par->p = nested_p;
-
- /*
- * If the '$' started the string literal (which means
- * no quotes), and the variable expression is followed
- * by a space, looks like a comparison operator or is
- * the end of the expression, we are done.
- */
- if (atStart && is_separator(par->p[0]))
+ if (!CondParser_StringExpr(par,
+ start, doEval, quoted, &buf, &str))
goto cleanup;
-
- Buf_AddStr(&buf, str.str);
- FStr_Done(&str);
- str = FStr_InitRefer(NULL); /* not finished yet */
continue;
default:
if (strictLHS && !quoted && *start != '$' &&
@@ -508,38 +539,17 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
}
}
got_str:
- str = FStr_InitOwn(Buf_GetAll(&buf, NULL));
+ str = FStr_InitOwn(buf.data);
cleanup:
- Buf_Destroy(&buf, FALSE);
+ Buf_DoneData(&buf);
*out_str = str;
}
-struct If {
- const char *form; /* Form of if */
- size_t formlen; /* Length of form */
- Boolean doNot; /* TRUE if default function should be negated */
- /* The default function to apply on unquoted bare words. */
- Boolean (*defProc)(size_t, const char *);
-};
-
-/* The different forms of .if directives. */
-static const struct If ifs[] = {
- { "def", 3, FALSE, FuncDefined },
- { "ndef", 4, TRUE, FuncDefined },
- { "make", 4, FALSE, FuncMake },
- { "nmake", 5, TRUE, FuncMake },
- { "", 0, FALSE, FuncDefined },
- { NULL, 0, FALSE, NULL }
-};
-enum {
- PLAIN_IF_INDEX = 4
-};
-
static Boolean
-If_Eval(const struct If *if_info, const char *arg, size_t arglen)
+If_Eval(const CondParser *par, const char *arg, size_t arglen)
{
- Boolean res = if_info->defProc(arglen, arg);
- return if_info->doNot ? !res : res;
+ Boolean res = par->evalBare(arglen, arg);
+ return par->negateEvalBare ? !res : res;
}
/*
@@ -563,69 +573,96 @@ EvalNotEmpty(CondParser *par, const char *value, Boolean quoted)
* the evaluation function from that .if variant, which would test
* whether a variable of the given name were defined. */
/* XXX: Whitespace should count as empty, just as in ParseEmptyArg. */
- if (par->if_info->form[0] == '\0')
+ if (par->plain)
return value[0] != '\0';
- /* For the other variants of .ifxxx ${...}, use its default function. */
- return If_Eval(par->if_info, value, strlen(value));
+ return If_Eval(par, value, strlen(value));
}
/* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
-static Token
-EvalCompareNum(double lhs, const char *op, double rhs)
+static Boolean
+EvalCompareNum(double lhs, ComparisonOp op, double rhs)
{
- DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, op);
-
- switch (op[0]) {
- case '!':
- if (op[1] != '=') {
- Parse_Error(PARSE_WARNING, "Unknown operator");
- /* The PARSE_FATAL follows in CondEvalExpression. */
- return TOK_ERROR;
- }
- return ToToken(lhs != rhs);
- case '=':
- if (op[1] != '=') {
- Parse_Error(PARSE_WARNING, "Unknown operator");
- /* The PARSE_FATAL follows in CondEvalExpression. */
- return TOK_ERROR;
- }
- return ToToken(lhs == rhs);
- case '<':
- return ToToken(op[1] == '=' ? lhs <= rhs : lhs < rhs);
- case '>':
- return ToToken(op[1] == '=' ? lhs >= rhs : lhs > rhs);
+ DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, opname[op]);
+
+ switch (op) {
+ case LT:
+ return lhs < rhs;
+ case LE:
+ return lhs <= rhs;
+ case GT:
+ return lhs > rhs;
+ case GE:
+ return lhs >= rhs;
+ case NE:
+ return lhs != rhs;
+ default:
+ return lhs == rhs;
}
- return TOK_ERROR;
}
static Token
-EvalCompareStr(const char *lhs, const char *op, const char *rhs)
+EvalCompareStr(CondParser *par, const char *lhs,
+ ComparisonOp op, const char *rhs)
{
- if (!((op[0] == '!' || op[0] == '=') && op[1] == '=')) {
- Parse_Error(PARSE_WARNING,
- "String comparison operator "
- "must be either == or !=");
- /* The PARSE_FATAL follows in CondEvalExpression. */
+ if (op != EQ && op != NE) {
+ Parse_Error(PARSE_FATAL,
+ "String comparison operator must be either == or !=");
+ par->printedError = TRUE;
return TOK_ERROR;
}
- DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", lhs, rhs, op);
- return ToToken((*op == '=') == (strcmp(lhs, rhs) == 0));
+ DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
+ lhs, rhs, opname[op]);
+ return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0));
}
/* Evaluate a comparison, such as "${VAR} == 12345". */
static Token
-EvalCompare(const char *lhs, Boolean lhsQuoted, const char *op,
- const char *rhs, Boolean rhsQuoted)
+EvalCompare(CondParser *par, const char *lhs, Boolean lhsQuoted,
+ ComparisonOp op, const char *rhs, Boolean rhsQuoted)
{
double left, right;
if (!rhsQuoted && !lhsQuoted)
if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
- return EvalCompareNum(left, op, right);
+ return ToToken(EvalCompareNum(left, op, right));
+
+ return EvalCompareStr(par, lhs, op, rhs);
+}
- return EvalCompareStr(lhs, op, rhs);
+static Boolean
+CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
+{
+ const char *p = par->p;
+
+ if (p[0] == '<' && p[1] == '=') {
+ *out_op = LE;
+ goto length_2;
+ } else if (p[0] == '<') {
+ *out_op = LT;
+ goto length_1;
+ } else if (p[0] == '>' && p[1] == '=') {
+ *out_op = GE;
+ goto length_2;
+ } else if (p[0] == '>') {
+ *out_op = GT;
+ goto length_1;
+ } else if (p[0] == '=' && p[1] == '=') {
+ *out_op = EQ;
+ goto length_2;
+ } else if (p[0] == '!' && p[1] == '=') {
+ *out_op = NE;
+ goto length_2;
+ }
+ return FALSE;
+
+length_2:
+ par->p = p + 2;
+ return TRUE;
+length_1:
+ par->p = p + 1;
+ return TRUE;
}
/*
@@ -641,7 +678,7 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
{
Token t = TOK_ERROR;
FStr lhs, rhs;
- const char *op;
+ ComparisonOp op;
Boolean lhsQuoted, rhsQuoted;
/*
@@ -654,18 +691,7 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
CondParser_SkipWhitespace(par);
- op = par->p;
- switch (par->p[0]) {
- case '!':
- case '=':
- case '<':
- case '>':
- if (par->p[1] == '=')
- par->p += 2;
- else
- par->p++;
- break;
- default:
+ if (!CondParser_ComparisonOp(par, &op)) {
/* Unknown operator, compare against an empty string or 0. */
t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted));
goto done_lhs;
@@ -674,9 +700,9 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
CondParser_SkipWhitespace(par);
if (par->p[0] == '\0') {
- Parse_Error(PARSE_WARNING,
- "Missing right-hand-side of operator");
- /* The PARSE_FATAL follows in CondEvalExpression. */
+ Parse_Error(PARSE_FATAL,
+ "Missing right-hand-side of operator '%s'", opname[op]);
+ par->printedError = TRUE;
goto done_lhs;
}
@@ -689,7 +715,7 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
goto done_rhs;
}
- t = EvalCompare(lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
+ t = EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
done_rhs:
FStr_Done(&rhs);
@@ -704,8 +730,9 @@ done_lhs:
*/
/*ARGSUSED*/
static size_t
-ParseEmptyArg(const char **pp, Boolean doEval,
- const char *func MAKE_ATTR_UNUSED, char **out_arg)
+ParseEmptyArg(CondParser *par MAKE_ATTR_UNUSED, const char **pp,
+ Boolean doEval, const char *func MAKE_ATTR_UNUSED,
+ char **out_arg)
{
FStr val;
size_t magic_res;
@@ -714,7 +741,7 @@ ParseEmptyArg(const char **pp, Boolean doEval,
*out_arg = NULL;
(*pp)--; /* Make (*pp)[1] point to the '('. */
- (void)Var_Parse(pp, VAR_CMDLINE, doEval ? VARE_WANTRES : VARE_NONE,
+ (void)Var_Parse(pp, SCOPE_CMDLINE, doEval ? VARE_WANTRES : VARE_NONE,
&val);
/* TODO: handle errors */
/* If successful, *pp points beyond the closing ')' now. */
@@ -753,8 +780,8 @@ CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
static const struct fn_def {
const char *fn_name;
size_t fn_name_len;
- size_t (*fn_parse)(const char **, Boolean, const char *,
- char **);
+ size_t (*fn_parse)(CondParser *, const char **, Boolean,
+ const char *, char **);
Boolean (*fn_eval)(size_t, const char *);
} fns[] = {
{ "defined", 7, ParseFuncArg, FuncDefined },
@@ -779,7 +806,7 @@ CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
if (*cp != '(')
break;
- arglen = fn->fn_parse(&cp, doEval, fn->fn_name, &arg);
+ arglen = fn->fn_parse(par, &cp, doEval, fn->fn_name, &arg);
if (arglen == 0 || arglen == (size_t)-1) {
par->p = cp;
*out_token = arglen == 0 ? TOK_FALSE : TOK_ERROR;
@@ -825,7 +852,7 @@ CondParser_LeafToken(CondParser *par, Boolean doEval)
* syntax would be invalid if we did "defined(a)" - so instead treat
* as an expression.
*/
- arglen = ParseFuncArg(&cp, doEval, NULL, &arg);
+ arglen = ParseFuncArg(par, &cp, doEval, NULL, &arg);
cp1 = cp;
cpp_skip_whitespace(&cp1);
if (*cp1 == '=' || *cp1 == '!')
@@ -838,7 +865,7 @@ CondParser_LeafToken(CondParser *par, Boolean doEval)
* after .if must have been taken literally, so the argument cannot
* be empty - even if it contained a variable expansion.
*/
- t = ToToken(!doEval || If_Eval(par->if_info, arg, arglen));
+ t = ToToken(!doEval || If_Eval(par, arg, arglen));
free(arg);
return t;
}
@@ -909,148 +936,113 @@ CondParser_Token(CondParser *par, Boolean doEval)
}
/*
- * Parse a single term in the expression. This consists of a terminal symbol
- * or TOK_NOT and a term (not including the binary operators):
- *
- * T -> defined(variable) | make(target) | exists(file) | symbol
- * T -> ! T | ( E )
- *
- * Results:
- * TOK_TRUE, TOK_FALSE or TOK_ERROR.
+ * Term -> '(' Or ')'
+ * Term -> '!' Term
+ * Term -> Leaf Operator Leaf
+ * Term -> Leaf
*/
-static Token
+static CondResult
CondParser_Term(CondParser *par, Boolean doEval)
{
+ CondResult res;
Token t;
t = CondParser_Token(par, doEval);
+ if (t == TOK_TRUE)
+ return CR_TRUE;
+ if (t == TOK_FALSE)
+ return CR_FALSE;
+
+ if (t == TOK_LPAREN) {
+ res = CondParser_Or(par, doEval);
+ if (res == CR_ERROR)
+ return CR_ERROR;
+ if (CondParser_Token(par, doEval) != TOK_RPAREN)
+ return CR_ERROR;
+ return res;
+ }
- if (t == TOK_EOF) {
- /*
- * If we reached the end of the expression, the expression
- * is malformed...
- */
- t = TOK_ERROR;
- } else if (t == TOK_LPAREN) {
- /*
- * T -> ( E )
- */
- t = CondParser_Expr(par, doEval);
- if (t != TOK_ERROR) {
- if (CondParser_Token(par, doEval) != TOK_RPAREN) {
- t = TOK_ERROR;
- }
- }
- } else if (t == TOK_NOT) {
- t = CondParser_Term(par, doEval);
- if (t == TOK_TRUE) {
- t = TOK_FALSE;
- } else if (t == TOK_FALSE) {
- t = TOK_TRUE;
- }
+ if (t == TOK_NOT) {
+ res = CondParser_Term(par, doEval);
+ if (res == CR_TRUE)
+ res = CR_FALSE;
+ else if (res == CR_FALSE)
+ res = CR_TRUE;
+ return res;
}
- return t;
+
+ return CR_ERROR;
}
/*
- * Parse a conjunctive factor (nice name, wot?)
- *
- * F -> T && F | T
- *
- * Results:
- * TOK_TRUE, TOK_FALSE or TOK_ERROR
+ * And -> Term '&&' And
+ * And -> Term
*/
-static Token
-CondParser_Factor(CondParser *par, Boolean doEval)
+static CondResult
+CondParser_And(CondParser *par, Boolean doEval)
{
- Token l, o;
-
- l = CondParser_Term(par, doEval);
- if (l != TOK_ERROR) {
- o = CondParser_Token(par, doEval);
-
- if (o == TOK_AND) {
- /*
- * F -> T && F
- *
- * 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). If T is TOK_TRUE, the result
- * is the r.h.s., be it a TOK_ERROR or not.
- */
- if (l == TOK_TRUE) {
- l = CondParser_Factor(par, doEval);
- } else {
- (void)CondParser_Factor(par, FALSE);
- }
- } else {
- /*
- * F -> T
- */
- CondParser_PushBack(par, o);
- }
+ CondResult res;
+ Token op;
+
+ res = CondParser_Term(par, doEval);
+ if (res == CR_ERROR)
+ return CR_ERROR;
+
+ op = CondParser_Token(par, doEval);
+ if (op == TOK_AND) {
+ if (res == CR_TRUE)
+ return CondParser_And(par, doEval);
+ if (CondParser_And(par, FALSE) == CR_ERROR)
+ return CR_ERROR;
+ return res;
}
- return l;
+
+ CondParser_PushBack(par, op);
+ return res;
}
/*
- * Main expression production.
- *
- * E -> F || E | F
- *
- * Results:
- * TOK_TRUE, TOK_FALSE or TOK_ERROR.
+ * Or -> And '||' Or
+ * Or -> And
*/
-static Token
-CondParser_Expr(CondParser *par, Boolean doEval)
+static CondResult
+CondParser_Or(CondParser *par, Boolean doEval)
{
- Token l, o;
-
- l = CondParser_Factor(par, doEval);
- if (l != TOK_ERROR) {
- o = CondParser_Token(par, doEval);
-
- if (o == TOK_OR) {
- /*
- * E -> F || E
- *
- * A similar thing occurs for ||, except that here
- * we make sure the l.h.s. is TOK_FALSE before we
- * bother to evaluate the r.h.s. Once again, if l
- * is TOK_FALSE, the result is the r.h.s. and once
- * again if l is TOK_TRUE, we parse the r.h.s. to
- * throw it away.
- */
- if (l == TOK_FALSE) {
- l = CondParser_Expr(par, doEval);
- } else {
- (void)CondParser_Expr(par, FALSE);
- }
- } else {
- /*
- * E -> F
- */
- CondParser_PushBack(par, o);
- }
+ CondResult res;
+ Token op;
+
+ res = CondParser_And(par, doEval);
+ if (res == CR_ERROR)
+ return CR_ERROR;
+
+ op = CondParser_Token(par, doEval);
+ if (op == TOK_OR) {
+ if (res == CR_FALSE)
+ return CondParser_Or(par, doEval);
+ if (CondParser_Or(par, FALSE) == CR_ERROR)
+ return CR_ERROR;
+ return res;
}
- return l;
+
+ CondParser_PushBack(par, op);
+ return res;
}
static CondEvalResult
-CondParser_Eval(CondParser *par, Boolean *value)
+CondParser_Eval(CondParser *par, Boolean *out_value)
{
- Token res;
+ CondResult res;
DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
- res = CondParser_Expr(par, TRUE);
- if (res != TOK_FALSE && res != TOK_TRUE)
+ res = CondParser_Or(par, TRUE);
+ if (res == CR_ERROR)
return COND_INVALID;
if (CondParser_Token(par, FALSE) != TOK_EOF)
return COND_INVALID;
- *value = res == TOK_TRUE;
+ *out_value = res == CR_TRUE;
return COND_PARSE;
}
@@ -1066,7 +1058,8 @@ CondParser_Eval(CondParser *par, Boolean *value)
* (*value) is set to the boolean value of the condition
*/
static CondEvalResult
-CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
+CondEvalExpression(const char *cond, Boolean *out_value, Boolean plain,
+ Boolean (*evalBare)(size_t, const char *), Boolean negate,
Boolean eprint, Boolean strictLHS)
{
CondParser par;
@@ -1076,12 +1069,14 @@ CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
cpp_skip_hspace(&cond);
- par.if_info = info != NULL ? info : ifs + PLAIN_IF_INDEX;
+ par.plain = plain;
+ par.evalBare = evalBare;
+ par.negateEvalBare = negate;
par.p = cond;
par.curr = TOK_NONE;
par.printedError = FALSE;
- rval = CondParser_Eval(&par, value);
+ rval = CondParser_Eval(&par, out_value);
if (rval == COND_INVALID && eprint && !par.printedError)
Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
@@ -1096,7 +1091,8 @@ CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
CondEvalResult
Cond_EvalCondition(const char *cond, Boolean *out_value)
{
- return CondEvalExpression(NULL, cond, out_value, FALSE, FALSE);
+ return CondEvalExpression(cond, out_value, TRUE,
+ FuncDefined, FALSE, FALSE, FALSE);
}
static Boolean
@@ -1106,6 +1102,43 @@ IsEndif(const char *p)
p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]);
}
+static Boolean
+DetermineKindOfConditional(const char **pp, Boolean *out_plain,
+ Boolean (**out_evalBare)(size_t, const char *),
+ Boolean *out_negate)
+{
+ const char *p = *pp;
+
+ p += 2;
+ *out_plain = FALSE;
+ *out_evalBare = FuncDefined;
+ *out_negate = FALSE;
+ if (*p == 'n') {
+ p++;
+ *out_negate = TRUE;
+ }
+ if (is_token(p, "def", 3)) { /* .ifdef and .ifndef */
+ p += 3;
+ } else if (is_token(p, "make", 4)) { /* .ifmake and .ifnmake */
+ p += 4;
+ *out_evalBare = FuncMake;
+ } else if (is_token(p, "", 0) && !*out_negate) { /* plain .if */
+ *out_plain = TRUE;
+ } else {
+ /*
+ * TODO: Add error message about unknown directive,
+ * since there is no other known directive that starts
+ * with 'el' or 'if'.
+ *
+ * Example: .elifx 123
+ */
+ return FALSE;
+ }
+
+ *pp = p;
+ return TRUE;
+}
+
/*
* Evaluate the conditional directive in the line, which is one of:
*
@@ -1159,7 +1192,9 @@ Cond_EvalLine(const char *line)
static enum IfState *cond_states = NULL;
static unsigned int cond_states_cap = 128;
- const struct If *ifp;
+ Boolean plain;
+ Boolean (*evalBare)(size_t, const char *);
+ Boolean negate;
Boolean isElif;
Boolean value;
IfState state;
@@ -1242,29 +1277,8 @@ Cond_EvalLine(const char *line)
return COND_INVALID; /* Not an ifxxx or elifxxx line */
}
- /*
- * Figure out what sort of conditional it is -- what its default
- * function is, etc. -- by looking in the table of valid "ifs"
- */
- p += 2;
- for (ifp = ifs;; ifp++) {
- if (ifp->form == NULL) {
- /*
- * TODO: Add error message about unknown directive,
- * since there is no other known directive that starts
- * with 'el' or 'if'.
- *
- * Example: .elifx 123
- */
- return COND_INVALID;
- }
- if (is_token(p, ifp->form, ifp->formlen)) {
- p += ifp->formlen;
- break;
- }
- }
-
- /* Now we know what sort of 'if' it is... */
+ if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
+ return COND_INVALID;
if (isElif) {
if (cond_depth == cond_min_depth) {
@@ -1308,7 +1322,8 @@ Cond_EvalLine(const char *line)
}
/* And evaluate the conditional expression */
- if (CondEvalExpression(ifp, p, &value, TRUE, TRUE) == COND_INVALID) {
+ if (CondEvalExpression(p, &value, plain, evalBare, negate,
+ TRUE, TRUE) == COND_INVALID) {
/* Syntax error in conditional, error message already output. */
/* Skip everything to matching .endif */
/* XXX: An extra '.else' is not detected in this case. */