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