xref: /dflybsd-src/contrib/bmake/cond.c (revision 9e7ae5a0527a977cab412aede3a532cfe2903bbb)
1*6eef5f0cSAntonio Huete Jimenez /*	$NetBSD: cond.c,v 1.342 2022/09/24 16:13:48 rillig Exp $	*/
201e196c8SJohn Marino 
301e196c8SJohn Marino /*
401e196c8SJohn Marino  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
501e196c8SJohn Marino  * All rights reserved.
601e196c8SJohn Marino  *
701e196c8SJohn Marino  * This code is derived from software contributed to Berkeley by
801e196c8SJohn Marino  * Adam de Boor.
901e196c8SJohn Marino  *
1001e196c8SJohn Marino  * Redistribution and use in source and binary forms, with or without
1101e196c8SJohn Marino  * modification, are permitted provided that the following conditions
1201e196c8SJohn Marino  * are met:
1301e196c8SJohn Marino  * 1. Redistributions of source code must retain the above copyright
1401e196c8SJohn Marino  *    notice, this list of conditions and the following disclaimer.
1501e196c8SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
1601e196c8SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
1701e196c8SJohn Marino  *    documentation and/or other materials provided with the distribution.
1801e196c8SJohn Marino  * 3. Neither the name of the University nor the names of its contributors
1901e196c8SJohn Marino  *    may be used to endorse or promote products derived from this software
2001e196c8SJohn Marino  *    without specific prior written permission.
2101e196c8SJohn Marino  *
2201e196c8SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2301e196c8SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2401e196c8SJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2501e196c8SJohn Marino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2601e196c8SJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2701e196c8SJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2801e196c8SJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2901e196c8SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3001e196c8SJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3101e196c8SJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3201e196c8SJohn Marino  * SUCH DAMAGE.
3301e196c8SJohn Marino  */
3401e196c8SJohn Marino 
3501e196c8SJohn Marino /*
3601e196c8SJohn Marino  * Copyright (c) 1988, 1989 by Adam de Boor
3701e196c8SJohn Marino  * Copyright (c) 1989 by Berkeley Softworks
3801e196c8SJohn Marino  * All rights reserved.
3901e196c8SJohn Marino  *
4001e196c8SJohn Marino  * This code is derived from software contributed to Berkeley by
4101e196c8SJohn Marino  * Adam de Boor.
4201e196c8SJohn Marino  *
4301e196c8SJohn Marino  * Redistribution and use in source and binary forms, with or without
4401e196c8SJohn Marino  * modification, are permitted provided that the following conditions
4501e196c8SJohn Marino  * are met:
4601e196c8SJohn Marino  * 1. Redistributions of source code must retain the above copyright
4701e196c8SJohn Marino  *    notice, this list of conditions and the following disclaimer.
4801e196c8SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
4901e196c8SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
5001e196c8SJohn Marino  *    documentation and/or other materials provided with the distribution.
5101e196c8SJohn Marino  * 3. All advertising materials mentioning features or use of this software
5201e196c8SJohn Marino  *    must display the following acknowledgement:
5301e196c8SJohn Marino  *	This product includes software developed by the University of
5401e196c8SJohn Marino  *	California, Berkeley and its contributors.
5501e196c8SJohn Marino  * 4. Neither the name of the University nor the names of its contributors
5601e196c8SJohn Marino  *    may be used to endorse or promote products derived from this software
5701e196c8SJohn Marino  *    without specific prior written permission.
5801e196c8SJohn Marino  *
5901e196c8SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
6001e196c8SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
6101e196c8SJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
6201e196c8SJohn Marino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
6301e196c8SJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
6401e196c8SJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
6501e196c8SJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
6601e196c8SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6701e196c8SJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6801e196c8SJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6901e196c8SJohn Marino  * SUCH DAMAGE.
7001e196c8SJohn Marino  */
7101e196c8SJohn Marino 
72a34d5fb1SAntonio Huete Jimenez /*
73a34d5fb1SAntonio Huete Jimenez  * Handling of conditionals in a makefile.
7401e196c8SJohn Marino  *
7501e196c8SJohn Marino  * Interface:
76a34d5fb1SAntonio Huete Jimenez  *	Cond_EvalLine   Evaluate the conditional directive, such as
77a34d5fb1SAntonio Huete Jimenez  *			'.if <cond>', '.elifnmake <cond>', '.else', '.endif'.
7801e196c8SJohn Marino  *
79a34d5fb1SAntonio Huete Jimenez  *	Cond_EvalCondition
80a34d5fb1SAntonio Huete Jimenez  *			Evaluate the conditional, which is either the argument
81a34d5fb1SAntonio Huete Jimenez  *			of one of the .if directives or the condition in a
82a34d5fb1SAntonio Huete Jimenez  *			':?then:else' variable modifier.
83a34d5fb1SAntonio Huete Jimenez  *
84*6eef5f0cSAntonio Huete Jimenez  *	Cond_EndFile
85*6eef5f0cSAntonio Huete Jimenez  *			At the end of reading a makefile, ensure that the
86*6eef5f0cSAntonio Huete Jimenez  *			conditional directives are well-balanced.
8701e196c8SJohn Marino  */
8801e196c8SJohn Marino 
89a34d5fb1SAntonio Huete Jimenez #include <errno.h>
9001e196c8SJohn Marino 
9101e196c8SJohn Marino #include "make.h"
9201e196c8SJohn Marino #include "dir.h"
93a34d5fb1SAntonio Huete Jimenez 
94a34d5fb1SAntonio Huete Jimenez /*	"@(#)cond.c	8.2 (Berkeley) 1/2/94"	*/
95*6eef5f0cSAntonio Huete Jimenez MAKE_RCSID("$NetBSD: cond.c,v 1.342 2022/09/24 16:13:48 rillig Exp $");
9601e196c8SJohn Marino 
9701e196c8SJohn Marino /*
98*6eef5f0cSAntonio Huete Jimenez  * Conditional expressions conform to this grammar:
99*6eef5f0cSAntonio Huete Jimenez  *	Or -> And ('||' And)*
100*6eef5f0cSAntonio Huete Jimenez  *	And -> Term ('&&' Term)*
101a34d5fb1SAntonio Huete Jimenez  *	Term -> Function '(' Argument ')'
102a34d5fb1SAntonio Huete Jimenez  *	Term -> Leaf Operator Leaf
103a34d5fb1SAntonio Huete Jimenez  *	Term -> Leaf
104a34d5fb1SAntonio Huete Jimenez  *	Term -> '(' Or ')'
105a34d5fb1SAntonio Huete Jimenez  *	Term -> '!' Term
106a34d5fb1SAntonio Huete Jimenez  *	Leaf -> "string"
107a34d5fb1SAntonio Huete Jimenez  *	Leaf -> Number
108a34d5fb1SAntonio Huete Jimenez  *	Leaf -> VariableExpression
109*6eef5f0cSAntonio Huete Jimenez  *	Leaf -> BareWord
110a34d5fb1SAntonio Huete Jimenez  *	Operator -> '==' | '!=' | '>' | '<' | '>=' | '<='
11101e196c8SJohn Marino  *
112*6eef5f0cSAntonio Huete Jimenez  * BareWord is an unquoted string literal, its evaluation depends on the kind
113*6eef5f0cSAntonio Huete Jimenez  * of '.if' directive.
11401e196c8SJohn Marino  *
115*6eef5f0cSAntonio Huete Jimenez  * The tokens are scanned by CondParser_Token, which returns:
116a34d5fb1SAntonio Huete Jimenez  *	TOK_AND		for '&&'
117a34d5fb1SAntonio Huete Jimenez  *	TOK_OR		for '||'
118a34d5fb1SAntonio Huete Jimenez  *	TOK_NOT		for '!'
119a34d5fb1SAntonio Huete Jimenez  *	TOK_LPAREN	for '('
120a34d5fb1SAntonio Huete Jimenez  *	TOK_RPAREN	for ')'
12101e196c8SJohn Marino  *
122a34d5fb1SAntonio Huete Jimenez  * Other terminal symbols are evaluated using either the default function or
123*6eef5f0cSAntonio Huete Jimenez  * the function given in the terminal, they return either TOK_TRUE, TOK_FALSE
124*6eef5f0cSAntonio Huete Jimenez  * or TOK_ERROR.
12501e196c8SJohn Marino  */
126a34d5fb1SAntonio Huete Jimenez typedef enum Token {
127a34d5fb1SAntonio Huete Jimenez 	TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT,
12801e196c8SJohn Marino 	TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
12901e196c8SJohn Marino } Token;
13001e196c8SJohn Marino 
131a34d5fb1SAntonio Huete Jimenez typedef enum ComparisonOp {
132a34d5fb1SAntonio Huete Jimenez 	LT, LE, GT, GE, EQ, NE
133a34d5fb1SAntonio Huete Jimenez } ComparisonOp;
134a34d5fb1SAntonio Huete Jimenez 
135a34d5fb1SAntonio Huete Jimenez typedef struct CondParser {
136a34d5fb1SAntonio Huete Jimenez 
137a34d5fb1SAntonio Huete Jimenez 	/*
138a34d5fb1SAntonio Huete Jimenez 	 * The plain '.if ${VAR}' evaluates to true if the value of the
139a34d5fb1SAntonio Huete Jimenez 	 * expression has length > 0.  The other '.if' variants delegate
140*6eef5f0cSAntonio Huete Jimenez 	 * to evalBare instead, for example '.ifdef ${VAR}' is equivalent to
141*6eef5f0cSAntonio Huete Jimenez 	 * '.if defined(${VAR})', checking whether the variable named by the
142*6eef5f0cSAntonio Huete Jimenez 	 * expression '${VAR}' is defined.
14301e196c8SJohn Marino 	 */
144*6eef5f0cSAntonio Huete Jimenez 	bool plain;
14501e196c8SJohn Marino 
146a34d5fb1SAntonio Huete Jimenez 	/* The function to apply on unquoted bare words. */
147*6eef5f0cSAntonio Huete Jimenez 	bool (*evalBare)(const char *);
148*6eef5f0cSAntonio Huete Jimenez 	bool negateEvalBare;
149*6eef5f0cSAntonio Huete Jimenez 
150*6eef5f0cSAntonio Huete Jimenez 	/*
151*6eef5f0cSAntonio Huete Jimenez 	 * Whether the left-hand side of a comparison may be an unquoted
152*6eef5f0cSAntonio Huete Jimenez 	 * string.  This is allowed for expressions of the form
153*6eef5f0cSAntonio Huete Jimenez 	 * ${condition:?:}, see ApplyModifier_IfElse.  Such a condition is
154*6eef5f0cSAntonio Huete Jimenez 	 * expanded before it is evaluated, due to ease of implementation.
155*6eef5f0cSAntonio Huete Jimenez 	 * This means that at the point where the condition is evaluated,
156*6eef5f0cSAntonio Huete Jimenez 	 * make cannot know anymore whether the left-hand side had originally
157*6eef5f0cSAntonio Huete Jimenez 	 * been a variable expression or a plain word.
158*6eef5f0cSAntonio Huete Jimenez 	 *
159*6eef5f0cSAntonio Huete Jimenez 	 * In conditional directives like '.if', the left-hand side must
160*6eef5f0cSAntonio Huete Jimenez 	 * either be a variable expression, a quoted string or a number.
161*6eef5f0cSAntonio Huete Jimenez 	 */
162*6eef5f0cSAntonio Huete Jimenez 	bool leftUnquotedOK;
16301e196c8SJohn Marino 
164a34d5fb1SAntonio Huete Jimenez 	const char *p;		/* The remaining condition to parse */
165a34d5fb1SAntonio Huete Jimenez 	Token curr;		/* Single push-back token used in parsing */
166a34d5fb1SAntonio Huete Jimenez 
167a34d5fb1SAntonio Huete Jimenez 	/*
168a34d5fb1SAntonio Huete Jimenez 	 * Whether an error message has already been printed for this
169a34d5fb1SAntonio Huete Jimenez 	 * condition. The first available error message is usually the most
170a34d5fb1SAntonio Huete Jimenez 	 * specific one, therefore it makes sense to suppress the standard
171a34d5fb1SAntonio Huete Jimenez 	 * "Malformed conditional" message.
172a34d5fb1SAntonio Huete Jimenez 	 */
173*6eef5f0cSAntonio Huete Jimenez 	bool printedError;
174a34d5fb1SAntonio Huete Jimenez } CondParser;
175a34d5fb1SAntonio Huete Jimenez 
176*6eef5f0cSAntonio Huete Jimenez static CondResult CondParser_Or(CondParser *par, bool);
17701e196c8SJohn Marino 
178*6eef5f0cSAntonio Huete Jimenez unsigned int cond_depth = 0;	/* current .if nesting level */
17901e196c8SJohn Marino 
180*6eef5f0cSAntonio Huete Jimenez /* Names for ComparisonOp. */
181*6eef5f0cSAntonio Huete Jimenez static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" };
182a34d5fb1SAntonio Huete Jimenez 
183*6eef5f0cSAntonio Huete Jimenez MAKE_INLINE bool
skip_string(const char ** pp,const char * str)184*6eef5f0cSAntonio Huete Jimenez skip_string(const char **pp, const char *str)
18501e196c8SJohn Marino {
186*6eef5f0cSAntonio Huete Jimenez 	size_t len = strlen(str);
187*6eef5f0cSAntonio Huete Jimenez 	bool ok = strncmp(*pp, str, len) == 0;
188*6eef5f0cSAntonio Huete Jimenez 	if (ok)
189*6eef5f0cSAntonio Huete Jimenez 		*pp += len;
190*6eef5f0cSAntonio Huete Jimenez 	return ok;
19101e196c8SJohn Marino }
19201e196c8SJohn Marino 
193a34d5fb1SAntonio Huete Jimenez static Token
ToToken(bool cond)194*6eef5f0cSAntonio Huete Jimenez ToToken(bool cond)
195a34d5fb1SAntonio Huete Jimenez {
196a34d5fb1SAntonio Huete Jimenez 	return cond ? TOK_TRUE : TOK_FALSE;
197a34d5fb1SAntonio Huete Jimenez }
198a34d5fb1SAntonio Huete Jimenez 
199a34d5fb1SAntonio Huete Jimenez static void
CondParser_SkipWhitespace(CondParser * par)200a34d5fb1SAntonio Huete Jimenez CondParser_SkipWhitespace(CondParser *par)
20101e196c8SJohn Marino {
202a34d5fb1SAntonio Huete Jimenez 	cpp_skip_whitespace(&par->p);
203a34d5fb1SAntonio Huete Jimenez }
204a34d5fb1SAntonio Huete Jimenez 
205a34d5fb1SAntonio Huete Jimenez /*
206*6eef5f0cSAntonio Huete Jimenez  * Parse a single word, taking into account balanced parentheses as well as
207*6eef5f0cSAntonio Huete Jimenez  * embedded expressions.  Used for the argument of a built-in function as
208*6eef5f0cSAntonio Huete Jimenez  * well as for bare words, which are then passed to the default function.
209a34d5fb1SAntonio Huete Jimenez  */
210*6eef5f0cSAntonio Huete Jimenez static char *
ParseWord(const char ** pp,bool doEval)211*6eef5f0cSAntonio Huete Jimenez ParseWord(const char **pp, bool doEval)
212a34d5fb1SAntonio Huete Jimenez {
213a34d5fb1SAntonio Huete Jimenez 	const char *p = *pp;
214*6eef5f0cSAntonio Huete Jimenez 	Buffer word;
21501e196c8SJohn Marino 	int paren_depth;
21601e196c8SJohn Marino 
217*6eef5f0cSAntonio Huete Jimenez 	Buf_InitSize(&word, 16);
21801e196c8SJohn Marino 
21901e196c8SJohn Marino 	paren_depth = 0;
22001e196c8SJohn Marino 	for (;;) {
221a34d5fb1SAntonio Huete Jimenez 		char ch = *p;
222a34d5fb1SAntonio Huete Jimenez 		if (ch == '\0' || ch == ' ' || ch == '\t')
22301e196c8SJohn Marino 			break;
22401e196c8SJohn Marino 		if ((ch == '&' || ch == '|') && paren_depth == 0)
22501e196c8SJohn Marino 			break;
226*6eef5f0cSAntonio Huete Jimenez 		if (ch == '$') {
22701e196c8SJohn Marino 			/*
228a34d5fb1SAntonio Huete Jimenez 			 * Parse the variable expression and install it as
229a34d5fb1SAntonio Huete Jimenez 			 * part of the argument if it's valid. We tell
230a34d5fb1SAntonio Huete Jimenez 			 * Var_Parse to complain on an undefined variable,
231a34d5fb1SAntonio Huete Jimenez 			 * (XXX: but Var_Parse ignores that request)
232a34d5fb1SAntonio Huete Jimenez 			 * so we don't need to do it. Nor do we return an
233a34d5fb1SAntonio Huete Jimenez 			 * error, though perhaps we should.
23401e196c8SJohn Marino 			 */
235*6eef5f0cSAntonio Huete Jimenez 			VarEvalMode emode = doEval
236*6eef5f0cSAntonio Huete Jimenez 			    ? VARE_UNDEFERR
237*6eef5f0cSAntonio Huete Jimenez 			    : VARE_PARSE_ONLY;
238a34d5fb1SAntonio Huete Jimenez 			FStr nestedVal;
239*6eef5f0cSAntonio Huete Jimenez 			(void)Var_Parse(&p, SCOPE_CMDLINE, emode, &nestedVal);
240a34d5fb1SAntonio Huete Jimenez 			/* TODO: handle errors */
241*6eef5f0cSAntonio Huete Jimenez 			Buf_AddStr(&word, nestedVal.str);
242a34d5fb1SAntonio Huete Jimenez 			FStr_Done(&nestedVal);
24301e196c8SJohn Marino 			continue;
24401e196c8SJohn Marino 		}
24501e196c8SJohn Marino 		if (ch == '(')
24601e196c8SJohn Marino 			paren_depth++;
247a34d5fb1SAntonio Huete Jimenez 		else if (ch == ')' && --paren_depth < 0)
24801e196c8SJohn Marino 			break;
249*6eef5f0cSAntonio Huete Jimenez 		Buf_AddByte(&word, ch);
250a34d5fb1SAntonio Huete Jimenez 		p++;
25101e196c8SJohn Marino 	}
25201e196c8SJohn Marino 
253*6eef5f0cSAntonio Huete Jimenez 	cpp_skip_hspace(&p);
254*6eef5f0cSAntonio Huete Jimenez 	*pp = p;
25501e196c8SJohn Marino 
256*6eef5f0cSAntonio Huete Jimenez 	return Buf_DoneData(&word);
257*6eef5f0cSAntonio Huete Jimenez }
258*6eef5f0cSAntonio Huete Jimenez 
259*6eef5f0cSAntonio Huete Jimenez /* Parse the function argument, including the surrounding parentheses. */
260*6eef5f0cSAntonio Huete Jimenez static char *
ParseFuncArg(CondParser * par,const char ** pp,bool doEval,const char * func)261*6eef5f0cSAntonio Huete Jimenez ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func)
262*6eef5f0cSAntonio Huete Jimenez {
263*6eef5f0cSAntonio Huete Jimenez 	const char *p = *pp;
264*6eef5f0cSAntonio Huete Jimenez 	char *res;
265*6eef5f0cSAntonio Huete Jimenez 
266*6eef5f0cSAntonio Huete Jimenez 	p++;			/* Skip opening '(' - verified by caller */
267*6eef5f0cSAntonio Huete Jimenez 	cpp_skip_hspace(&p);
268*6eef5f0cSAntonio Huete Jimenez 	res = ParseWord(&p, doEval);
269a34d5fb1SAntonio Huete Jimenez 	cpp_skip_hspace(&p);
27001e196c8SJohn Marino 
271*6eef5f0cSAntonio Huete Jimenez 	if (*p++ != ')') {
272*6eef5f0cSAntonio Huete Jimenez 		int len = 0;
273*6eef5f0cSAntonio Huete Jimenez 		while (ch_isalpha(func[len]))
274*6eef5f0cSAntonio Huete Jimenez 			len++;
275*6eef5f0cSAntonio Huete Jimenez 
276a34d5fb1SAntonio Huete Jimenez 		Parse_Error(PARSE_FATAL,
277*6eef5f0cSAntonio Huete Jimenez 		    "Missing closing parenthesis for %.*s()", len, func);
278*6eef5f0cSAntonio Huete Jimenez 		par->printedError = true;
279*6eef5f0cSAntonio Huete Jimenez 		free(res);
280*6eef5f0cSAntonio Huete Jimenez 		return NULL;
28101e196c8SJohn Marino 	}
28201e196c8SJohn Marino 
283a34d5fb1SAntonio Huete Jimenez 	*pp = p;
284*6eef5f0cSAntonio Huete Jimenez 	return res;
28501e196c8SJohn Marino }
286a34d5fb1SAntonio Huete Jimenez 
287*6eef5f0cSAntonio Huete Jimenez /* See if the given variable is defined. */
288*6eef5f0cSAntonio Huete Jimenez static bool
FuncDefined(const char * var)289*6eef5f0cSAntonio Huete Jimenez FuncDefined(const char *var)
29001e196c8SJohn Marino {
291*6eef5f0cSAntonio Huete Jimenez 	return Var_Exists(SCOPE_CMDLINE, var);
29201e196c8SJohn Marino }
293a34d5fb1SAntonio Huete Jimenez 
294*6eef5f0cSAntonio Huete Jimenez /* See if a target matching targetPattern is requested to be made. */
295*6eef5f0cSAntonio Huete Jimenez static bool
FuncMake(const char * targetPattern)296*6eef5f0cSAntonio Huete Jimenez FuncMake(const char *targetPattern)
29701e196c8SJohn Marino {
298a34d5fb1SAntonio Huete Jimenez 	StringListNode *ln;
299a34d5fb1SAntonio Huete Jimenez 
300a34d5fb1SAntonio Huete Jimenez 	for (ln = opts.create.first; ln != NULL; ln = ln->next)
301*6eef5f0cSAntonio Huete Jimenez 		if (Str_Match(ln->datum, targetPattern))
302*6eef5f0cSAntonio Huete Jimenez 			return true;
303*6eef5f0cSAntonio Huete Jimenez 	return false;
30401e196c8SJohn Marino }
305a34d5fb1SAntonio Huete Jimenez 
306a34d5fb1SAntonio Huete Jimenez /* See if the given file exists. */
307*6eef5f0cSAntonio Huete Jimenez static bool
FuncExists(const char * file)308*6eef5f0cSAntonio Huete Jimenez FuncExists(const char *file)
30901e196c8SJohn Marino {
310*6eef5f0cSAntonio Huete Jimenez 	bool result;
31101e196c8SJohn Marino 	char *path;
31201e196c8SJohn Marino 
313*6eef5f0cSAntonio Huete Jimenez 	path = Dir_FindFile(file, &dirSearchPath);
314a34d5fb1SAntonio Huete Jimenez 	DEBUG2(COND, "exists(%s) result is \"%s\"\n",
315*6eef5f0cSAntonio Huete Jimenez 	    file, path != NULL ? path : "");
316a34d5fb1SAntonio Huete Jimenez 	result = path != NULL;
31701e196c8SJohn Marino 	free(path);
318ca58f742SDaniel Fojt 	return result;
31901e196c8SJohn Marino }
32001e196c8SJohn Marino 
321a34d5fb1SAntonio Huete Jimenez /* See if the given node exists and is an actual target. */
322*6eef5f0cSAntonio Huete Jimenez static bool
FuncTarget(const char * node)323*6eef5f0cSAntonio Huete Jimenez FuncTarget(const char *node)
324a34d5fb1SAntonio Huete Jimenez {
325*6eef5f0cSAntonio Huete Jimenez 	GNode *gn = Targ_FindNode(node);
326a34d5fb1SAntonio Huete Jimenez 	return gn != NULL && GNode_IsTarget(gn);
32701e196c8SJohn Marino }
32801e196c8SJohn Marino 
329a34d5fb1SAntonio Huete Jimenez /*
33001e196c8SJohn Marino  * See if the given node exists and is an actual target with commands
33101e196c8SJohn Marino  * associated with it.
33201e196c8SJohn Marino  */
333*6eef5f0cSAntonio Huete Jimenez static bool
FuncCommands(const char * node)334*6eef5f0cSAntonio Huete Jimenez FuncCommands(const char *node)
33501e196c8SJohn Marino {
336*6eef5f0cSAntonio Huete Jimenez 	GNode *gn = Targ_FindNode(node);
337*6eef5f0cSAntonio Huete Jimenez 	return gn != NULL && GNode_IsTarget(gn) &&
338*6eef5f0cSAntonio Huete Jimenez 	       !Lst_IsEmpty(&gn->commands);
33901e196c8SJohn Marino }
340a34d5fb1SAntonio Huete Jimenez 
341a34d5fb1SAntonio Huete Jimenez /*
342*6eef5f0cSAntonio Huete Jimenez  * Convert the string into a floating-point number.  Accepted formats are
343*6eef5f0cSAntonio Huete Jimenez  * base-10 integer, base-16 integer and finite floating point numbers.
34401e196c8SJohn Marino  */
345*6eef5f0cSAntonio Huete Jimenez static bool
TryParseNumber(const char * str,double * out_value)346a34d5fb1SAntonio Huete Jimenez TryParseNumber(const char *str, double *out_value)
34701e196c8SJohn Marino {
348a34d5fb1SAntonio Huete Jimenez 	char *end;
349a34d5fb1SAntonio Huete Jimenez 	unsigned long ul_val;
350a34d5fb1SAntonio Huete Jimenez 	double dbl_val;
35101e196c8SJohn Marino 
352a34d5fb1SAntonio Huete Jimenez 	if (str[0] == '\0') {	/* XXX: why is an empty string a number? */
353a34d5fb1SAntonio Huete Jimenez 		*out_value = 0.0;
354*6eef5f0cSAntonio Huete Jimenez 		return true;
35501e196c8SJohn Marino 	}
35601e196c8SJohn Marino 
357*6eef5f0cSAntonio Huete Jimenez 	errno = 0;
358a34d5fb1SAntonio Huete Jimenez 	ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10);
359a34d5fb1SAntonio Huete Jimenez 	if (*end == '\0' && errno != ERANGE) {
360a34d5fb1SAntonio Huete Jimenez 		*out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val;
361*6eef5f0cSAntonio Huete Jimenez 		return true;
362a34d5fb1SAntonio Huete Jimenez 	}
363a34d5fb1SAntonio Huete Jimenez 
364a34d5fb1SAntonio Huete Jimenez 	if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E')
365*6eef5f0cSAntonio Huete Jimenez 		return false;	/* skip the expensive strtod call */
366a34d5fb1SAntonio Huete Jimenez 	dbl_val = strtod(str, &end);
367a34d5fb1SAntonio Huete Jimenez 	if (*end != '\0')
368*6eef5f0cSAntonio Huete Jimenez 		return false;
369a34d5fb1SAntonio Huete Jimenez 
370a34d5fb1SAntonio Huete Jimenez 	*out_value = dbl_val;
371*6eef5f0cSAntonio Huete Jimenez 	return true;
372a34d5fb1SAntonio Huete Jimenez }
373a34d5fb1SAntonio Huete Jimenez 
374*6eef5f0cSAntonio Huete Jimenez static bool
is_separator(char ch)375a34d5fb1SAntonio Huete Jimenez is_separator(char ch)
376a34d5fb1SAntonio Huete Jimenez {
377*6eef5f0cSAntonio Huete Jimenez 	return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' ||
378*6eef5f0cSAntonio Huete Jimenez 	       ch == '>' || ch == '<' || ch == ')' /* but not '(' */;
379a34d5fb1SAntonio Huete Jimenez }
380a34d5fb1SAntonio Huete Jimenez 
381a34d5fb1SAntonio Huete Jimenez /*
382a34d5fb1SAntonio Huete Jimenez  * In a quoted or unquoted string literal or a number, parse a variable
383a34d5fb1SAntonio Huete Jimenez  * expression.
384a34d5fb1SAntonio Huete Jimenez  *
385a34d5fb1SAntonio Huete Jimenez  * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX}
386a34d5fb1SAntonio Huete Jimenez  */
387*6eef5f0cSAntonio Huete Jimenez static bool
CondParser_StringExpr(CondParser * par,const char * start,bool doEval,bool quoted,Buffer * buf,FStr * inout_str)388a34d5fb1SAntonio Huete Jimenez CondParser_StringExpr(CondParser *par, const char *start,
389*6eef5f0cSAntonio Huete Jimenez 		      bool doEval, bool quoted,
390*6eef5f0cSAntonio Huete Jimenez 		      Buffer *buf, FStr *inout_str)
391a34d5fb1SAntonio Huete Jimenez {
392*6eef5f0cSAntonio Huete Jimenez 	VarEvalMode emode;
393*6eef5f0cSAntonio Huete Jimenez 	const char *p;
394*6eef5f0cSAntonio Huete Jimenez 	bool atStart;
395a34d5fb1SAntonio Huete Jimenez 	VarParseResult parseResult;
396a34d5fb1SAntonio Huete Jimenez 
397*6eef5f0cSAntonio Huete Jimenez 	emode = doEval && quoted ? VARE_WANTRES
398*6eef5f0cSAntonio Huete Jimenez 	    : doEval ? VARE_UNDEFERR
399*6eef5f0cSAntonio Huete Jimenez 	    : VARE_PARSE_ONLY;
400a34d5fb1SAntonio Huete Jimenez 
401*6eef5f0cSAntonio Huete Jimenez 	p = par->p;
402*6eef5f0cSAntonio Huete Jimenez 	atStart = p == start;
403*6eef5f0cSAntonio Huete Jimenez 	parseResult = Var_Parse(&p, SCOPE_CMDLINE, emode, inout_str);
404a34d5fb1SAntonio Huete Jimenez 	/* TODO: handle errors */
405a34d5fb1SAntonio Huete Jimenez 	if (inout_str->str == var_Error) {
406a34d5fb1SAntonio Huete Jimenez 		if (parseResult == VPR_ERR) {
407a34d5fb1SAntonio Huete Jimenez 			/*
408a34d5fb1SAntonio Huete Jimenez 			 * FIXME: Even if an error occurs, there is no
409a34d5fb1SAntonio Huete Jimenez 			 *  guarantee that it is reported.
410a34d5fb1SAntonio Huete Jimenez 			 *
411a34d5fb1SAntonio Huete Jimenez 			 * See cond-token-plain.mk $$$$$$$$.
412a34d5fb1SAntonio Huete Jimenez 			 */
413*6eef5f0cSAntonio Huete Jimenez 			par->printedError = true;
414a34d5fb1SAntonio Huete Jimenez 		}
415a34d5fb1SAntonio Huete Jimenez 		/*
416a34d5fb1SAntonio Huete Jimenez 		 * XXX: Can there be any situation in which a returned
417ec533708SSascha Wildner 		 * var_Error needs to be freed?
418a34d5fb1SAntonio Huete Jimenez 		 */
419a34d5fb1SAntonio Huete Jimenez 		FStr_Done(inout_str);
420a34d5fb1SAntonio Huete Jimenez 		/*
421a34d5fb1SAntonio Huete Jimenez 		 * Even if !doEval, we still report syntax errors, which is
422a34d5fb1SAntonio Huete Jimenez 		 * what getting var_Error back with !doEval means.
423a34d5fb1SAntonio Huete Jimenez 		 */
424a34d5fb1SAntonio Huete Jimenez 		*inout_str = FStr_InitRefer(NULL);
425*6eef5f0cSAntonio Huete Jimenez 		return false;
426a34d5fb1SAntonio Huete Jimenez 	}
427*6eef5f0cSAntonio Huete Jimenez 	par->p = p;
428a34d5fb1SAntonio Huete Jimenez 
429a34d5fb1SAntonio Huete Jimenez 	/*
430a34d5fb1SAntonio Huete Jimenez 	 * If the '$' started the string literal (which means no quotes), and
431a34d5fb1SAntonio Huete Jimenez 	 * the variable expression is followed by a space, looks like a
432a34d5fb1SAntonio Huete Jimenez 	 * comparison operator or is the end of the expression, we are done.
433a34d5fb1SAntonio Huete Jimenez 	 */
434a34d5fb1SAntonio Huete Jimenez 	if (atStart && is_separator(par->p[0]))
435*6eef5f0cSAntonio Huete Jimenez 		return false;
436a34d5fb1SAntonio Huete Jimenez 
437a34d5fb1SAntonio Huete Jimenez 	Buf_AddStr(buf, inout_str->str);
438a34d5fb1SAntonio Huete Jimenez 	FStr_Done(inout_str);
439a34d5fb1SAntonio Huete Jimenez 	*inout_str = FStr_InitRefer(NULL);	/* not finished yet */
440*6eef5f0cSAntonio Huete Jimenez 	return true;
441a34d5fb1SAntonio Huete Jimenez }
442a34d5fb1SAntonio Huete Jimenez 
443a34d5fb1SAntonio Huete Jimenez /*
444*6eef5f0cSAntonio Huete Jimenez  * Parse a string from a variable expression or an optionally quoted string,
445*6eef5f0cSAntonio Huete Jimenez  * on the left-hand and right-hand sides of comparisons.
44601e196c8SJohn Marino  *
44701e196c8SJohn Marino  * Results:
448*6eef5f0cSAntonio Huete Jimenez  *	Returns the string without any enclosing quotes, or NULL on error.
449*6eef5f0cSAntonio Huete Jimenez  *	Sets out_quoted if the leaf was a quoted string literal.
45001e196c8SJohn Marino  */
451a34d5fb1SAntonio Huete Jimenez static void
CondParser_Leaf(CondParser * par,bool doEval,bool unquotedOK,FStr * out_str,bool * out_quoted)452*6eef5f0cSAntonio Huete Jimenez CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
453*6eef5f0cSAntonio Huete Jimenez 		  FStr *out_str, bool *out_quoted)
45401e196c8SJohn Marino {
45501e196c8SJohn Marino 	Buffer buf;
456a34d5fb1SAntonio Huete Jimenez 	FStr str;
457*6eef5f0cSAntonio Huete Jimenez 	bool quoted;
458a34d5fb1SAntonio Huete Jimenez 	const char *start;
45901e196c8SJohn Marino 
460a34d5fb1SAntonio Huete Jimenez 	Buf_Init(&buf);
461a34d5fb1SAntonio Huete Jimenez 	str = FStr_InitRefer(NULL);
462a34d5fb1SAntonio Huete Jimenez 	*out_quoted = quoted = par->p[0] == '"';
463a34d5fb1SAntonio Huete Jimenez 	start = par->p;
464a34d5fb1SAntonio Huete Jimenez 	if (quoted)
465a34d5fb1SAntonio Huete Jimenez 		par->p++;
466a34d5fb1SAntonio Huete Jimenez 
467a34d5fb1SAntonio Huete Jimenez 	while (par->p[0] != '\0' && str.str == NULL) {
468a34d5fb1SAntonio Huete Jimenez 		switch (par->p[0]) {
46901e196c8SJohn Marino 		case '\\':
470a34d5fb1SAntonio Huete Jimenez 			par->p++;
471a34d5fb1SAntonio Huete Jimenez 			if (par->p[0] != '\0') {
472a34d5fb1SAntonio Huete Jimenez 				Buf_AddByte(&buf, par->p[0]);
473a34d5fb1SAntonio Huete Jimenez 				par->p++;
47401e196c8SJohn Marino 			}
475a34d5fb1SAntonio Huete Jimenez 			continue;
47601e196c8SJohn Marino 		case '"':
477a34d5fb1SAntonio Huete Jimenez 			par->p++;
478a34d5fb1SAntonio Huete Jimenez 			if (quoted)
479*6eef5f0cSAntonio Huete Jimenez 				goto return_buf;	/* skip the closing quote */
480a34d5fb1SAntonio Huete Jimenez 			Buf_AddByte(&buf, '"');
481a34d5fb1SAntonio Huete Jimenez 			continue;
482a34d5fb1SAntonio Huete Jimenez 		case ')':	/* see is_separator */
48301e196c8SJohn Marino 		case '!':
48401e196c8SJohn Marino 		case '=':
48501e196c8SJohn Marino 		case '>':
48601e196c8SJohn Marino 		case '<':
48701e196c8SJohn Marino 		case ' ':
48801e196c8SJohn Marino 		case '\t':
489a34d5fb1SAntonio Huete Jimenez 			if (!quoted)
490*6eef5f0cSAntonio Huete Jimenez 				goto return_buf;
491a34d5fb1SAntonio Huete Jimenez 			Buf_AddByte(&buf, par->p[0]);
492a34d5fb1SAntonio Huete Jimenez 			par->p++;
493a34d5fb1SAntonio Huete Jimenez 			continue;
49401e196c8SJohn Marino 		case '$':
495a34d5fb1SAntonio Huete Jimenez 			if (!CondParser_StringExpr(par,
496a34d5fb1SAntonio Huete Jimenez 			    start, doEval, quoted, &buf, &str))
497*6eef5f0cSAntonio Huete Jimenez 				goto return_str;
498a34d5fb1SAntonio Huete Jimenez 			continue;
49901e196c8SJohn Marino 		default:
500*6eef5f0cSAntonio Huete Jimenez 			if (!unquotedOK && !quoted && *start != '$' &&
501a34d5fb1SAntonio Huete Jimenez 			    !ch_isdigit(*start)) {
502a34d5fb1SAntonio Huete Jimenez 				/*
503a34d5fb1SAntonio Huete Jimenez 				 * The left-hand side must be quoted,
504*6eef5f0cSAntonio Huete Jimenez 				 * a variable expression or a number.
505a34d5fb1SAntonio Huete Jimenez 				 */
506a34d5fb1SAntonio Huete Jimenez 				str = FStr_InitRefer(NULL);
507*6eef5f0cSAntonio Huete Jimenez 				goto return_str;
508f445c897SJohn Marino 			}
509a34d5fb1SAntonio Huete Jimenez 			Buf_AddByte(&buf, par->p[0]);
510a34d5fb1SAntonio Huete Jimenez 			par->p++;
511a34d5fb1SAntonio Huete Jimenez 			continue;
51201e196c8SJohn Marino 		}
51301e196c8SJohn Marino 	}
514*6eef5f0cSAntonio Huete Jimenez return_buf:
515a34d5fb1SAntonio Huete Jimenez 	str = FStr_InitOwn(buf.data);
516*6eef5f0cSAntonio Huete Jimenez 	buf.data = NULL;
517*6eef5f0cSAntonio Huete Jimenez return_str:
518*6eef5f0cSAntonio Huete Jimenez 	Buf_Done(&buf);
519a34d5fb1SAntonio Huete Jimenez 	*out_str = str;
52001e196c8SJohn Marino }
521a34d5fb1SAntonio Huete Jimenez 
522a34d5fb1SAntonio Huete Jimenez /*
523a34d5fb1SAntonio Huete Jimenez  * Evaluate a "comparison without operator", such as in ".if ${VAR}" or
524a34d5fb1SAntonio Huete Jimenez  * ".if 0".
525a34d5fb1SAntonio Huete Jimenez  */
526*6eef5f0cSAntonio Huete Jimenez static bool
EvalNotEmpty(CondParser * par,const char * value,bool quoted)527*6eef5f0cSAntonio Huete Jimenez EvalNotEmpty(CondParser *par, const char *value, bool quoted)
528a34d5fb1SAntonio Huete Jimenez {
529a34d5fb1SAntonio Huete Jimenez 	double num;
530a34d5fb1SAntonio Huete Jimenez 
531a34d5fb1SAntonio Huete Jimenez 	/* For .ifxxx "...", check for non-empty string. */
532a34d5fb1SAntonio Huete Jimenez 	if (quoted)
533a34d5fb1SAntonio Huete Jimenez 		return value[0] != '\0';
534a34d5fb1SAntonio Huete Jimenez 
535a34d5fb1SAntonio Huete Jimenez 	/* For .ifxxx <number>, compare against zero */
536a34d5fb1SAntonio Huete Jimenez 	if (TryParseNumber(value, &num))
537a34d5fb1SAntonio Huete Jimenez 		return num != 0.0;
538a34d5fb1SAntonio Huete Jimenez 
539*6eef5f0cSAntonio Huete Jimenez 	/*
540*6eef5f0cSAntonio Huete Jimenez 	 * For .if ${...}, check for non-empty string.  This is different
541*6eef5f0cSAntonio Huete Jimenez 	 * from the evaluation function from that .if variant, which would
542*6eef5f0cSAntonio Huete Jimenez 	 * test whether a variable of the given name were defined.
543*6eef5f0cSAntonio Huete Jimenez 	 */
544*6eef5f0cSAntonio Huete Jimenez 	/*
545*6eef5f0cSAntonio Huete Jimenez 	 * XXX: Whitespace should count as empty, just as in
546*6eef5f0cSAntonio Huete Jimenez 	 * CondParser_FuncCallEmpty.
547*6eef5f0cSAntonio Huete Jimenez 	 */
548a34d5fb1SAntonio Huete Jimenez 	if (par->plain)
549a34d5fb1SAntonio Huete Jimenez 		return value[0] != '\0';
550a34d5fb1SAntonio Huete Jimenez 
551*6eef5f0cSAntonio Huete Jimenez 	return par->evalBare(value) != par->negateEvalBare;
552a34d5fb1SAntonio Huete Jimenez }
553a34d5fb1SAntonio Huete Jimenez 
554a34d5fb1SAntonio Huete Jimenez /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
555*6eef5f0cSAntonio Huete Jimenez static bool
EvalCompareNum(double lhs,ComparisonOp op,double rhs)556a34d5fb1SAntonio Huete Jimenez EvalCompareNum(double lhs, ComparisonOp op, double rhs)
557a34d5fb1SAntonio Huete Jimenez {
558*6eef5f0cSAntonio Huete Jimenez 	DEBUG3(COND, "Comparing %f %s %f\n", lhs, opname[op], rhs);
559a34d5fb1SAntonio Huete Jimenez 
560a34d5fb1SAntonio Huete Jimenez 	switch (op) {
561a34d5fb1SAntonio Huete Jimenez 	case LT:
562a34d5fb1SAntonio Huete Jimenez 		return lhs < rhs;
563a34d5fb1SAntonio Huete Jimenez 	case LE:
564a34d5fb1SAntonio Huete Jimenez 		return lhs <= rhs;
565a34d5fb1SAntonio Huete Jimenez 	case GT:
566a34d5fb1SAntonio Huete Jimenez 		return lhs > rhs;
567a34d5fb1SAntonio Huete Jimenez 	case GE:
568a34d5fb1SAntonio Huete Jimenez 		return lhs >= rhs;
569*6eef5f0cSAntonio Huete Jimenez 	case EQ:
570a34d5fb1SAntonio Huete Jimenez 		return lhs == rhs;
571*6eef5f0cSAntonio Huete Jimenez 	default:
572*6eef5f0cSAntonio Huete Jimenez 		return lhs != rhs;
573a34d5fb1SAntonio Huete Jimenez 	}
574a34d5fb1SAntonio Huete Jimenez }
575a34d5fb1SAntonio Huete Jimenez 
576a34d5fb1SAntonio Huete Jimenez static Token
EvalCompareStr(CondParser * par,const char * lhs,ComparisonOp op,const char * rhs)577a34d5fb1SAntonio Huete Jimenez EvalCompareStr(CondParser *par, const char *lhs,
578a34d5fb1SAntonio Huete Jimenez 	       ComparisonOp op, const char *rhs)
579a34d5fb1SAntonio Huete Jimenez {
580a34d5fb1SAntonio Huete Jimenez 	if (op != EQ && op != NE) {
581a34d5fb1SAntonio Huete Jimenez 		Parse_Error(PARSE_FATAL,
582*6eef5f0cSAntonio Huete Jimenez 		    "Comparison with '%s' requires both operands "
583*6eef5f0cSAntonio Huete Jimenez 		    "'%s' and '%s' to be numeric",
584*6eef5f0cSAntonio Huete Jimenez 		    opname[op], lhs, rhs);
585*6eef5f0cSAntonio Huete Jimenez 		par->printedError = true;
586a34d5fb1SAntonio Huete Jimenez 		return TOK_ERROR;
587a34d5fb1SAntonio Huete Jimenez 	}
588a34d5fb1SAntonio Huete Jimenez 
589*6eef5f0cSAntonio Huete Jimenez 	DEBUG3(COND, "Comparing \"%s\" %s \"%s\"\n", lhs, opname[op], rhs);
590a34d5fb1SAntonio Huete Jimenez 	return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0));
591a34d5fb1SAntonio Huete Jimenez }
592a34d5fb1SAntonio Huete Jimenez 
593a34d5fb1SAntonio Huete Jimenez /* Evaluate a comparison, such as "${VAR} == 12345". */
594a34d5fb1SAntonio Huete Jimenez static Token
EvalCompare(CondParser * par,const char * lhs,bool lhsQuoted,ComparisonOp op,const char * rhs,bool rhsQuoted)595*6eef5f0cSAntonio Huete Jimenez EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted,
596*6eef5f0cSAntonio Huete Jimenez 	    ComparisonOp op, const char *rhs, bool rhsQuoted)
597a34d5fb1SAntonio Huete Jimenez {
59801e196c8SJohn Marino 	double left, right;
59901e196c8SJohn Marino 
600a34d5fb1SAntonio Huete Jimenez 	if (!rhsQuoted && !lhsQuoted)
601a34d5fb1SAntonio Huete Jimenez 		if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
602a34d5fb1SAntonio Huete Jimenez 			return ToToken(EvalCompareNum(left, op, right));
603a34d5fb1SAntonio Huete Jimenez 
604a34d5fb1SAntonio Huete Jimenez 	return EvalCompareStr(par, lhs, op, rhs);
605a34d5fb1SAntonio Huete Jimenez }
606a34d5fb1SAntonio Huete Jimenez 
607*6eef5f0cSAntonio Huete Jimenez static bool
CondParser_ComparisonOp(CondParser * par,ComparisonOp * out_op)608a34d5fb1SAntonio Huete Jimenez CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
609a34d5fb1SAntonio Huete Jimenez {
610a34d5fb1SAntonio Huete Jimenez 	const char *p = par->p;
611a34d5fb1SAntonio Huete Jimenez 
612*6eef5f0cSAntonio Huete Jimenez 	if (p[0] == '<' && p[1] == '=')
613*6eef5f0cSAntonio Huete Jimenez 		return par->p += 2, *out_op = LE, true;
614*6eef5f0cSAntonio Huete Jimenez 	if (p[0] == '<')
615*6eef5f0cSAntonio Huete Jimenez 		return par->p += 1, *out_op = LT, true;
616*6eef5f0cSAntonio Huete Jimenez 	if (p[0] == '>' && p[1] == '=')
617*6eef5f0cSAntonio Huete Jimenez 		return par->p += 2, *out_op = GE, true;
618*6eef5f0cSAntonio Huete Jimenez 	if (p[0] == '>')
619*6eef5f0cSAntonio Huete Jimenez 		return par->p += 1, *out_op = GT, true;
620*6eef5f0cSAntonio Huete Jimenez 	if (p[0] == '=' && p[1] == '=')
621*6eef5f0cSAntonio Huete Jimenez 		return par->p += 2, *out_op = EQ, true;
622*6eef5f0cSAntonio Huete Jimenez 	if (p[0] == '!' && p[1] == '=')
623*6eef5f0cSAntonio Huete Jimenez 		return par->p += 2, *out_op = NE, true;
624*6eef5f0cSAntonio Huete Jimenez 	return false;
625a34d5fb1SAntonio Huete Jimenez }
626a34d5fb1SAntonio Huete Jimenez 
627a34d5fb1SAntonio Huete Jimenez /*
628a34d5fb1SAntonio Huete Jimenez  * Parse a comparison condition such as:
629a34d5fb1SAntonio Huete Jimenez  *
630a34d5fb1SAntonio Huete Jimenez  *	0
631a34d5fb1SAntonio Huete Jimenez  *	${VAR:Mpattern}
632a34d5fb1SAntonio Huete Jimenez  *	${VAR} == value
633a34d5fb1SAntonio Huete Jimenez  *	${VAR:U0} < 12345
634a34d5fb1SAntonio Huete Jimenez  */
635a34d5fb1SAntonio Huete Jimenez static Token
CondParser_Comparison(CondParser * par,bool doEval)636*6eef5f0cSAntonio Huete Jimenez CondParser_Comparison(CondParser *par, bool doEval)
637a34d5fb1SAntonio Huete Jimenez {
638a34d5fb1SAntonio Huete Jimenez 	Token t = TOK_ERROR;
639a34d5fb1SAntonio Huete Jimenez 	FStr lhs, rhs;
640a34d5fb1SAntonio Huete Jimenez 	ComparisonOp op;
641*6eef5f0cSAntonio Huete Jimenez 	bool lhsQuoted, rhsQuoted;
64201e196c8SJohn Marino 
643*6eef5f0cSAntonio Huete Jimenez 	CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhs, &lhsQuoted);
644a34d5fb1SAntonio Huete Jimenez 	if (lhs.str == NULL)
645a34d5fb1SAntonio Huete Jimenez 		goto done_lhs;
64601e196c8SJohn Marino 
647a34d5fb1SAntonio Huete Jimenez 	CondParser_SkipWhitespace(par);
64801e196c8SJohn Marino 
649a34d5fb1SAntonio Huete Jimenez 	if (!CondParser_ComparisonOp(par, &op)) {
650a34d5fb1SAntonio Huete Jimenez 		/* Unknown operator, compare against an empty string or 0. */
651a34d5fb1SAntonio Huete Jimenez 		t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted));
652a34d5fb1SAntonio Huete Jimenez 		goto done_lhs;
65301e196c8SJohn Marino 	}
65401e196c8SJohn Marino 
655a34d5fb1SAntonio Huete Jimenez 	CondParser_SkipWhitespace(par);
65601e196c8SJohn Marino 
657a34d5fb1SAntonio Huete Jimenez 	if (par->p[0] == '\0') {
658a34d5fb1SAntonio Huete Jimenez 		Parse_Error(PARSE_FATAL,
659*6eef5f0cSAntonio Huete Jimenez 		    "Missing right-hand side of operator '%s'", opname[op]);
660*6eef5f0cSAntonio Huete Jimenez 		par->printedError = true;
661a34d5fb1SAntonio Huete Jimenez 		goto done_lhs;
66201e196c8SJohn Marino 	}
66301e196c8SJohn Marino 
664*6eef5f0cSAntonio Huete Jimenez 	CondParser_Leaf(par, doEval, true, &rhs, &rhsQuoted);
665*6eef5f0cSAntonio Huete Jimenez 	t = rhs.str == NULL ? TOK_ERROR
666*6eef5f0cSAntonio Huete Jimenez 	    : !doEval ? TOK_FALSE
667*6eef5f0cSAntonio Huete Jimenez 	    : EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
668a34d5fb1SAntonio Huete Jimenez 	FStr_Done(&rhs);
669*6eef5f0cSAntonio Huete Jimenez 
670a34d5fb1SAntonio Huete Jimenez done_lhs:
671a34d5fb1SAntonio Huete Jimenez 	FStr_Done(&lhs);
67201e196c8SJohn Marino 	return t;
67301e196c8SJohn Marino }
67401e196c8SJohn Marino 
67501e196c8SJohn Marino /*
676a34d5fb1SAntonio Huete Jimenez  * The argument to empty() is a variable name, optionally followed by
677a34d5fb1SAntonio Huete Jimenez  * variable modifiers.
67801e196c8SJohn Marino  */
679*6eef5f0cSAntonio Huete Jimenez static bool
CondParser_FuncCallEmpty(CondParser * par,bool doEval,Token * out_token)680*6eef5f0cSAntonio Huete Jimenez CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
681a34d5fb1SAntonio Huete Jimenez {
682a34d5fb1SAntonio Huete Jimenez 	const char *cp = par->p;
683*6eef5f0cSAntonio Huete Jimenez 	Token tok;
684*6eef5f0cSAntonio Huete Jimenez 	FStr val;
68501e196c8SJohn Marino 
686*6eef5f0cSAntonio Huete Jimenez 	if (!skip_string(&cp, "empty"))
687*6eef5f0cSAntonio Huete Jimenez 		return false;
688a34d5fb1SAntonio Huete Jimenez 
689a34d5fb1SAntonio Huete Jimenez 	cpp_skip_whitespace(&cp);
69001e196c8SJohn Marino 	if (*cp != '(')
691*6eef5f0cSAntonio Huete Jimenez 		return false;
69201e196c8SJohn Marino 
693*6eef5f0cSAntonio Huete Jimenez 	cp--;			/* Make cp[1] point to the '('. */
694*6eef5f0cSAntonio Huete Jimenez 	(void)Var_Parse(&cp, SCOPE_CMDLINE,
695*6eef5f0cSAntonio Huete Jimenez 	    doEval ? VARE_WANTRES : VARE_PARSE_ONLY, &val);
696*6eef5f0cSAntonio Huete Jimenez 	/* TODO: handle errors */
697*6eef5f0cSAntonio Huete Jimenez 
698*6eef5f0cSAntonio Huete Jimenez 	if (val.str == var_Error)
699*6eef5f0cSAntonio Huete Jimenez 		tok = TOK_ERROR;
700*6eef5f0cSAntonio Huete Jimenez 	else {
701*6eef5f0cSAntonio Huete Jimenez 		cpp_skip_whitespace(&val.str);
702*6eef5f0cSAntonio Huete Jimenez 		tok = ToToken(doEval && val.str[0] == '\0');
70301e196c8SJohn Marino 	}
70401e196c8SJohn Marino 
705*6eef5f0cSAntonio Huete Jimenez 	FStr_Done(&val);
706*6eef5f0cSAntonio Huete Jimenez 	*out_token = tok;
707*6eef5f0cSAntonio Huete Jimenez 	par->p = cp;
708*6eef5f0cSAntonio Huete Jimenez 	return true;
709*6eef5f0cSAntonio Huete Jimenez }
710*6eef5f0cSAntonio Huete Jimenez 
711*6eef5f0cSAntonio Huete Jimenez /* Parse a function call expression, such as 'exists(${file})'. */
712*6eef5f0cSAntonio Huete Jimenez static bool
CondParser_FuncCall(CondParser * par,bool doEval,Token * out_token)713*6eef5f0cSAntonio Huete Jimenez CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
714*6eef5f0cSAntonio Huete Jimenez {
715*6eef5f0cSAntonio Huete Jimenez 	char *arg;
716*6eef5f0cSAntonio Huete Jimenez 	const char *p = par->p;
717*6eef5f0cSAntonio Huete Jimenez 	bool (*fn)(const char *);
718*6eef5f0cSAntonio Huete Jimenez 	const char *fn_name = p;
719*6eef5f0cSAntonio Huete Jimenez 
720*6eef5f0cSAntonio Huete Jimenez 	if (skip_string(&p, "defined"))
721*6eef5f0cSAntonio Huete Jimenez 		fn = FuncDefined;
722*6eef5f0cSAntonio Huete Jimenez 	else if (skip_string(&p, "make"))
723*6eef5f0cSAntonio Huete Jimenez 		fn = FuncMake;
724*6eef5f0cSAntonio Huete Jimenez 	else if (skip_string(&p, "exists"))
725*6eef5f0cSAntonio Huete Jimenez 		fn = FuncExists;
726*6eef5f0cSAntonio Huete Jimenez 	else if (skip_string(&p, "target"))
727*6eef5f0cSAntonio Huete Jimenez 		fn = FuncTarget;
728*6eef5f0cSAntonio Huete Jimenez 	else if (skip_string(&p, "commands"))
729*6eef5f0cSAntonio Huete Jimenez 		fn = FuncCommands;
730*6eef5f0cSAntonio Huete Jimenez 	else
731*6eef5f0cSAntonio Huete Jimenez 		return false;
732*6eef5f0cSAntonio Huete Jimenez 
733*6eef5f0cSAntonio Huete Jimenez 	cpp_skip_whitespace(&p);
734*6eef5f0cSAntonio Huete Jimenez 	if (*p != '(')
735*6eef5f0cSAntonio Huete Jimenez 		return false;
736*6eef5f0cSAntonio Huete Jimenez 
737*6eef5f0cSAntonio Huete Jimenez 	arg = ParseFuncArg(par, &p, doEval, fn_name);
738*6eef5f0cSAntonio Huete Jimenez 	*out_token = ToToken(doEval &&
739*6eef5f0cSAntonio Huete Jimenez 	    arg != NULL && arg[0] != '\0' && fn(arg));
740a34d5fb1SAntonio Huete Jimenez 	free(arg);
741a34d5fb1SAntonio Huete Jimenez 
742*6eef5f0cSAntonio Huete Jimenez 	par->p = p;
743*6eef5f0cSAntonio Huete Jimenez 	return true;
744a34d5fb1SAntonio Huete Jimenez }
745a34d5fb1SAntonio Huete Jimenez 
746a34d5fb1SAntonio Huete Jimenez /*
747*6eef5f0cSAntonio Huete Jimenez  * Parse a comparison that neither starts with '"' nor '$', such as the
748*6eef5f0cSAntonio Huete Jimenez  * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without
749*6eef5f0cSAntonio Huete Jimenez  * operator, which is a number, a variable expression or a string literal.
750*6eef5f0cSAntonio Huete Jimenez  *
751*6eef5f0cSAntonio Huete Jimenez  * TODO: Can this be merged into CondParser_Comparison?
752a34d5fb1SAntonio Huete Jimenez  */
753a34d5fb1SAntonio Huete Jimenez static Token
CondParser_ComparisonOrLeaf(CondParser * par,bool doEval)754*6eef5f0cSAntonio Huete Jimenez CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
755a34d5fb1SAntonio Huete Jimenez {
756a34d5fb1SAntonio Huete Jimenez 	Token t;
757*6eef5f0cSAntonio Huete Jimenez 	char *arg;
758a34d5fb1SAntonio Huete Jimenez 	const char *cp;
759a34d5fb1SAntonio Huete Jimenez 
76001e196c8SJohn Marino 	/* Push anything numeric through the compare expression */
761a34d5fb1SAntonio Huete Jimenez 	cp = par->p;
762a34d5fb1SAntonio Huete Jimenez 	if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
763a34d5fb1SAntonio Huete Jimenez 		return CondParser_Comparison(par, doEval);
76401e196c8SJohn Marino 
76501e196c8SJohn Marino 	/*
76601e196c8SJohn Marino 	 * Most likely we have a naked token to apply the default function to.
76701e196c8SJohn Marino 	 * However ".if a == b" gets here when the "a" is unquoted and doesn't
76801e196c8SJohn Marino 	 * start with a '$'. This surprises people.
769a34d5fb1SAntonio Huete Jimenez 	 * If what follows the function argument is a '=' or '!' then the
770a34d5fb1SAntonio Huete Jimenez 	 * syntax would be invalid if we did "defined(a)" - so instead treat
771a34d5fb1SAntonio Huete Jimenez 	 * as an expression.
77201e196c8SJohn Marino 	 */
773*6eef5f0cSAntonio Huete Jimenez 	/*
774*6eef5f0cSAntonio Huete Jimenez 	 * XXX: In edge cases, a variable expression may be evaluated twice,
775*6eef5f0cSAntonio Huete Jimenez 	 *  see cond-token-plain.mk, keyword 'twice'.
776*6eef5f0cSAntonio Huete Jimenez 	 */
777*6eef5f0cSAntonio Huete Jimenez 	arg = ParseWord(&cp, doEval);
778*6eef5f0cSAntonio Huete Jimenez 	assert(arg[0] != '\0');
779*6eef5f0cSAntonio Huete Jimenez 
780*6eef5f0cSAntonio Huete Jimenez 	if (*cp == '=' || *cp == '!' || *cp == '<' || *cp == '>')
781a34d5fb1SAntonio Huete Jimenez 		return CondParser_Comparison(par, doEval);
782a34d5fb1SAntonio Huete Jimenez 	par->p = cp;
78301e196c8SJohn Marino 
78401e196c8SJohn Marino 	/*
78501e196c8SJohn Marino 	 * Evaluate the argument using the default function.
786a34d5fb1SAntonio Huete Jimenez 	 * This path always treats .if as .ifdef. To get here, the character
78701e196c8SJohn Marino 	 * after .if must have been taken literally, so the argument cannot
78801e196c8SJohn Marino 	 * be empty - even if it contained a variable expansion.
78901e196c8SJohn Marino 	 */
790*6eef5f0cSAntonio Huete Jimenez 	t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare);
79101e196c8SJohn Marino 	free(arg);
79201e196c8SJohn Marino 	return t;
79301e196c8SJohn Marino }
79401e196c8SJohn Marino 
795a34d5fb1SAntonio Huete Jimenez /* Return the next token or comparison result from the parser. */
79601e196c8SJohn Marino static Token
CondParser_Token(CondParser * par,bool doEval)797*6eef5f0cSAntonio Huete Jimenez CondParser_Token(CondParser *par, bool doEval)
79801e196c8SJohn Marino {
79901e196c8SJohn Marino 	Token t;
80001e196c8SJohn Marino 
801a34d5fb1SAntonio Huete Jimenez 	t = par->curr;
80201e196c8SJohn Marino 	if (t != TOK_NONE) {
803a34d5fb1SAntonio Huete Jimenez 		par->curr = TOK_NONE;
80401e196c8SJohn Marino 		return t;
80501e196c8SJohn Marino 	}
80601e196c8SJohn Marino 
807a34d5fb1SAntonio Huete Jimenez 	cpp_skip_hspace(&par->p);
80801e196c8SJohn Marino 
809a34d5fb1SAntonio Huete Jimenez 	switch (par->p[0]) {
81001e196c8SJohn Marino 
81101e196c8SJohn Marino 	case '(':
812a34d5fb1SAntonio Huete Jimenez 		par->p++;
81301e196c8SJohn Marino 		return TOK_LPAREN;
81401e196c8SJohn Marino 
81501e196c8SJohn Marino 	case ')':
816a34d5fb1SAntonio Huete Jimenez 		par->p++;
81701e196c8SJohn Marino 		return TOK_RPAREN;
81801e196c8SJohn Marino 
81901e196c8SJohn Marino 	case '|':
820a34d5fb1SAntonio Huete Jimenez 		par->p++;
821a34d5fb1SAntonio Huete Jimenez 		if (par->p[0] == '|')
822a34d5fb1SAntonio Huete Jimenez 			par->p++;
823a34d5fb1SAntonio Huete Jimenez 		else if (opts.strict) {
824a34d5fb1SAntonio Huete Jimenez 			Parse_Error(PARSE_FATAL, "Unknown operator '|'");
825*6eef5f0cSAntonio Huete Jimenez 			par->printedError = true;
826a34d5fb1SAntonio Huete Jimenez 			return TOK_ERROR;
82701e196c8SJohn Marino 		}
82801e196c8SJohn Marino 		return TOK_OR;
82901e196c8SJohn Marino 
83001e196c8SJohn Marino 	case '&':
831a34d5fb1SAntonio Huete Jimenez 		par->p++;
832a34d5fb1SAntonio Huete Jimenez 		if (par->p[0] == '&')
833a34d5fb1SAntonio Huete Jimenez 			par->p++;
834a34d5fb1SAntonio Huete Jimenez 		else if (opts.strict) {
835a34d5fb1SAntonio Huete Jimenez 			Parse_Error(PARSE_FATAL, "Unknown operator '&'");
836*6eef5f0cSAntonio Huete Jimenez 			par->printedError = true;
837a34d5fb1SAntonio Huete Jimenez 			return TOK_ERROR;
83801e196c8SJohn Marino 		}
83901e196c8SJohn Marino 		return TOK_AND;
84001e196c8SJohn Marino 
84101e196c8SJohn Marino 	case '!':
842a34d5fb1SAntonio Huete Jimenez 		par->p++;
84301e196c8SJohn Marino 		return TOK_NOT;
84401e196c8SJohn Marino 
845a34d5fb1SAntonio Huete Jimenez 	case '#':		/* XXX: see unit-tests/cond-token-plain.mk */
846a34d5fb1SAntonio Huete Jimenez 	case '\n':		/* XXX: why should this end the condition? */
847a34d5fb1SAntonio Huete Jimenez 		/* Probably obsolete now, from 1993-03-21. */
84801e196c8SJohn Marino 	case '\0':
84901e196c8SJohn Marino 		return TOK_EOF;
85001e196c8SJohn Marino 
85101e196c8SJohn Marino 	case '"':
85201e196c8SJohn Marino 	case '$':
853a34d5fb1SAntonio Huete Jimenez 		return CondParser_Comparison(par, doEval);
85401e196c8SJohn Marino 
85501e196c8SJohn Marino 	default:
856*6eef5f0cSAntonio Huete Jimenez 		if (CondParser_FuncCallEmpty(par, doEval, &t))
857*6eef5f0cSAntonio Huete Jimenez 			return t;
858*6eef5f0cSAntonio Huete Jimenez 		if (CondParser_FuncCall(par, doEval, &t))
859*6eef5f0cSAntonio Huete Jimenez 			return t;
860*6eef5f0cSAntonio Huete Jimenez 		return CondParser_ComparisonOrLeaf(par, doEval);
86101e196c8SJohn Marino 	}
86201e196c8SJohn Marino }
86301e196c8SJohn Marino 
864*6eef5f0cSAntonio Huete Jimenez /* Skip the next token if it equals t. */
865*6eef5f0cSAntonio Huete Jimenez static bool
CondParser_Skip(CondParser * par,Token t)866*6eef5f0cSAntonio Huete Jimenez CondParser_Skip(CondParser *par, Token t)
867*6eef5f0cSAntonio Huete Jimenez {
868*6eef5f0cSAntonio Huete Jimenez 	Token actual;
869*6eef5f0cSAntonio Huete Jimenez 
870*6eef5f0cSAntonio Huete Jimenez 	actual = CondParser_Token(par, false);
871*6eef5f0cSAntonio Huete Jimenez 	if (actual == t)
872*6eef5f0cSAntonio Huete Jimenez 		return true;
873*6eef5f0cSAntonio Huete Jimenez 
874*6eef5f0cSAntonio Huete Jimenez 	assert(par->curr == TOK_NONE);
875*6eef5f0cSAntonio Huete Jimenez 	assert(actual != TOK_NONE);
876*6eef5f0cSAntonio Huete Jimenez 	par->curr = actual;
877*6eef5f0cSAntonio Huete Jimenez 	return false;
878*6eef5f0cSAntonio Huete Jimenez }
879*6eef5f0cSAntonio Huete Jimenez 
880a34d5fb1SAntonio Huete Jimenez /*
881a34d5fb1SAntonio Huete Jimenez  * Term -> '(' Or ')'
882a34d5fb1SAntonio Huete Jimenez  * Term -> '!' Term
883a34d5fb1SAntonio Huete Jimenez  * Term -> Leaf Operator Leaf
884a34d5fb1SAntonio Huete Jimenez  * Term -> Leaf
88501e196c8SJohn Marino  */
886a34d5fb1SAntonio Huete Jimenez static CondResult
CondParser_Term(CondParser * par,bool doEval)887*6eef5f0cSAntonio Huete Jimenez CondParser_Term(CondParser *par, bool doEval)
88801e196c8SJohn Marino {
889a34d5fb1SAntonio Huete Jimenez 	CondResult res;
89001e196c8SJohn Marino 	Token t;
89101e196c8SJohn Marino 
892a34d5fb1SAntonio Huete Jimenez 	t = CondParser_Token(par, doEval);
893a34d5fb1SAntonio Huete Jimenez 	if (t == TOK_TRUE)
894a34d5fb1SAntonio Huete Jimenez 		return CR_TRUE;
895a34d5fb1SAntonio Huete Jimenez 	if (t == TOK_FALSE)
896a34d5fb1SAntonio Huete Jimenez 		return CR_FALSE;
89701e196c8SJohn Marino 
898a34d5fb1SAntonio Huete Jimenez 	if (t == TOK_LPAREN) {
899a34d5fb1SAntonio Huete Jimenez 		res = CondParser_Or(par, doEval);
900a34d5fb1SAntonio Huete Jimenez 		if (res == CR_ERROR)
901a34d5fb1SAntonio Huete Jimenez 			return CR_ERROR;
902a34d5fb1SAntonio Huete Jimenez 		if (CondParser_Token(par, doEval) != TOK_RPAREN)
903a34d5fb1SAntonio Huete Jimenez 			return CR_ERROR;
904a34d5fb1SAntonio Huete Jimenez 		return res;
905a34d5fb1SAntonio Huete Jimenez 	}
906a34d5fb1SAntonio Huete Jimenez 
907a34d5fb1SAntonio Huete Jimenez 	if (t == TOK_NOT) {
908a34d5fb1SAntonio Huete Jimenez 		res = CondParser_Term(par, doEval);
909a34d5fb1SAntonio Huete Jimenez 		if (res == CR_TRUE)
910a34d5fb1SAntonio Huete Jimenez 			res = CR_FALSE;
911a34d5fb1SAntonio Huete Jimenez 		else if (res == CR_FALSE)
912a34d5fb1SAntonio Huete Jimenez 			res = CR_TRUE;
913a34d5fb1SAntonio Huete Jimenez 		return res;
914a34d5fb1SAntonio Huete Jimenez 	}
915a34d5fb1SAntonio Huete Jimenez 
916a34d5fb1SAntonio Huete Jimenez 	return CR_ERROR;
917a34d5fb1SAntonio Huete Jimenez }
918a34d5fb1SAntonio Huete Jimenez 
91901e196c8SJohn Marino /*
920*6eef5f0cSAntonio Huete Jimenez  * And -> Term ('&&' Term)*
92101e196c8SJohn Marino  */
922a34d5fb1SAntonio Huete Jimenez static CondResult
CondParser_And(CondParser * par,bool doEval)923*6eef5f0cSAntonio Huete Jimenez CondParser_And(CondParser *par, bool doEval)
92401e196c8SJohn Marino {
925*6eef5f0cSAntonio Huete Jimenez 	CondResult res, rhs;
92601e196c8SJohn Marino 
927*6eef5f0cSAntonio Huete Jimenez 	res = CR_TRUE;
928*6eef5f0cSAntonio Huete Jimenez 	do {
929*6eef5f0cSAntonio Huete Jimenez 		if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR)
930a34d5fb1SAntonio Huete Jimenez 			return CR_ERROR;
931*6eef5f0cSAntonio Huete Jimenez 		if (rhs == CR_FALSE) {
932*6eef5f0cSAntonio Huete Jimenez 			res = CR_FALSE;
933*6eef5f0cSAntonio Huete Jimenez 			doEval = false;
934a34d5fb1SAntonio Huete Jimenez 		}
935*6eef5f0cSAntonio Huete Jimenez 	} while (CondParser_Skip(par, TOK_AND));
936a34d5fb1SAntonio Huete Jimenez 
937a34d5fb1SAntonio Huete Jimenez 	return res;
938a34d5fb1SAntonio Huete Jimenez }
939a34d5fb1SAntonio Huete Jimenez 
94001e196c8SJohn Marino /*
941*6eef5f0cSAntonio Huete Jimenez  * Or -> And ('||' And)*
94201e196c8SJohn Marino  */
943a34d5fb1SAntonio Huete Jimenez static CondResult
CondParser_Or(CondParser * par,bool doEval)944*6eef5f0cSAntonio Huete Jimenez CondParser_Or(CondParser *par, bool doEval)
94501e196c8SJohn Marino {
946*6eef5f0cSAntonio Huete Jimenez 	CondResult res, rhs;
94701e196c8SJohn Marino 
948*6eef5f0cSAntonio Huete Jimenez 	res = CR_FALSE;
949*6eef5f0cSAntonio Huete Jimenez 	do {
950*6eef5f0cSAntonio Huete Jimenez 		if ((rhs = CondParser_And(par, doEval)) == CR_ERROR)
951a34d5fb1SAntonio Huete Jimenez 			return CR_ERROR;
952*6eef5f0cSAntonio Huete Jimenez 		if (rhs == CR_TRUE) {
953*6eef5f0cSAntonio Huete Jimenez 			res = CR_TRUE;
954*6eef5f0cSAntonio Huete Jimenez 			doEval = false;
955*6eef5f0cSAntonio Huete Jimenez 		}
956*6eef5f0cSAntonio Huete Jimenez 	} while (CondParser_Skip(par, TOK_OR));
95701e196c8SJohn Marino 
958a34d5fb1SAntonio Huete Jimenez 	return res;
959a34d5fb1SAntonio Huete Jimenez }
960a34d5fb1SAntonio Huete Jimenez 
961*6eef5f0cSAntonio Huete Jimenez static CondResult
CondParser_Eval(CondParser * par)962*6eef5f0cSAntonio Huete Jimenez CondParser_Eval(CondParser *par)
963a34d5fb1SAntonio Huete Jimenez {
964a34d5fb1SAntonio Huete Jimenez 	CondResult res;
965a34d5fb1SAntonio Huete Jimenez 
966a34d5fb1SAntonio Huete Jimenez 	DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
967a34d5fb1SAntonio Huete Jimenez 
968*6eef5f0cSAntonio Huete Jimenez 	res = CondParser_Or(par, true);
969*6eef5f0cSAntonio Huete Jimenez 	if (res != CR_ERROR && CondParser_Token(par, false) != TOK_EOF)
970*6eef5f0cSAntonio Huete Jimenez 		return CR_ERROR;
971a34d5fb1SAntonio Huete Jimenez 
972*6eef5f0cSAntonio Huete Jimenez 	return res;
973a34d5fb1SAntonio Huete Jimenez }
974a34d5fb1SAntonio Huete Jimenez 
97501e196c8SJohn Marino /*
976a34d5fb1SAntonio Huete Jimenez  * Evaluate the condition, including any side effects from the variable
977a34d5fb1SAntonio Huete Jimenez  * expressions in the condition. The condition consists of &&, ||, !,
978a34d5fb1SAntonio Huete Jimenez  * function(arg), comparisons and parenthetical groupings thereof.
97901e196c8SJohn Marino  */
980*6eef5f0cSAntonio Huete Jimenez static CondResult
CondEvalExpression(const char * cond,bool plain,bool (* evalBare)(const char *),bool negate,bool eprint,bool leftUnquotedOK)981*6eef5f0cSAntonio Huete Jimenez CondEvalExpression(const char *cond, bool plain,
982*6eef5f0cSAntonio Huete Jimenez 		   bool (*evalBare)(const char *), bool negate,
983*6eef5f0cSAntonio Huete Jimenez 		   bool eprint, bool leftUnquotedOK)
98401e196c8SJohn Marino {
985a34d5fb1SAntonio Huete Jimenez 	CondParser par;
986*6eef5f0cSAntonio Huete Jimenez 	CondResult rval;
987f445c897SJohn Marino 
988a34d5fb1SAntonio Huete Jimenez 	cpp_skip_hspace(&cond);
98901e196c8SJohn Marino 
990a34d5fb1SAntonio Huete Jimenez 	par.plain = plain;
991a34d5fb1SAntonio Huete Jimenez 	par.evalBare = evalBare;
992a34d5fb1SAntonio Huete Jimenez 	par.negateEvalBare = negate;
993*6eef5f0cSAntonio Huete Jimenez 	par.leftUnquotedOK = leftUnquotedOK;
994a34d5fb1SAntonio Huete Jimenez 	par.p = cond;
995a34d5fb1SAntonio Huete Jimenez 	par.curr = TOK_NONE;
996*6eef5f0cSAntonio Huete Jimenez 	par.printedError = false;
99701e196c8SJohn Marino 
998*6eef5f0cSAntonio Huete Jimenez 	rval = CondParser_Eval(&par);
99901e196c8SJohn Marino 
1000*6eef5f0cSAntonio Huete Jimenez 	if (rval == CR_ERROR && eprint && !par.printedError)
1001a34d5fb1SAntonio Huete Jimenez 		Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
100201e196c8SJohn Marino 
100301e196c8SJohn Marino 	return rval;
100401e196c8SJohn Marino }
100501e196c8SJohn Marino 
1006a34d5fb1SAntonio Huete Jimenez /*
1007a34d5fb1SAntonio Huete Jimenez  * Evaluate a condition in a :? modifier, such as
1008a34d5fb1SAntonio Huete Jimenez  * ${"${VAR}" == value:?yes:no}.
1009a34d5fb1SAntonio Huete Jimenez  */
1010*6eef5f0cSAntonio Huete Jimenez CondResult
Cond_EvalCondition(const char * cond)1011*6eef5f0cSAntonio Huete Jimenez Cond_EvalCondition(const char *cond)
101201e196c8SJohn Marino {
1013*6eef5f0cSAntonio Huete Jimenez 	return CondEvalExpression(cond, true,
1014*6eef5f0cSAntonio Huete Jimenez 	    FuncDefined, false, false, true);
101501e196c8SJohn Marino }
101601e196c8SJohn Marino 
1017*6eef5f0cSAntonio Huete Jimenez static bool
IsEndif(const char * p)1018a34d5fb1SAntonio Huete Jimenez IsEndif(const char *p)
1019a34d5fb1SAntonio Huete Jimenez {
1020a34d5fb1SAntonio Huete Jimenez 	return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' &&
1021a34d5fb1SAntonio Huete Jimenez 	       p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]);
102201e196c8SJohn Marino }
102301e196c8SJohn Marino 
1024*6eef5f0cSAntonio Huete Jimenez static bool
DetermineKindOfConditional(const char ** pp,bool * out_plain,bool (** out_evalBare)(const char *),bool * out_negate)1025*6eef5f0cSAntonio Huete Jimenez DetermineKindOfConditional(const char **pp, bool *out_plain,
1026*6eef5f0cSAntonio Huete Jimenez 			   bool (**out_evalBare)(const char *),
1027*6eef5f0cSAntonio Huete Jimenez 			   bool *out_negate)
1028a34d5fb1SAntonio Huete Jimenez {
1029*6eef5f0cSAntonio Huete Jimenez 	const char *p = *pp + 2;
1030a34d5fb1SAntonio Huete Jimenez 
1031*6eef5f0cSAntonio Huete Jimenez 	*out_plain = false;
1032a34d5fb1SAntonio Huete Jimenez 	*out_evalBare = FuncDefined;
1033*6eef5f0cSAntonio Huete Jimenez 	*out_negate = skip_string(&p, "n");
1034*6eef5f0cSAntonio Huete Jimenez 
1035*6eef5f0cSAntonio Huete Jimenez 	if (skip_string(&p, "def")) {		/* .ifdef and .ifndef */
1036*6eef5f0cSAntonio Huete Jimenez 	} else if (skip_string(&p, "make"))	/* .ifmake and .ifnmake */
1037a34d5fb1SAntonio Huete Jimenez 		*out_evalBare = FuncMake;
1038*6eef5f0cSAntonio Huete Jimenez 	else if (!*out_negate)			/* plain .if */
1039*6eef5f0cSAntonio Huete Jimenez 		*out_plain = true;
1040*6eef5f0cSAntonio Huete Jimenez 	else
1041*6eef5f0cSAntonio Huete Jimenez 		goto unknown_directive;
1042*6eef5f0cSAntonio Huete Jimenez 	if (ch_isalpha(*p))
1043*6eef5f0cSAntonio Huete Jimenez 		goto unknown_directive;
1044*6eef5f0cSAntonio Huete Jimenez 
1045*6eef5f0cSAntonio Huete Jimenez 	*pp = p;
1046*6eef5f0cSAntonio Huete Jimenez 	return true;
1047*6eef5f0cSAntonio Huete Jimenez 
1048*6eef5f0cSAntonio Huete Jimenez unknown_directive:
1049a34d5fb1SAntonio Huete Jimenez 	/*
1050*6eef5f0cSAntonio Huete Jimenez 	 * TODO: Add error message about unknown directive, since there is no
1051*6eef5f0cSAntonio Huete Jimenez 	 * other known directive that starts with 'el' or 'if'.
105201e196c8SJohn Marino 	 *
1053a34d5fb1SAntonio Huete Jimenez 	 * Example: .elifx 123
1054a34d5fb1SAntonio Huete Jimenez 	 */
1055*6eef5f0cSAntonio Huete Jimenez 	return false;
1056a34d5fb1SAntonio Huete Jimenez }
1057a34d5fb1SAntonio Huete Jimenez 
1058a34d5fb1SAntonio Huete Jimenez /*
1059a34d5fb1SAntonio Huete Jimenez  * Evaluate the conditional directive in the line, which is one of:
1060a34d5fb1SAntonio Huete Jimenez  *
1061a34d5fb1SAntonio Huete Jimenez  *	.if <cond>
1062a34d5fb1SAntonio Huete Jimenez  *	.ifmake <cond>
1063a34d5fb1SAntonio Huete Jimenez  *	.ifnmake <cond>
1064a34d5fb1SAntonio Huete Jimenez  *	.ifdef <cond>
1065a34d5fb1SAntonio Huete Jimenez  *	.ifndef <cond>
1066a34d5fb1SAntonio Huete Jimenez  *	.elif <cond>
1067a34d5fb1SAntonio Huete Jimenez  *	.elifmake <cond>
1068a34d5fb1SAntonio Huete Jimenez  *	.elifnmake <cond>
1069a34d5fb1SAntonio Huete Jimenez  *	.elifdef <cond>
1070a34d5fb1SAntonio Huete Jimenez  *	.elifndef <cond>
1071a34d5fb1SAntonio Huete Jimenez  *	.else
1072a34d5fb1SAntonio Huete Jimenez  *	.endif
1073a34d5fb1SAntonio Huete Jimenez  *
1074a34d5fb1SAntonio Huete Jimenez  * In these directives, <cond> consists of &&, ||, !, function(arg),
1075a34d5fb1SAntonio Huete Jimenez  * comparisons, expressions, bare words, numbers and strings, and
1076a34d5fb1SAntonio Huete Jimenez  * parenthetical groupings thereof.
107701e196c8SJohn Marino  *
107801e196c8SJohn Marino  * Results:
1079*6eef5f0cSAntonio Huete Jimenez  *	CR_TRUE		to continue parsing the lines that follow the
1080*6eef5f0cSAntonio Huete Jimenez  *			conditional (when <cond> evaluates to true)
1081*6eef5f0cSAntonio Huete Jimenez  *	CR_FALSE	to skip the lines after the conditional
1082*6eef5f0cSAntonio Huete Jimenez  *			(when <cond> evaluates to false, or when a previous
1083a34d5fb1SAntonio Huete Jimenez  *			branch has already been taken)
1084*6eef5f0cSAntonio Huete Jimenez  *	CR_ERROR	if the conditional was not valid, either because of
1085a34d5fb1SAntonio Huete Jimenez  *			a syntax error or because some variable was undefined
1086a34d5fb1SAntonio Huete Jimenez  *			or because the condition could not be evaluated
108701e196c8SJohn Marino  */
1088*6eef5f0cSAntonio Huete Jimenez CondResult
Cond_EvalLine(const char * line)1089a34d5fb1SAntonio Huete Jimenez Cond_EvalLine(const char *line)
109001e196c8SJohn Marino {
1091a34d5fb1SAntonio Huete Jimenez 	typedef enum IfState {
109201e196c8SJohn Marino 
1093*6eef5f0cSAntonio Huete Jimenez 		/* None of the previous <cond> evaluated to true. */
1094a34d5fb1SAntonio Huete Jimenez 		IFS_INITIAL	= 0,
1095a34d5fb1SAntonio Huete Jimenez 
1096*6eef5f0cSAntonio Huete Jimenez 		/*
1097*6eef5f0cSAntonio Huete Jimenez 		 * The previous <cond> evaluated to true. The lines following
1098*6eef5f0cSAntonio Huete Jimenez 		 * this condition are interpreted.
1099*6eef5f0cSAntonio Huete Jimenez 		 */
1100a34d5fb1SAntonio Huete Jimenez 		IFS_ACTIVE	= 1 << 0,
1101a34d5fb1SAntonio Huete Jimenez 
1102a34d5fb1SAntonio Huete Jimenez 		/* The previous directive was an '.else'. */
1103a34d5fb1SAntonio Huete Jimenez 		IFS_SEEN_ELSE	= 1 << 1,
1104a34d5fb1SAntonio Huete Jimenez 
1105*6eef5f0cSAntonio Huete Jimenez 		/* One of the previous <cond> evaluated to true. */
1106a34d5fb1SAntonio Huete Jimenez 		IFS_WAS_ACTIVE	= 1 << 2
1107a34d5fb1SAntonio Huete Jimenez 
1108a34d5fb1SAntonio Huete Jimenez 	} IfState;
1109a34d5fb1SAntonio Huete Jimenez 
1110a34d5fb1SAntonio Huete Jimenez 	static enum IfState *cond_states = NULL;
1111a34d5fb1SAntonio Huete Jimenez 	static unsigned int cond_states_cap = 128;
1112a34d5fb1SAntonio Huete Jimenez 
1113*6eef5f0cSAntonio Huete Jimenez 	bool plain;
1114*6eef5f0cSAntonio Huete Jimenez 	bool (*evalBare)(const char *);
1115*6eef5f0cSAntonio Huete Jimenez 	bool negate;
1116*6eef5f0cSAntonio Huete Jimenez 	bool isElif;
1117*6eef5f0cSAntonio Huete Jimenez 	CondResult res;
1118a34d5fb1SAntonio Huete Jimenez 	IfState state;
1119a34d5fb1SAntonio Huete Jimenez 	const char *p = line;
112001e196c8SJohn Marino 
1121a34d5fb1SAntonio Huete Jimenez 	if (cond_states == NULL) {
1122a34d5fb1SAntonio Huete Jimenez 		cond_states = bmake_malloc(
1123a34d5fb1SAntonio Huete Jimenez 		    cond_states_cap * sizeof *cond_states);
1124a34d5fb1SAntonio Huete Jimenez 		cond_states[0] = IFS_ACTIVE;
11255f1e34d9SAlexandre Perrin 	}
112601e196c8SJohn Marino 
1127a34d5fb1SAntonio Huete Jimenez 	p++;			/* skip the leading '.' */
1128a34d5fb1SAntonio Huete Jimenez 	cpp_skip_hspace(&p);
1129a34d5fb1SAntonio Huete Jimenez 
1130a34d5fb1SAntonio Huete Jimenez 	if (IsEndif(p)) {	/* It is an '.endif'. */
1131a34d5fb1SAntonio Huete Jimenez 		if (p[5] != '\0') {
1132a34d5fb1SAntonio Huete Jimenez 			Parse_Error(PARSE_FATAL,
1133*6eef5f0cSAntonio Huete Jimenez 			    "The .endif directive does not take arguments");
1134a34d5fb1SAntonio Huete Jimenez 		}
1135a34d5fb1SAntonio Huete Jimenez 
1136*6eef5f0cSAntonio Huete Jimenez 		if (cond_depth == CurFile_CondMinDepth()) {
1137a34d5fb1SAntonio Huete Jimenez 			Parse_Error(PARSE_FATAL, "if-less endif");
1138*6eef5f0cSAntonio Huete Jimenez 			return CR_TRUE;
113901e196c8SJohn Marino 		}
1140a34d5fb1SAntonio Huete Jimenez 
114101e196c8SJohn Marino 		/* Return state for previous conditional */
114201e196c8SJohn Marino 		cond_depth--;
1143a34d5fb1SAntonio Huete Jimenez 		return cond_states[cond_depth] & IFS_ACTIVE
1144*6eef5f0cSAntonio Huete Jimenez 		    ? CR_TRUE : CR_FALSE;
1145a34d5fb1SAntonio Huete Jimenez 	}
1146a34d5fb1SAntonio Huete Jimenez 
1147a34d5fb1SAntonio Huete Jimenez 	/* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
1148a34d5fb1SAntonio Huete Jimenez 	if (p[0] == 'e') {
1149a34d5fb1SAntonio Huete Jimenez 		if (p[1] != 'l') {
1150a34d5fb1SAntonio Huete Jimenez 			/*
1151a34d5fb1SAntonio Huete Jimenez 			 * Unknown directive.  It might still be a
1152*6eef5f0cSAntonio Huete Jimenez 			 * transformation rule like '.err.txt',
1153a34d5fb1SAntonio Huete Jimenez 			 * therefore no error message here.
1154a34d5fb1SAntonio Huete Jimenez 			 */
1155*6eef5f0cSAntonio Huete Jimenez 			return CR_ERROR;
115601e196c8SJohn Marino 		}
115701e196c8SJohn Marino 
115801e196c8SJohn Marino 		/* Quite likely this is 'else' or 'elif' */
1159a34d5fb1SAntonio Huete Jimenez 		p += 2;
1160*6eef5f0cSAntonio Huete Jimenez 		if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) {
1161a34d5fb1SAntonio Huete Jimenez 			if (p[2] != '\0')
1162a34d5fb1SAntonio Huete Jimenez 				Parse_Error(PARSE_FATAL,
1163a34d5fb1SAntonio Huete Jimenez 				    "The .else directive "
1164*6eef5f0cSAntonio Huete Jimenez 				    "does not take arguments");
1165a34d5fb1SAntonio Huete Jimenez 
1166*6eef5f0cSAntonio Huete Jimenez 			if (cond_depth == CurFile_CondMinDepth()) {
1167a34d5fb1SAntonio Huete Jimenez 				Parse_Error(PARSE_FATAL, "if-less else");
1168*6eef5f0cSAntonio Huete Jimenez 				return CR_TRUE;
116901e196c8SJohn Marino 			}
117001e196c8SJohn Marino 
1171a34d5fb1SAntonio Huete Jimenez 			state = cond_states[cond_depth];
1172a34d5fb1SAntonio Huete Jimenez 			if (state == IFS_INITIAL) {
1173a34d5fb1SAntonio Huete Jimenez 				state = IFS_ACTIVE | IFS_SEEN_ELSE;
1174a34d5fb1SAntonio Huete Jimenez 			} else {
1175a34d5fb1SAntonio Huete Jimenez 				if (state & IFS_SEEN_ELSE)
1176a34d5fb1SAntonio Huete Jimenez 					Parse_Error(PARSE_WARNING,
1177a34d5fb1SAntonio Huete Jimenez 					    "extra else");
1178a34d5fb1SAntonio Huete Jimenez 				state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
117901e196c8SJohn Marino 			}
1180a34d5fb1SAntonio Huete Jimenez 			cond_states[cond_depth] = state;
1181a34d5fb1SAntonio Huete Jimenez 
1182*6eef5f0cSAntonio Huete Jimenez 			return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE;
118301e196c8SJohn Marino 		}
118401e196c8SJohn Marino 		/* Assume for now it is an elif */
1185*6eef5f0cSAntonio Huete Jimenez 		isElif = true;
118601e196c8SJohn Marino 	} else
1187*6eef5f0cSAntonio Huete Jimenez 		isElif = false;
118801e196c8SJohn Marino 
1189a34d5fb1SAntonio Huete Jimenez 	if (p[0] != 'i' || p[1] != 'f') {
119001e196c8SJohn Marino 		/*
1191a34d5fb1SAntonio Huete Jimenez 		 * Unknown directive.  It might still be a transformation rule
1192a34d5fb1SAntonio Huete Jimenez 		 * like '.elisp.scm', therefore no error message here.
119301e196c8SJohn Marino 		 */
1194*6eef5f0cSAntonio Huete Jimenez 		return CR_ERROR;	/* Not an ifxxx or elifxxx line */
119501e196c8SJohn Marino 	}
119601e196c8SJohn Marino 
1197a34d5fb1SAntonio Huete Jimenez 	if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
1198*6eef5f0cSAntonio Huete Jimenez 		return CR_ERROR;
119901e196c8SJohn Marino 
120001e196c8SJohn Marino 	if (isElif) {
1201*6eef5f0cSAntonio Huete Jimenez 		if (cond_depth == CurFile_CondMinDepth()) {
1202a34d5fb1SAntonio Huete Jimenez 			Parse_Error(PARSE_FATAL, "if-less elif");
1203*6eef5f0cSAntonio Huete Jimenez 			return CR_TRUE;
120401e196c8SJohn Marino 		}
1205a34d5fb1SAntonio Huete Jimenez 		state = cond_states[cond_depth];
1206a34d5fb1SAntonio Huete Jimenez 		if (state & IFS_SEEN_ELSE) {
120701e196c8SJohn Marino 			Parse_Error(PARSE_WARNING, "extra elif");
1208a34d5fb1SAntonio Huete Jimenez 			cond_states[cond_depth] =
1209a34d5fb1SAntonio Huete Jimenez 			    IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
1210*6eef5f0cSAntonio Huete Jimenez 			return CR_FALSE;
121101e196c8SJohn Marino 		}
1212a34d5fb1SAntonio Huete Jimenez 		if (state != IFS_INITIAL) {
1213a34d5fb1SAntonio Huete Jimenez 			cond_states[cond_depth] = IFS_WAS_ACTIVE;
1214*6eef5f0cSAntonio Huete Jimenez 			return CR_FALSE;
121501e196c8SJohn Marino 		}
121601e196c8SJohn Marino 	} else {
121701e196c8SJohn Marino 		/* Normal .if */
1218a34d5fb1SAntonio Huete Jimenez 		if (cond_depth + 1 >= cond_states_cap) {
12195f1e34d9SAlexandre Perrin 			/*
12205f1e34d9SAlexandre Perrin 			 * This is rare, but not impossible.
12215f1e34d9SAlexandre Perrin 			 * In meta mode, dirdeps.mk (only runs at level 0)
12225f1e34d9SAlexandre Perrin 			 * can need more than the default.
12235f1e34d9SAlexandre Perrin 			 */
1224a34d5fb1SAntonio Huete Jimenez 			cond_states_cap += 32;
1225a34d5fb1SAntonio Huete Jimenez 			cond_states = bmake_realloc(cond_states,
1226*6eef5f0cSAntonio Huete Jimenez 			    cond_states_cap * sizeof *cond_states);
122701e196c8SJohn Marino 		}
1228a34d5fb1SAntonio Huete Jimenez 		state = cond_states[cond_depth];
122901e196c8SJohn Marino 		cond_depth++;
1230a34d5fb1SAntonio Huete Jimenez 		if (!(state & IFS_ACTIVE)) {
1231a34d5fb1SAntonio Huete Jimenez 			/*
1232a34d5fb1SAntonio Huete Jimenez 			 * If we aren't parsing the data,
1233a34d5fb1SAntonio Huete Jimenez 			 * treat as always false.
1234a34d5fb1SAntonio Huete Jimenez 			 */
1235a34d5fb1SAntonio Huete Jimenez 			cond_states[cond_depth] = IFS_WAS_ACTIVE;
1236*6eef5f0cSAntonio Huete Jimenez 			return CR_FALSE;
123701e196c8SJohn Marino 		}
123801e196c8SJohn Marino 	}
123901e196c8SJohn Marino 
1240a34d5fb1SAntonio Huete Jimenez 	/* And evaluate the conditional expression */
1241*6eef5f0cSAntonio Huete Jimenez 	res = CondEvalExpression(p, plain, evalBare, negate, true, false);
1242*6eef5f0cSAntonio Huete Jimenez 	if (res == CR_ERROR) {
1243*6eef5f0cSAntonio Huete Jimenez 		/* Syntax error, error message already output. */
1244*6eef5f0cSAntonio Huete Jimenez 		/* Skip everything to the matching '.endif'. */
1245*6eef5f0cSAntonio Huete Jimenez 		/* An extra '.else' is not detected in this case. */
1246a34d5fb1SAntonio Huete Jimenez 		cond_states[cond_depth] = IFS_WAS_ACTIVE;
1247*6eef5f0cSAntonio Huete Jimenez 		return CR_FALSE;
124801e196c8SJohn Marino 	}
124901e196c8SJohn Marino 
1250*6eef5f0cSAntonio Huete Jimenez 	cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL;
1251*6eef5f0cSAntonio Huete Jimenez 	return res;
125201e196c8SJohn Marino }
125301e196c8SJohn Marino 
125401e196c8SJohn Marino void
Cond_EndFile(void)1255*6eef5f0cSAntonio Huete Jimenez Cond_EndFile(void)
125601e196c8SJohn Marino {
1257*6eef5f0cSAntonio Huete Jimenez 	unsigned int open_conds = cond_depth - CurFile_CondMinDepth();
125801e196c8SJohn Marino 
1259*6eef5f0cSAntonio Huete Jimenez 	if (open_conds != 0) {
1260a34d5fb1SAntonio Huete Jimenez 		Parse_Error(PARSE_FATAL, "%u open conditional%s",
1261a34d5fb1SAntonio Huete Jimenez 		    open_conds, open_conds == 1 ? "" : "s");
1262*6eef5f0cSAntonio Huete Jimenez 		cond_depth = CurFile_CondMinDepth();
126301e196c8SJohn Marino 	}
126401e196c8SJohn Marino }
1265