1 /* $NetBSD: cond.c,v 1.342 2022/09/24 16:13:48 rillig Exp $ */
4 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
7 * This code is derived from software contributed to Berkeley by
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * Copyright (c) 1988, 1989 by Adam de Boor
37 * Copyright (c) 1989 by Berkeley Softworks
38 * All rights reserved.
40 * This code is derived from software contributed to Berkeley by
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement:
53 * This product includes software developed by the University of
54 * California, Berkeley and its contributors.
55 * 4. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
73 * Handling of conditionals in a makefile.
76 * Cond_EvalLine Evaluate the conditional directive, such as
77 * '.if <cond>', '.elifnmake <cond>', '.else', '.endif'.
80 * Evaluate the conditional, which is either the argument
81 * of one of the .if directives or the condition in a
82 * ':?then:else' variable modifier.
85 * At the end of reading a makefile, ensure that the
86 * conditional directives are well-balanced.
94 /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
95 MAKE_RCSID("$NetBSD: cond.c,v 1.342 2022/09/24 16:13:48 rillig Exp $");
98 * Conditional expressions conform to this grammar:
99 * Or -> And ('||' And)*
100 * And -> Term ('&&' Term)*
101 * Term -> Function '(' Argument ')'
102 * Term -> Leaf Operator Leaf
108 * Leaf -> VariableExpression
110 * Operator -> '==' | '!=' | '>' | '<' | '>=' | '<='
112 * BareWord is an unquoted string literal, its evaluation depends on the kind
113 * of '.if' directive.
115 * The tokens are scanned by CondParser_Token, which returns:
122 * Other terminal symbols are evaluated using either the default function or
123 * the function given in the terminal, they return either TOK_TRUE, TOK_FALSE
127 TOK_FALSE
, TOK_TRUE
, TOK_AND
, TOK_OR
, TOK_NOT
,
128 TOK_LPAREN
, TOK_RPAREN
, TOK_EOF
, TOK_NONE
, TOK_ERROR
131 typedef enum ComparisonOp
{
132 LT
, LE
, GT
, GE
, EQ
, NE
135 typedef struct CondParser
{
138 * The plain '.if ${VAR}' evaluates to true if the value of the
139 * expression has length > 0. The other '.if' variants delegate
140 * to evalBare instead, for example '.ifdef ${VAR}' is equivalent to
141 * '.if defined(${VAR})', checking whether the variable named by the
142 * expression '${VAR}' is defined.
146 /* The function to apply on unquoted bare words. */
147 bool (*evalBare
)(const char *);
151 * Whether the left-hand side of a comparison may be an unquoted
152 * string. This is allowed for expressions of the form
153 * ${condition:?:}, see ApplyModifier_IfElse. Such a condition is
154 * expanded before it is evaluated, due to ease of implementation.
155 * This means that at the point where the condition is evaluated,
156 * make cannot know anymore whether the left-hand side had originally
157 * been a variable expression or a plain word.
159 * In conditional directives like '.if', the left-hand side must
160 * either be a variable expression, a quoted string or a number.
164 const char *p
; /* The remaining condition to parse */
165 Token curr
; /* Single push-back token used in parsing */
168 * Whether an error message has already been printed for this
169 * condition. The first available error message is usually the most
170 * specific one, therefore it makes sense to suppress the standard
171 * "Malformed conditional" message.
176 static CondResult
CondParser_Or(CondParser
*par
, bool);
178 unsigned int cond_depth
= 0; /* current .if nesting level */
180 /* Names for ComparisonOp. */
181 static const char opname
[][3] = { "<", "<=", ">", ">=", "==", "!=" };
184 skip_string(const char **pp
, const char *str
)
186 size_t len
= strlen(str
);
187 bool ok
= strncmp(*pp
, str
, len
) == 0;
196 return cond
? TOK_TRUE
: TOK_FALSE
;
200 CondParser_SkipWhitespace(CondParser
*par
)
202 cpp_skip_whitespace(&par
->p
);
206 * Parse a single word, taking into account balanced parentheses as well as
207 * embedded expressions. Used for the argument of a built-in function as
208 * well as for bare words, which are then passed to the default function.
211 ParseWord(const char **pp
, bool doEval
)
217 Buf_InitSize(&word
, 16);
222 if (ch
== '\0' || ch
== ' ' || ch
== '\t')
224 if ((ch
== '&' || ch
== '|') && paren_depth
== 0)
228 * Parse the variable expression and install it as
229 * part of the argument if it's valid. We tell
230 * Var_Parse to complain on an undefined variable,
231 * (XXX: but Var_Parse ignores that request)
232 * so we don't need to do it. Nor do we return an
233 * error, though perhaps we should.
235 VarEvalMode emode
= doEval
239 (void)Var_Parse(&p
, SCOPE_CMDLINE
, emode
, &nestedVal
);
240 /* TODO: handle errors */
241 Buf_AddStr(&word
, nestedVal
.str
);
242 FStr_Done(&nestedVal
);
247 else if (ch
== ')' && --paren_depth
< 0)
249 Buf_AddByte(&word
, ch
);
256 return Buf_DoneData(&word
);
259 /* Parse the function argument, including the surrounding parentheses. */
261 ParseFuncArg(CondParser
*par
, const char **pp
, bool doEval
, const char *func
)
266 p
++; /* Skip opening '(' - verified by caller */
268 res
= ParseWord(&p
, doEval
);
273 while (ch_isalpha(func
[len
]))
276 Parse_Error(PARSE_FATAL
,
277 "Missing closing parenthesis for %.*s()", len
, func
);
278 par
->printedError
= true;
287 /* See if the given variable is defined. */
289 FuncDefined(const char *var
)
291 return Var_Exists(SCOPE_CMDLINE
, var
);
294 /* See if a target matching targetPattern is requested to be made. */
296 FuncMake(const char *targetPattern
)
300 for (ln
= opts
.create
.first
; ln
!= NULL
; ln
= ln
->next
)
301 if (Str_Match(ln
->datum
, targetPattern
))
306 /* See if the given file exists. */
308 FuncExists(const char *file
)
313 path
= Dir_FindFile(file
, &dirSearchPath
);
314 DEBUG2(COND
, "exists(%s) result is \"%s\"\n",
315 file
, path
!= NULL
? path
: "");
316 result
= path
!= NULL
;
321 /* See if the given node exists and is an actual target. */
323 FuncTarget(const char *node
)
325 GNode
*gn
= Targ_FindNode(node
);
326 return gn
!= NULL
&& GNode_IsTarget(gn
);
330 * See if the given node exists and is an actual target with commands
331 * associated with it.
334 FuncCommands(const char *node
)
336 GNode
*gn
= Targ_FindNode(node
);
337 return gn
!= NULL
&& GNode_IsTarget(gn
) &&
338 !Lst_IsEmpty(&gn
->commands
);
342 * Convert the string into a floating-point number. Accepted formats are
343 * base-10 integer, base-16 integer and finite floating point numbers.
346 TryParseNumber(const char *str
, double *out_value
)
349 unsigned long ul_val
;
352 if (str
[0] == '\0') { /* XXX: why is an empty string a number? */
358 ul_val
= strtoul(str
, &end
, str
[1] == 'x' ? 16 : 10);
359 if (*end
== '\0' && errno
!= ERANGE
) {
360 *out_value
= str
[0] == '-' ? -(double)-ul_val
: (double)ul_val
;
364 if (*end
!= '\0' && *end
!= '.' && *end
!= 'e' && *end
!= 'E')
365 return false; /* skip the expensive strtod call */
366 dbl_val
= strtod(str
, &end
);
370 *out_value
= dbl_val
;
375 is_separator(char ch
)
377 return ch
== '\0' || ch_isspace(ch
) || ch
== '!' || ch
== '=' ||
378 ch
== '>' || ch
== '<' || ch
== ')' /* but not '(' */;
382 * In a quoted or unquoted string literal or a number, parse a variable
385 * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX}
388 CondParser_StringExpr(CondParser
*par
, const char *start
,
389 bool doEval
, bool quoted
,
390 Buffer
*buf
, FStr
*inout_str
)
395 VarParseResult parseResult
;
397 emode
= doEval
&& quoted
? VARE_WANTRES
398 : doEval
? VARE_UNDEFERR
402 atStart
= p
== start
;
403 parseResult
= Var_Parse(&p
, SCOPE_CMDLINE
, emode
, inout_str
);
404 /* TODO: handle errors */
405 if (inout_str
->str
== var_Error
) {
406 if (parseResult
== VPR_ERR
) {
408 * FIXME: Even if an error occurs, there is no
409 * guarantee that it is reported.
411 * See cond-token-plain.mk $$$$$$$$.
413 par
->printedError
= true;
416 * XXX: Can there be any situation in which a returned
417 * var_Error needs to be freed?
419 FStr_Done(inout_str
);
421 * Even if !doEval, we still report syntax errors, which is
422 * what getting var_Error back with !doEval means.
424 *inout_str
= FStr_InitRefer(NULL
);
430 * If the '$' started the string literal (which means no quotes), and
431 * the variable expression is followed by a space, looks like a
432 * comparison operator or is the end of the expression, we are done.
434 if (atStart
&& is_separator(par
->p
[0]))
437 Buf_AddStr(buf
, inout_str
->str
);
438 FStr_Done(inout_str
);
439 *inout_str
= FStr_InitRefer(NULL
); /* not finished yet */
444 * Parse a string from a variable expression or an optionally quoted string,
445 * on the left-hand and right-hand sides of comparisons.
448 * Returns the string without any enclosing quotes, or NULL on error.
449 * Sets out_quoted if the leaf was a quoted string literal.
452 CondParser_Leaf(CondParser
*par
, bool doEval
, bool unquotedOK
,
453 FStr
*out_str
, bool *out_quoted
)
461 str
= FStr_InitRefer(NULL
);
462 *out_quoted
= quoted
= par
->p
[0] == '"';
467 while (par
->p
[0] != '\0' && str
.str
== NULL
) {
471 if (par
->p
[0] != '\0') {
472 Buf_AddByte(&buf
, par
->p
[0]);
479 goto return_buf
; /* skip the closing quote */
480 Buf_AddByte(&buf
, '"');
482 case ')': /* see is_separator */
491 Buf_AddByte(&buf
, par
->p
[0]);
495 if (!CondParser_StringExpr(par
,
496 start
, doEval
, quoted
, &buf
, &str
))
500 if (!unquotedOK
&& !quoted
&& *start
!= '$' &&
501 !ch_isdigit(*start
)) {
503 * The left-hand side must be quoted,
504 * a variable expression or a number.
506 str
= FStr_InitRefer(NULL
);
509 Buf_AddByte(&buf
, par
->p
[0]);
515 str
= FStr_InitOwn(buf
.data
);
523 * Evaluate a "comparison without operator", such as in ".if ${VAR}" or
527 EvalNotEmpty(CondParser
*par
, const char *value
, bool quoted
)
531 /* For .ifxxx "...", check for non-empty string. */
533 return value
[0] != '\0';
535 /* For .ifxxx <number>, compare against zero */
536 if (TryParseNumber(value
, &num
))
540 * For .if ${...}, check for non-empty string. This is different
541 * from the evaluation function from that .if variant, which would
542 * test whether a variable of the given name were defined.
545 * XXX: Whitespace should count as empty, just as in
546 * CondParser_FuncCallEmpty.
549 return value
[0] != '\0';
551 return par
->evalBare(value
) != par
->negateEvalBare
;
554 /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
556 EvalCompareNum(double lhs
, ComparisonOp op
, double rhs
)
558 DEBUG3(COND
, "Comparing %f %s %f\n", lhs
, opname
[op
], rhs
);
577 EvalCompareStr(CondParser
*par
, const char *lhs
,
578 ComparisonOp op
, const char *rhs
)
580 if (op
!= EQ
&& op
!= NE
) {
581 Parse_Error(PARSE_FATAL
,
582 "Comparison with '%s' requires both operands "
583 "'%s' and '%s' to be numeric",
584 opname
[op
], lhs
, rhs
);
585 par
->printedError
= true;
589 DEBUG3(COND
, "Comparing \"%s\" %s \"%s\"\n", lhs
, opname
[op
], rhs
);
590 return ToToken((op
== EQ
) == (strcmp(lhs
, rhs
) == 0));
593 /* Evaluate a comparison, such as "${VAR} == 12345". */
595 EvalCompare(CondParser
*par
, const char *lhs
, bool lhsQuoted
,
596 ComparisonOp op
, const char *rhs
, bool rhsQuoted
)
600 if (!rhsQuoted
&& !lhsQuoted
)
601 if (TryParseNumber(lhs
, &left
) && TryParseNumber(rhs
, &right
))
602 return ToToken(EvalCompareNum(left
, op
, right
));
604 return EvalCompareStr(par
, lhs
, op
, rhs
);
608 CondParser_ComparisonOp(CondParser
*par
, ComparisonOp
*out_op
)
610 const char *p
= par
->p
;
612 if (p
[0] == '<' && p
[1] == '=')
613 return par
->p
+= 2, *out_op
= LE
, true;
615 return par
->p
+= 1, *out_op
= LT
, true;
616 if (p
[0] == '>' && p
[1] == '=')
617 return par
->p
+= 2, *out_op
= GE
, true;
619 return par
->p
+= 1, *out_op
= GT
, true;
620 if (p
[0] == '=' && p
[1] == '=')
621 return par
->p
+= 2, *out_op
= EQ
, true;
622 if (p
[0] == '!' && p
[1] == '=')
623 return par
->p
+= 2, *out_op
= NE
, true;
628 * Parse a comparison condition such as:
636 CondParser_Comparison(CondParser
*par
, bool doEval
)
641 bool lhsQuoted
, rhsQuoted
;
643 CondParser_Leaf(par
, doEval
, par
->leftUnquotedOK
, &lhs
, &lhsQuoted
);
647 CondParser_SkipWhitespace(par
);
649 if (!CondParser_ComparisonOp(par
, &op
)) {
650 /* Unknown operator, compare against an empty string or 0. */
651 t
= ToToken(doEval
&& EvalNotEmpty(par
, lhs
.str
, lhsQuoted
));
655 CondParser_SkipWhitespace(par
);
657 if (par
->p
[0] == '\0') {
658 Parse_Error(PARSE_FATAL
,
659 "Missing right-hand side of operator '%s'", opname
[op
]);
660 par
->printedError
= true;
664 CondParser_Leaf(par
, doEval
, true, &rhs
, &rhsQuoted
);
665 t
= rhs
.str
== NULL
? TOK_ERROR
666 : !doEval
? TOK_FALSE
667 : EvalCompare(par
, lhs
.str
, lhsQuoted
, op
, rhs
.str
, rhsQuoted
);
676 * The argument to empty() is a variable name, optionally followed by
677 * variable modifiers.
680 CondParser_FuncCallEmpty(CondParser
*par
, bool doEval
, Token
*out_token
)
682 const char *cp
= par
->p
;
686 if (!skip_string(&cp
, "empty"))
689 cpp_skip_whitespace(&cp
);
693 cp
--; /* Make cp[1] point to the '('. */
694 (void)Var_Parse(&cp
, SCOPE_CMDLINE
,
695 doEval
? VARE_WANTRES
: VARE_PARSE_ONLY
, &val
);
696 /* TODO: handle errors */
698 if (val
.str
== var_Error
)
701 cpp_skip_whitespace(&val
.str
);
702 tok
= ToToken(doEval
&& val
.str
[0] == '\0');
711 /* Parse a function call expression, such as 'exists(${file})'. */
713 CondParser_FuncCall(CondParser
*par
, bool doEval
, Token
*out_token
)
716 const char *p
= par
->p
;
717 bool (*fn
)(const char *);
718 const char *fn_name
= p
;
720 if (skip_string(&p
, "defined"))
722 else if (skip_string(&p
, "make"))
724 else if (skip_string(&p
, "exists"))
726 else if (skip_string(&p
, "target"))
728 else if (skip_string(&p
, "commands"))
733 cpp_skip_whitespace(&p
);
737 arg
= ParseFuncArg(par
, &p
, doEval
, fn_name
);
738 *out_token
= ToToken(doEval
&&
739 arg
!= NULL
&& arg
[0] != '\0' && fn(arg
));
747 * Parse a comparison that neither starts with '"' nor '$', such as the
748 * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without
749 * operator, which is a number, a variable expression or a string literal.
751 * TODO: Can this be merged into CondParser_Comparison?
754 CondParser_ComparisonOrLeaf(CondParser
*par
, bool doEval
)
760 /* Push anything numeric through the compare expression */
762 if (ch_isdigit(cp
[0]) || cp
[0] == '-' || cp
[0] == '+')
763 return CondParser_Comparison(par
, doEval
);
766 * Most likely we have a naked token to apply the default function to.
767 * However ".if a == b" gets here when the "a" is unquoted and doesn't
768 * start with a '$'. This surprises people.
769 * If what follows the function argument is a '=' or '!' then the
770 * syntax would be invalid if we did "defined(a)" - so instead treat
774 * XXX: In edge cases, a variable expression may be evaluated twice,
775 * see cond-token-plain.mk, keyword 'twice'.
777 arg
= ParseWord(&cp
, doEval
);
778 assert(arg
[0] != '\0');
780 if (*cp
== '=' || *cp
== '!' || *cp
== '<' || *cp
== '>')
781 return CondParser_Comparison(par
, doEval
);
785 * Evaluate the argument using the default function.
786 * This path always treats .if as .ifdef. To get here, the character
787 * after .if must have been taken literally, so the argument cannot
788 * be empty - even if it contained a variable expansion.
790 t
= ToToken(doEval
&& par
->evalBare(arg
) != par
->negateEvalBare
);
795 /* Return the next token or comparison result from the parser. */
797 CondParser_Token(CondParser
*par
, bool doEval
)
803 par
->curr
= TOK_NONE
;
807 cpp_skip_hspace(&par
->p
);
821 if (par
->p
[0] == '|')
823 else if (opts
.strict
) {
824 Parse_Error(PARSE_FATAL
, "Unknown operator '|'");
825 par
->printedError
= true;
832 if (par
->p
[0] == '&')
834 else if (opts
.strict
) {
835 Parse_Error(PARSE_FATAL
, "Unknown operator '&'");
836 par
->printedError
= true;
845 case '#': /* XXX: see unit-tests/cond-token-plain.mk */
846 case '\n': /* XXX: why should this end the condition? */
847 /* Probably obsolete now, from 1993-03-21. */
853 return CondParser_Comparison(par
, doEval
);
856 if (CondParser_FuncCallEmpty(par
, doEval
, &t
))
858 if (CondParser_FuncCall(par
, doEval
, &t
))
860 return CondParser_ComparisonOrLeaf(par
, doEval
);
864 /* Skip the next token if it equals t. */
866 CondParser_Skip(CondParser
*par
, Token t
)
870 actual
= CondParser_Token(par
, false);
874 assert(par
->curr
== TOK_NONE
);
875 assert(actual
!= TOK_NONE
);
883 * Term -> Leaf Operator Leaf
887 CondParser_Term(CondParser
*par
, bool doEval
)
892 t
= CondParser_Token(par
, doEval
);
898 if (t
== TOK_LPAREN
) {
899 res
= CondParser_Or(par
, doEval
);
902 if (CondParser_Token(par
, doEval
) != TOK_RPAREN
)
908 res
= CondParser_Term(par
, doEval
);
911 else if (res
== CR_FALSE
)
920 * And -> Term ('&&' Term)*
923 CondParser_And(CondParser
*par
, bool doEval
)
929 if ((rhs
= CondParser_Term(par
, doEval
)) == CR_ERROR
)
931 if (rhs
== CR_FALSE
) {
935 } while (CondParser_Skip(par
, TOK_AND
));
941 * Or -> And ('||' And)*
944 CondParser_Or(CondParser
*par
, bool doEval
)
950 if ((rhs
= CondParser_And(par
, doEval
)) == CR_ERROR
)
952 if (rhs
== CR_TRUE
) {
956 } while (CondParser_Skip(par
, TOK_OR
));
962 CondParser_Eval(CondParser
*par
)
966 DEBUG1(COND
, "CondParser_Eval: %s\n", par
->p
);
968 res
= CondParser_Or(par
, true);
969 if (res
!= CR_ERROR
&& CondParser_Token(par
, false) != TOK_EOF
)
976 * Evaluate the condition, including any side effects from the variable
977 * expressions in the condition. The condition consists of &&, ||, !,
978 * function(arg), comparisons and parenthetical groupings thereof.
981 CondEvalExpression(const char *cond
, bool plain
,
982 bool (*evalBare
)(const char *), bool negate
,
983 bool eprint
, bool leftUnquotedOK
)
988 cpp_skip_hspace(&cond
);
991 par
.evalBare
= evalBare
;
992 par
.negateEvalBare
= negate
;
993 par
.leftUnquotedOK
= leftUnquotedOK
;
996 par
.printedError
= false;
998 rval
= CondParser_Eval(&par
);
1000 if (rval
== CR_ERROR
&& eprint
&& !par
.printedError
)
1001 Parse_Error(PARSE_FATAL
, "Malformed conditional (%s)", cond
);
1007 * Evaluate a condition in a :? modifier, such as
1008 * ${"${VAR}" == value:?yes:no}.
1011 Cond_EvalCondition(const char *cond
)
1013 return CondEvalExpression(cond
, true,
1014 FuncDefined
, false, false, true);
1018 IsEndif(const char *p
)
1020 return p
[0] == 'e' && p
[1] == 'n' && p
[2] == 'd' &&
1021 p
[3] == 'i' && p
[4] == 'f' && !ch_isalpha(p
[5]);
1025 DetermineKindOfConditional(const char **pp
, bool *out_plain
,
1026 bool (**out_evalBare
)(const char *),
1029 const char *p
= *pp
+ 2;
1032 *out_evalBare
= FuncDefined
;
1033 *out_negate
= skip_string(&p
, "n");
1035 if (skip_string(&p
, "def")) { /* .ifdef and .ifndef */
1036 } else if (skip_string(&p
, "make")) /* .ifmake and .ifnmake */
1037 *out_evalBare
= FuncMake
;
1038 else if (!*out_negate
) /* plain .if */
1041 goto unknown_directive
;
1043 goto unknown_directive
;
1050 * TODO: Add error message about unknown directive, since there is no
1051 * other known directive that starts with 'el' or 'if'.
1053 * Example: .elifx 123
1059 * Evaluate the conditional directive in the line, which is one of:
1074 * In these directives, <cond> consists of &&, ||, !, function(arg),
1075 * comparisons, expressions, bare words, numbers and strings, and
1076 * parenthetical groupings thereof.
1079 * CR_TRUE to continue parsing the lines that follow the
1080 * conditional (when <cond> evaluates to true)
1081 * CR_FALSE to skip the lines after the conditional
1082 * (when <cond> evaluates to false, or when a previous
1083 * branch has already been taken)
1084 * CR_ERROR if the conditional was not valid, either because of
1085 * a syntax error or because some variable was undefined
1086 * or because the condition could not be evaluated
1089 Cond_EvalLine(const char *line
)
1091 typedef enum IfState
{
1093 /* None of the previous <cond> evaluated to true. */
1097 * The previous <cond> evaluated to true. The lines following
1098 * this condition are interpreted.
1100 IFS_ACTIVE
= 1 << 0,
1102 /* The previous directive was an '.else'. */
1103 IFS_SEEN_ELSE
= 1 << 1,
1105 /* One of the previous <cond> evaluated to true. */
1106 IFS_WAS_ACTIVE
= 1 << 2
1110 static enum IfState
*cond_states
= NULL
;
1111 static unsigned int cond_states_cap
= 128;
1114 bool (*evalBare
)(const char *);
1119 const char *p
= line
;
1121 if (cond_states
== NULL
) {
1122 cond_states
= bmake_malloc(
1123 cond_states_cap
* sizeof *cond_states
);
1124 cond_states
[0] = IFS_ACTIVE
;
1127 p
++; /* skip the leading '.' */
1128 cpp_skip_hspace(&p
);
1130 if (IsEndif(p
)) { /* It is an '.endif'. */
1132 Parse_Error(PARSE_FATAL
,
1133 "The .endif directive does not take arguments");
1136 if (cond_depth
== CurFile_CondMinDepth()) {
1137 Parse_Error(PARSE_FATAL
, "if-less endif");
1141 /* Return state for previous conditional */
1143 return cond_states
[cond_depth
] & IFS_ACTIVE
1144 ? CR_TRUE
: CR_FALSE
;
1147 /* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
1151 * Unknown directive. It might still be a
1152 * transformation rule like '.err.txt',
1153 * therefore no error message here.
1158 /* Quite likely this is 'else' or 'elif' */
1160 if (strncmp(p
, "se", 2) == 0 && !ch_isalpha(p
[2])) {
1162 Parse_Error(PARSE_FATAL
,
1163 "The .else directive "
1164 "does not take arguments");
1166 if (cond_depth
== CurFile_CondMinDepth()) {
1167 Parse_Error(PARSE_FATAL
, "if-less else");
1171 state
= cond_states
[cond_depth
];
1172 if (state
== IFS_INITIAL
) {
1173 state
= IFS_ACTIVE
| IFS_SEEN_ELSE
;
1175 if (state
& IFS_SEEN_ELSE
)
1176 Parse_Error(PARSE_WARNING
,
1178 state
= IFS_WAS_ACTIVE
| IFS_SEEN_ELSE
;
1180 cond_states
[cond_depth
] = state
;
1182 return state
& IFS_ACTIVE
? CR_TRUE
: CR_FALSE
;
1184 /* Assume for now it is an elif */
1189 if (p
[0] != 'i' || p
[1] != 'f') {
1191 * Unknown directive. It might still be a transformation rule
1192 * like '.elisp.scm', therefore no error message here.
1194 return CR_ERROR
; /* Not an ifxxx or elifxxx line */
1197 if (!DetermineKindOfConditional(&p
, &plain
, &evalBare
, &negate
))
1201 if (cond_depth
== CurFile_CondMinDepth()) {
1202 Parse_Error(PARSE_FATAL
, "if-less elif");
1205 state
= cond_states
[cond_depth
];
1206 if (state
& IFS_SEEN_ELSE
) {
1207 Parse_Error(PARSE_WARNING
, "extra elif");
1208 cond_states
[cond_depth
] =
1209 IFS_WAS_ACTIVE
| IFS_SEEN_ELSE
;
1212 if (state
!= IFS_INITIAL
) {
1213 cond_states
[cond_depth
] = IFS_WAS_ACTIVE
;
1218 if (cond_depth
+ 1 >= cond_states_cap
) {
1220 * This is rare, but not impossible.
1221 * In meta mode, dirdeps.mk (only runs at level 0)
1222 * can need more than the default.
1224 cond_states_cap
+= 32;
1225 cond_states
= bmake_realloc(cond_states
,
1226 cond_states_cap
* sizeof *cond_states
);
1228 state
= cond_states
[cond_depth
];
1230 if (!(state
& IFS_ACTIVE
)) {
1232 * If we aren't parsing the data,
1233 * treat as always false.
1235 cond_states
[cond_depth
] = IFS_WAS_ACTIVE
;
1240 /* And evaluate the conditional expression */
1241 res
= CondEvalExpression(p
, plain
, evalBare
, negate
, true, false);
1242 if (res
== CR_ERROR
) {
1243 /* Syntax error, error message already output. */
1244 /* Skip everything to the matching '.endif'. */
1245 /* An extra '.else' is not detected in this case. */
1246 cond_states
[cond_depth
] = IFS_WAS_ACTIVE
;
1250 cond_states
[cond_depth
] = res
== CR_TRUE
? IFS_ACTIVE
: IFS_INITIAL
;
1257 unsigned int open_conds
= cond_depth
- CurFile_CondMinDepth();
1259 if (open_conds
!= 0) {
1260 Parse_Error(PARSE_FATAL
, "%u open conditional%s",
1261 open_conds
, open_conds
== 1 ? "" : "s");
1262 cond_depth
= CurFile_CondMinDepth();