1*6a7405f5SSimon J. Gerraty /* $NetBSD: cond.c,v 1.371 2025/01/11 21:21:33 rillig Exp $ */ 23955d011SMarcel Moolenaar 33955d011SMarcel Moolenaar /* 43955d011SMarcel Moolenaar * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 53955d011SMarcel Moolenaar * All rights reserved. 63955d011SMarcel Moolenaar * 73955d011SMarcel Moolenaar * This code is derived from software contributed to Berkeley by 83955d011SMarcel Moolenaar * Adam de Boor. 93955d011SMarcel Moolenaar * 103955d011SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 113955d011SMarcel Moolenaar * modification, are permitted provided that the following conditions 123955d011SMarcel Moolenaar * are met: 133955d011SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 143955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 153955d011SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 163955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 173955d011SMarcel Moolenaar * documentation and/or other materials provided with the distribution. 183955d011SMarcel Moolenaar * 3. Neither the name of the University nor the names of its contributors 193955d011SMarcel Moolenaar * may be used to endorse or promote products derived from this software 203955d011SMarcel Moolenaar * without specific prior written permission. 213955d011SMarcel Moolenaar * 223955d011SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 233955d011SMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 243955d011SMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 253955d011SMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 263955d011SMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 273955d011SMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 283955d011SMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 293955d011SMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 303955d011SMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 313955d011SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 323955d011SMarcel Moolenaar * SUCH DAMAGE. 333955d011SMarcel Moolenaar */ 343955d011SMarcel Moolenaar 353955d011SMarcel Moolenaar /* 363955d011SMarcel Moolenaar * Copyright (c) 1988, 1989 by Adam de Boor 373955d011SMarcel Moolenaar * Copyright (c) 1989 by Berkeley Softworks 383955d011SMarcel Moolenaar * All rights reserved. 393955d011SMarcel Moolenaar * 403955d011SMarcel Moolenaar * This code is derived from software contributed to Berkeley by 413955d011SMarcel Moolenaar * Adam de Boor. 423955d011SMarcel Moolenaar * 433955d011SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 443955d011SMarcel Moolenaar * modification, are permitted provided that the following conditions 453955d011SMarcel Moolenaar * are met: 463955d011SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 473955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 483955d011SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 493955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 503955d011SMarcel Moolenaar * documentation and/or other materials provided with the distribution. 513955d011SMarcel Moolenaar * 3. All advertising materials mentioning features or use of this software 523955d011SMarcel Moolenaar * must display the following acknowledgement: 533955d011SMarcel Moolenaar * This product includes software developed by the University of 543955d011SMarcel Moolenaar * California, Berkeley and its contributors. 553955d011SMarcel Moolenaar * 4. Neither the name of the University nor the names of its contributors 563955d011SMarcel Moolenaar * may be used to endorse or promote products derived from this software 573955d011SMarcel Moolenaar * without specific prior written permission. 583955d011SMarcel Moolenaar * 593955d011SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 603955d011SMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 613955d011SMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 623955d011SMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 633955d011SMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 643955d011SMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 653955d011SMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 663955d011SMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 673955d011SMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 683955d011SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 693955d011SMarcel Moolenaar * SUCH DAMAGE. 703955d011SMarcel Moolenaar */ 713955d011SMarcel Moolenaar 7206b9b3e0SSimon J. Gerraty /* 7306b9b3e0SSimon J. Gerraty * Handling of conditionals in a makefile. 743955d011SMarcel Moolenaar * 753955d011SMarcel Moolenaar * Interface: 76e2eeea75SSimon J. Gerraty * Cond_EvalLine Evaluate the conditional directive, such as 77e2eeea75SSimon J. Gerraty * '.if <cond>', '.elifnmake <cond>', '.else', '.endif'. 783955d011SMarcel Moolenaar * 79956e45f6SSimon J. Gerraty * Cond_EvalCondition 80956e45f6SSimon J. Gerraty * Evaluate the conditional, which is either the argument 81956e45f6SSimon J. Gerraty * of one of the .if directives or the condition in a 82956e45f6SSimon J. Gerraty * ':?then:else' variable modifier. 83956e45f6SSimon J. Gerraty * 84d5e0a182SSimon J. Gerraty * Cond_EndFile At the end of reading a makefile, ensure that the 854fde40d9SSimon J. Gerraty * conditional directives are well-balanced. 863955d011SMarcel Moolenaar */ 873955d011SMarcel Moolenaar 882c3632d1SSimon J. Gerraty #include <errno.h> 893955d011SMarcel Moolenaar 903955d011SMarcel Moolenaar #include "make.h" 913955d011SMarcel Moolenaar #include "dir.h" 923955d011SMarcel Moolenaar 93956e45f6SSimon J. Gerraty /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */ 94*6a7405f5SSimon J. Gerraty MAKE_RCSID("$NetBSD: cond.c,v 1.371 2025/01/11 21:21:33 rillig Exp $"); 95956e45f6SSimon J. Gerraty 963955d011SMarcel Moolenaar /* 979f45a3c8SSimon J. Gerraty * Conditional expressions conform to this grammar: 9812904384SSimon J. Gerraty * Or -> And ('||' And)* 9912904384SSimon J. Gerraty * And -> Term ('&&' Term)* 100dba7b0efSSimon J. Gerraty * Term -> Function '(' Argument ')' 101dba7b0efSSimon J. Gerraty * Term -> Leaf Operator Leaf 102dba7b0efSSimon J. Gerraty * Term -> Leaf 103dba7b0efSSimon J. Gerraty * Term -> '(' Or ')' 104dba7b0efSSimon J. Gerraty * Term -> '!' Term 105dba7b0efSSimon J. Gerraty * Leaf -> "string" 106dba7b0efSSimon J. Gerraty * Leaf -> Number 107dba7b0efSSimon J. Gerraty * Leaf -> VariableExpression 1089f45a3c8SSimon J. Gerraty * Leaf -> BareWord 109dba7b0efSSimon J. Gerraty * Operator -> '==' | '!=' | '>' | '<' | '>=' | '<=' 1103955d011SMarcel Moolenaar * 1119f45a3c8SSimon J. Gerraty * BareWord is an unquoted string literal, its evaluation depends on the kind 1129f45a3c8SSimon J. Gerraty * of '.if' directive. 1133955d011SMarcel Moolenaar * 1149f45a3c8SSimon J. Gerraty * The tokens are scanned by CondParser_Token, which returns: 115dba7b0efSSimon J. Gerraty * TOK_AND for '&&' 116dba7b0efSSimon J. Gerraty * TOK_OR for '||' 117956e45f6SSimon J. Gerraty * TOK_NOT for '!' 118956e45f6SSimon J. Gerraty * TOK_LPAREN for '(' 119956e45f6SSimon J. Gerraty * TOK_RPAREN for ')' 120dba7b0efSSimon J. Gerraty * 121956e45f6SSimon J. Gerraty * Other terminal symbols are evaluated using either the default function or 1229f45a3c8SSimon J. Gerraty * the function given in the terminal, they return either TOK_TRUE, TOK_FALSE 1239f45a3c8SSimon J. Gerraty * or TOK_ERROR. 1243955d011SMarcel Moolenaar */ 125956e45f6SSimon J. Gerraty typedef enum Token { 126dba7b0efSSimon J. Gerraty TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT, 1273955d011SMarcel Moolenaar TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR 1283955d011SMarcel Moolenaar } Token; 1293955d011SMarcel Moolenaar 130dba7b0efSSimon J. Gerraty typedef enum ComparisonOp { 131dba7b0efSSimon J. Gerraty LT, LE, GT, GE, EQ, NE 132dba7b0efSSimon J. Gerraty } ComparisonOp; 133dba7b0efSSimon J. Gerraty 134956e45f6SSimon J. Gerraty typedef struct CondParser { 135dba7b0efSSimon J. Gerraty 136dba7b0efSSimon J. Gerraty /* 137dba7b0efSSimon J. Gerraty * The plain '.if ${VAR}' evaluates to true if the value of the 138148ee845SSimon J. Gerraty * expression has length > 0 and is not numerically zero. The other 139148ee845SSimon J. Gerraty * '.if' variants delegate to evalBare instead, for example '.ifdef 140148ee845SSimon J. Gerraty * ${VAR}' is equivalent to '.if defined(${VAR})', checking whether 141148ee845SSimon J. Gerraty * the variable named by the expression '${VAR}' is defined. 142dba7b0efSSimon J. Gerraty */ 143b0c40a00SSimon J. Gerraty bool plain; 144dba7b0efSSimon J. Gerraty 145dba7b0efSSimon J. Gerraty /* The function to apply on unquoted bare words. */ 14612904384SSimon J. Gerraty bool (*evalBare)(const char *); 147b0c40a00SSimon J. Gerraty bool negateEvalBare; 148dba7b0efSSimon J. Gerraty 14912904384SSimon J. Gerraty /* 15012904384SSimon J. Gerraty * Whether the left-hand side of a comparison may be an unquoted 15112904384SSimon J. Gerraty * string. This is allowed for expressions of the form 15212904384SSimon J. Gerraty * ${condition:?:}, see ApplyModifier_IfElse. Such a condition is 15312904384SSimon J. Gerraty * expanded before it is evaluated, due to ease of implementation. 15412904384SSimon J. Gerraty * This means that at the point where the condition is evaluated, 15512904384SSimon J. Gerraty * make cannot know anymore whether the left-hand side had originally 156d5e0a182SSimon J. Gerraty * been an expression or a plain word. 15712904384SSimon J. Gerraty * 1581d3f2ddcSSimon J. Gerraty * In conditional directives like '.if', the left-hand side must 159d5e0a182SSimon J. Gerraty * either be an expression, a quoted string or a number. 16012904384SSimon J. Gerraty */ 16112904384SSimon J. Gerraty bool leftUnquotedOK; 16212904384SSimon J. Gerraty 163956e45f6SSimon J. Gerraty const char *p; /* The remaining condition to parse */ 164956e45f6SSimon J. Gerraty Token curr; /* Single push-back token used in parsing */ 1653955d011SMarcel Moolenaar 16606b9b3e0SSimon J. Gerraty /* 16706b9b3e0SSimon J. Gerraty * Whether an error message has already been printed for this 168d5e0a182SSimon J. Gerraty * condition. 16906b9b3e0SSimon J. Gerraty */ 170b0c40a00SSimon J. Gerraty bool printedError; 171956e45f6SSimon J. Gerraty } CondParser; 172956e45f6SSimon J. Gerraty 173148ee845SSimon J. Gerraty static CondResult CondParser_Or(CondParser *, bool); 1743955d011SMarcel Moolenaar 1754fde40d9SSimon J. Gerraty unsigned int cond_depth = 0; /* current .if nesting level */ 1763955d011SMarcel Moolenaar 17712904384SSimon J. Gerraty /* Names for ComparisonOp. */ 17812904384SSimon J. Gerraty static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" }; 17928a6bc81SSimon J. Gerraty 1809f45a3c8SSimon J. Gerraty MAKE_INLINE bool 1819f45a3c8SSimon J. Gerraty skip_string(const char **pp, const char *str) 1823955d011SMarcel Moolenaar { 1839f45a3c8SSimon J. Gerraty size_t len = strlen(str); 1849f45a3c8SSimon J. Gerraty bool ok = strncmp(*pp, str, len) == 0; 1859f45a3c8SSimon J. Gerraty if (ok) 1869f45a3c8SSimon J. Gerraty *pp += len; 1879f45a3c8SSimon J. Gerraty return ok; 1883955d011SMarcel Moolenaar } 1893955d011SMarcel Moolenaar 190e2eeea75SSimon J. Gerraty static Token 191b0c40a00SSimon J. Gerraty ToToken(bool cond) 192e2eeea75SSimon J. Gerraty { 193e2eeea75SSimon J. Gerraty return cond ? TOK_TRUE : TOK_FALSE; 194e2eeea75SSimon J. Gerraty } 195e2eeea75SSimon J. Gerraty 196956e45f6SSimon J. Gerraty static void 197956e45f6SSimon J. Gerraty CondParser_SkipWhitespace(CondParser *par) 1983955d011SMarcel Moolenaar { 199956e45f6SSimon J. Gerraty cpp_skip_whitespace(&par->p); 200956e45f6SSimon J. Gerraty } 201956e45f6SSimon J. Gerraty 20206b9b3e0SSimon J. Gerraty /* 20312904384SSimon J. Gerraty * Parse a single word, taking into account balanced parentheses as well as 20412904384SSimon J. Gerraty * embedded expressions. Used for the argument of a built-in function as 20512904384SSimon J. Gerraty * well as for bare words, which are then passed to the default function. 20606b9b3e0SSimon J. Gerraty */ 2079f45a3c8SSimon J. Gerraty static char * 2089f45a3c8SSimon J. Gerraty ParseWord(const char **pp, bool doEval) 20906b9b3e0SSimon J. Gerraty { 210956e45f6SSimon J. Gerraty const char *p = *pp; 2111d3f2ddcSSimon J. Gerraty Buffer word; 212d5e0a182SSimon J. Gerraty int depth; 2133955d011SMarcel Moolenaar 214d5e0a182SSimon J. Gerraty Buf_Init(&word); 2153955d011SMarcel Moolenaar 216d5e0a182SSimon J. Gerraty depth = 0; 2173955d011SMarcel Moolenaar for (;;) { 218956e45f6SSimon J. Gerraty char ch = *p; 219e2eeea75SSimon J. Gerraty if (ch == '\0' || ch == ' ' || ch == '\t') 2203955d011SMarcel Moolenaar break; 221d5e0a182SSimon J. Gerraty if ((ch == '&' || ch == '|') && depth == 0) 2223955d011SMarcel Moolenaar break; 2239f45a3c8SSimon J. Gerraty if (ch == '$') { 224*6a7405f5SSimon J. Gerraty VarEvalMode emode = doEval ? VARE_EVAL : VARE_PARSE; 2258c973ee2SSimon J. Gerraty FStr nestedVal = Var_Parse(&p, SCOPE_CMDLINE, emode); 226956e45f6SSimon J. Gerraty /* TODO: handle errors */ 2271d3f2ddcSSimon J. Gerraty Buf_AddStr(&word, nestedVal.str); 22806b9b3e0SSimon J. Gerraty FStr_Done(&nestedVal); 2293955d011SMarcel Moolenaar continue; 2303955d011SMarcel Moolenaar } 2313955d011SMarcel Moolenaar if (ch == '(') 232d5e0a182SSimon J. Gerraty depth++; 233d5e0a182SSimon J. Gerraty else if (ch == ')' && --depth < 0) 2343955d011SMarcel Moolenaar break; 2351d3f2ddcSSimon J. Gerraty Buf_AddByte(&word, ch); 236956e45f6SSimon J. Gerraty p++; 2373955d011SMarcel Moolenaar } 2383955d011SMarcel Moolenaar 2399f45a3c8SSimon J. Gerraty *pp = p; 2403955d011SMarcel Moolenaar 2411d3f2ddcSSimon J. Gerraty return Buf_DoneData(&word); 2429f45a3c8SSimon J. Gerraty } 2439f45a3c8SSimon J. Gerraty 2449f45a3c8SSimon J. Gerraty /* Parse the function argument, including the surrounding parentheses. */ 2459f45a3c8SSimon J. Gerraty static char * 2469f45a3c8SSimon J. Gerraty ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func) 2479f45a3c8SSimon J. Gerraty { 248*6a7405f5SSimon J. Gerraty const char *p = *pp, *argStart, *argEnd; 2499f45a3c8SSimon J. Gerraty char *res; 2509f45a3c8SSimon J. Gerraty 251d5e0a182SSimon J. Gerraty p++; /* skip the '(' */ 2529f45a3c8SSimon J. Gerraty cpp_skip_hspace(&p); 253*6a7405f5SSimon J. Gerraty argStart = p; 2549f45a3c8SSimon J. Gerraty res = ParseWord(&p, doEval); 255*6a7405f5SSimon J. Gerraty argEnd = p; 256e2eeea75SSimon J. Gerraty cpp_skip_hspace(&p); 2573955d011SMarcel Moolenaar 2589f45a3c8SSimon J. Gerraty if (*p++ != ')') { 2599f45a3c8SSimon J. Gerraty int len = 0; 2609f45a3c8SSimon J. Gerraty while (ch_isalpha(func[len])) 2619f45a3c8SSimon J. Gerraty len++; 2629f45a3c8SSimon J. Gerraty 263dba7b0efSSimon J. Gerraty Parse_Error(PARSE_FATAL, 264*6a7405f5SSimon J. Gerraty "Missing ')' after argument '%.*s' for '%.*s'", 265*6a7405f5SSimon J. Gerraty (int)(argEnd - argStart), argStart, len, func); 266b0c40a00SSimon J. Gerraty par->printedError = true; 2679f45a3c8SSimon J. Gerraty free(res); 2689f45a3c8SSimon J. Gerraty return NULL; 2693955d011SMarcel Moolenaar } 2703955d011SMarcel Moolenaar 271956e45f6SSimon J. Gerraty *pp = p; 2729f45a3c8SSimon J. Gerraty return res; 2733955d011SMarcel Moolenaar } 2742c3632d1SSimon J. Gerraty 2751d3f2ddcSSimon J. Gerraty /* See if the given variable is defined. */ 276b0c40a00SSimon J. Gerraty static bool 2779f45a3c8SSimon J. Gerraty FuncDefined(const char *var) 2783955d011SMarcel Moolenaar { 2799f45a3c8SSimon J. Gerraty return Var_Exists(SCOPE_CMDLINE, var); 2803955d011SMarcel Moolenaar } 2812c3632d1SSimon J. Gerraty 2821d3f2ddcSSimon J. Gerraty /* See if a target matching targetPattern is requested to be made. */ 283b0c40a00SSimon J. Gerraty static bool 2841d3f2ddcSSimon J. Gerraty FuncMake(const char *targetPattern) 2853955d011SMarcel Moolenaar { 286956e45f6SSimon J. Gerraty StringListNode *ln; 287148ee845SSimon J. Gerraty bool warned = false; 288956e45f6SSimon J. Gerraty 289148ee845SSimon J. Gerraty for (ln = opts.create.first; ln != NULL; ln = ln->next) { 290148ee845SSimon J. Gerraty StrMatchResult res = Str_Match(ln->datum, targetPattern); 291148ee845SSimon J. Gerraty if (res.error != NULL && !warned) { 292148ee845SSimon J. Gerraty warned = true; 293148ee845SSimon J. Gerraty Parse_Error(PARSE_WARNING, 294148ee845SSimon J. Gerraty "%s in pattern argument '%s' to function 'make'", 295148ee845SSimon J. Gerraty res.error, targetPattern); 296148ee845SSimon J. Gerraty } 297148ee845SSimon J. Gerraty if (res.matched) 298b0c40a00SSimon J. Gerraty return true; 299148ee845SSimon J. Gerraty } 300b0c40a00SSimon J. Gerraty return false; 3013955d011SMarcel Moolenaar } 3022c3632d1SSimon J. Gerraty 3032c3632d1SSimon J. Gerraty /* See if the given file exists. */ 304b0c40a00SSimon J. Gerraty static bool 3059f45a3c8SSimon J. Gerraty FuncExists(const char *file) 3063955d011SMarcel Moolenaar { 307b0c40a00SSimon J. Gerraty bool result; 3083955d011SMarcel Moolenaar char *path; 3093955d011SMarcel Moolenaar 3109f45a3c8SSimon J. Gerraty path = Dir_FindFile(file, &dirSearchPath); 311e2eeea75SSimon J. Gerraty DEBUG2(COND, "exists(%s) result is \"%s\"\n", 3129f45a3c8SSimon J. Gerraty file, path != NULL ? path : ""); 313e2eeea75SSimon J. Gerraty result = path != NULL; 3143955d011SMarcel Moolenaar free(path); 3153841c287SSimon J. Gerraty return result; 3163955d011SMarcel Moolenaar } 3172c3632d1SSimon J. Gerraty 3182c3632d1SSimon J. Gerraty /* See if the given node exists and is an actual target. */ 319b0c40a00SSimon J. Gerraty static bool 3209f45a3c8SSimon J. Gerraty FuncTarget(const char *node) 3213955d011SMarcel Moolenaar { 3229f45a3c8SSimon J. Gerraty GNode *gn = Targ_FindNode(node); 323956e45f6SSimon J. Gerraty return gn != NULL && GNode_IsTarget(gn); 3243955d011SMarcel Moolenaar } 3253955d011SMarcel Moolenaar 32606b9b3e0SSimon J. Gerraty /* 32706b9b3e0SSimon J. Gerraty * See if the given node exists and is an actual target with commands 32806b9b3e0SSimon J. Gerraty * associated with it. 32906b9b3e0SSimon J. Gerraty */ 330b0c40a00SSimon J. Gerraty static bool 3319f45a3c8SSimon J. Gerraty FuncCommands(const char *node) 3323955d011SMarcel Moolenaar { 3339f45a3c8SSimon J. Gerraty GNode *gn = Targ_FindNode(node); 3349f45a3c8SSimon J. Gerraty return gn != NULL && GNode_IsTarget(gn) && 3359f45a3c8SSimon J. Gerraty !Lst_IsEmpty(&gn->commands); 3363955d011SMarcel Moolenaar } 3372c3632d1SSimon J. Gerraty 338e2eeea75SSimon J. Gerraty /* 339148ee845SSimon J. Gerraty * Convert the string to a floating point number. Accepted formats are 3409f45a3c8SSimon J. Gerraty * base-10 integer, base-16 integer and finite floating point numbers. 3413955d011SMarcel Moolenaar */ 342b0c40a00SSimon J. Gerraty static bool 343e2eeea75SSimon J. Gerraty TryParseNumber(const char *str, double *out_value) 3443955d011SMarcel Moolenaar { 345e2eeea75SSimon J. Gerraty char *end; 346e2eeea75SSimon J. Gerraty unsigned long ul_val; 347e2eeea75SSimon J. Gerraty double dbl_val; 3483955d011SMarcel Moolenaar 349e2eeea75SSimon J. Gerraty if (str[0] == '\0') { /* XXX: why is an empty string a number? */ 350e2eeea75SSimon J. Gerraty *out_value = 0.0; 351b0c40a00SSimon J. Gerraty return true; 352ac3446e9SSimon J. Gerraty } 353e2eeea75SSimon J. Gerraty 35412904384SSimon J. Gerraty errno = 0; 355e2eeea75SSimon J. Gerraty ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10); 356e2eeea75SSimon J. Gerraty if (*end == '\0' && errno != ERANGE) { 357e2eeea75SSimon J. Gerraty *out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val; 358b0c40a00SSimon J. Gerraty return true; 3593955d011SMarcel Moolenaar } 3603955d011SMarcel Moolenaar 361e2eeea75SSimon J. Gerraty if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E') 362b0c40a00SSimon J. Gerraty return false; /* skip the expensive strtod call */ 363e2eeea75SSimon J. Gerraty dbl_val = strtod(str, &end); 364e2eeea75SSimon J. Gerraty if (*end != '\0') 365b0c40a00SSimon J. Gerraty return false; 366e2eeea75SSimon J. Gerraty 367e2eeea75SSimon J. Gerraty *out_value = dbl_val; 368b0c40a00SSimon J. Gerraty return true; 3693955d011SMarcel Moolenaar } 3703955d011SMarcel Moolenaar 371b0c40a00SSimon J. Gerraty static bool 372956e45f6SSimon J. Gerraty is_separator(char ch) 373956e45f6SSimon J. Gerraty { 374b0c40a00SSimon J. Gerraty return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' || 375b0c40a00SSimon J. Gerraty ch == '>' || ch == '<' || ch == ')' /* but not '(' */; 376956e45f6SSimon J. Gerraty } 377956e45f6SSimon J. Gerraty 378dba7b0efSSimon J. Gerraty /* 379d5e0a182SSimon J. Gerraty * In a quoted or unquoted string literal or a number, parse an 3808c973ee2SSimon J. Gerraty * expression and add its value to the buffer. 3818c973ee2SSimon J. Gerraty * 3828c973ee2SSimon J. Gerraty * Return whether to continue parsing the leaf. 383dba7b0efSSimon J. Gerraty * 384dba7b0efSSimon J. Gerraty * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX} 385dba7b0efSSimon J. Gerraty */ 386b0c40a00SSimon J. Gerraty static bool 387dba7b0efSSimon J. Gerraty CondParser_StringExpr(CondParser *par, const char *start, 38812904384SSimon J. Gerraty bool doEval, bool quoted, 38912904384SSimon J. Gerraty Buffer *buf, FStr *inout_str) 390dba7b0efSSimon J. Gerraty { 391b0c40a00SSimon J. Gerraty VarEvalMode emode; 3929f45a3c8SSimon J. Gerraty const char *p; 393c59c3bf3SSimon J. Gerraty bool atStart; /* true means an expression outside quotes */ 394dba7b0efSSimon J. Gerraty 3958d5c8e21SSimon J. Gerraty emode = doEval && quoted ? VARE_EVAL 396*6a7405f5SSimon J. Gerraty : doEval ? VARE_EVAL_DEFINED_LOUD 3978d5c8e21SSimon J. Gerraty : VARE_PARSE; 398dba7b0efSSimon J. Gerraty 3999f45a3c8SSimon J. Gerraty p = par->p; 4009f45a3c8SSimon J. Gerraty atStart = p == start; 4018c973ee2SSimon J. Gerraty *inout_str = Var_Parse(&p, SCOPE_CMDLINE, emode); 402dba7b0efSSimon J. Gerraty /* TODO: handle errors */ 403dba7b0efSSimon J. Gerraty if (inout_str->str == var_Error) { 404dba7b0efSSimon J. Gerraty FStr_Done(inout_str); 405dba7b0efSSimon J. Gerraty *inout_str = FStr_InitRefer(NULL); 406b0c40a00SSimon J. Gerraty return false; 407dba7b0efSSimon J. Gerraty } 4089f45a3c8SSimon J. Gerraty par->p = p; 409dba7b0efSSimon J. Gerraty 410dba7b0efSSimon J. Gerraty if (atStart && is_separator(par->p[0])) 411b0c40a00SSimon J. Gerraty return false; 412dba7b0efSSimon J. Gerraty 413dba7b0efSSimon J. Gerraty Buf_AddStr(buf, inout_str->str); 414dba7b0efSSimon J. Gerraty FStr_Done(inout_str); 415dba7b0efSSimon J. Gerraty *inout_str = FStr_InitRefer(NULL); /* not finished yet */ 416b0c40a00SSimon J. Gerraty return true; 417dba7b0efSSimon J. Gerraty } 418dba7b0efSSimon J. Gerraty 419dba7b0efSSimon J. Gerraty /* 420d5e0a182SSimon J. Gerraty * Parse a string from an expression or an optionally quoted string, 4219f45a3c8SSimon J. Gerraty * on the left-hand and right-hand sides of comparisons. 4223955d011SMarcel Moolenaar * 423548bfc56SSimon J. Gerraty * Return the string without any enclosing quotes, or NULL on error. 424b0c40a00SSimon J. Gerraty * Sets out_quoted if the leaf was a quoted string literal. 4253955d011SMarcel Moolenaar */ 426548bfc56SSimon J. Gerraty static FStr 42712904384SSimon J. Gerraty CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK, 428548bfc56SSimon J. Gerraty bool *out_quoted) 4293955d011SMarcel Moolenaar { 4303955d011SMarcel Moolenaar Buffer buf; 43106b9b3e0SSimon J. Gerraty FStr str; 432b0c40a00SSimon J. Gerraty bool quoted; 4332c3632d1SSimon J. Gerraty const char *start; 4343955d011SMarcel Moolenaar 435e2eeea75SSimon J. Gerraty Buf_Init(&buf); 43606b9b3e0SSimon J. Gerraty str = FStr_InitRefer(NULL); 437e2eeea75SSimon J. Gerraty *out_quoted = quoted = par->p[0] == '"'; 438956e45f6SSimon J. Gerraty start = par->p; 439e2eeea75SSimon J. Gerraty if (quoted) 440956e45f6SSimon J. Gerraty par->p++; 44106b9b3e0SSimon J. Gerraty 44206b9b3e0SSimon J. Gerraty while (par->p[0] != '\0' && str.str == NULL) { 443956e45f6SSimon J. Gerraty switch (par->p[0]) { 4443955d011SMarcel Moolenaar case '\\': 445956e45f6SSimon J. Gerraty par->p++; 446956e45f6SSimon J. Gerraty if (par->p[0] != '\0') { 447956e45f6SSimon J. Gerraty Buf_AddByte(&buf, par->p[0]); 448956e45f6SSimon J. Gerraty par->p++; 4493955d011SMarcel Moolenaar } 450956e45f6SSimon J. Gerraty continue; 4513955d011SMarcel Moolenaar case '"': 452956e45f6SSimon J. Gerraty par->p++; 453dba7b0efSSimon J. Gerraty if (quoted) 4549f45a3c8SSimon J. Gerraty goto return_buf; /* skip the closing quote */ 455dba7b0efSSimon J. Gerraty Buf_AddByte(&buf, '"'); 456956e45f6SSimon J. Gerraty continue; 457e2eeea75SSimon J. Gerraty case ')': /* see is_separator */ 4583955d011SMarcel Moolenaar case '!': 4593955d011SMarcel Moolenaar case '=': 4603955d011SMarcel Moolenaar case '>': 4613955d011SMarcel Moolenaar case '<': 4623955d011SMarcel Moolenaar case ' ': 4633955d011SMarcel Moolenaar case '\t': 464e2eeea75SSimon J. Gerraty if (!quoted) 4659f45a3c8SSimon J. Gerraty goto return_buf; 466956e45f6SSimon J. Gerraty Buf_AddByte(&buf, par->p[0]); 467956e45f6SSimon J. Gerraty par->p++; 468956e45f6SSimon J. Gerraty continue; 4693955d011SMarcel Moolenaar case '$': 470dba7b0efSSimon J. Gerraty if (!CondParser_StringExpr(par, 471dba7b0efSSimon J. Gerraty start, doEval, quoted, &buf, &str)) 4729f45a3c8SSimon J. Gerraty goto return_str; 473956e45f6SSimon J. Gerraty continue; 4743955d011SMarcel Moolenaar default: 47512904384SSimon J. Gerraty if (!unquotedOK && !quoted && *start != '$' && 47606b9b3e0SSimon J. Gerraty !ch_isdigit(*start)) { 47706b9b3e0SSimon J. Gerraty str = FStr_InitRefer(NULL); 4789f45a3c8SSimon J. Gerraty goto return_str; 47928a6bc81SSimon J. Gerraty } 480956e45f6SSimon J. Gerraty Buf_AddByte(&buf, par->p[0]); 481956e45f6SSimon J. Gerraty par->p++; 482956e45f6SSimon J. Gerraty continue; 4833955d011SMarcel Moolenaar } 4843955d011SMarcel Moolenaar } 4859f45a3c8SSimon J. Gerraty return_buf: 486dba7b0efSSimon J. Gerraty str = FStr_InitOwn(buf.data); 48712904384SSimon J. Gerraty buf.data = NULL; 4889f45a3c8SSimon J. Gerraty return_str: 48912904384SSimon J. Gerraty Buf_Done(&buf); 490548bfc56SSimon J. Gerraty return str; 4913955d011SMarcel Moolenaar } 4922c3632d1SSimon J. Gerraty 49306b9b3e0SSimon J. Gerraty /* 49406b9b3e0SSimon J. Gerraty * Evaluate a "comparison without operator", such as in ".if ${VAR}" or 49506b9b3e0SSimon J. Gerraty * ".if 0". 49606b9b3e0SSimon J. Gerraty */ 497b0c40a00SSimon J. Gerraty static bool 498148ee845SSimon J. Gerraty EvalTruthy(CondParser *par, const char *value, bool quoted) 4993955d011SMarcel Moolenaar { 500e2eeea75SSimon J. Gerraty double num; 501956e45f6SSimon J. Gerraty 502e2eeea75SSimon J. Gerraty if (quoted) 503e2eeea75SSimon J. Gerraty return value[0] != '\0'; 504e2eeea75SSimon J. Gerraty if (TryParseNumber(value, &num)) 505e2eeea75SSimon J. Gerraty return num != 0.0; 506dba7b0efSSimon J. Gerraty if (par->plain) 507e2eeea75SSimon J. Gerraty return value[0] != '\0'; 5089f45a3c8SSimon J. Gerraty return par->evalBare(value) != par->negateEvalBare; 509956e45f6SSimon J. Gerraty } 510956e45f6SSimon J. Gerraty 511956e45f6SSimon J. Gerraty /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */ 512b0c40a00SSimon J. Gerraty static bool 513dba7b0efSSimon J. Gerraty EvalCompareNum(double lhs, ComparisonOp op, double rhs) 514956e45f6SSimon J. Gerraty { 5151d3f2ddcSSimon J. Gerraty DEBUG3(COND, "Comparing %f %s %f\n", lhs, opname[op], rhs); 516956e45f6SSimon J. Gerraty 517dba7b0efSSimon J. Gerraty switch (op) { 518dba7b0efSSimon J. Gerraty case LT: 519dba7b0efSSimon J. Gerraty return lhs < rhs; 520dba7b0efSSimon J. Gerraty case LE: 521dba7b0efSSimon J. Gerraty return lhs <= rhs; 522dba7b0efSSimon J. Gerraty case GT: 523dba7b0efSSimon J. Gerraty return lhs > rhs; 524dba7b0efSSimon J. Gerraty case GE: 525dba7b0efSSimon J. Gerraty return lhs >= rhs; 5264fde40d9SSimon J. Gerraty case EQ: 527dba7b0efSSimon J. Gerraty return lhs == rhs; 5284fde40d9SSimon J. Gerraty default: 5294fde40d9SSimon J. Gerraty return lhs != rhs; 530956e45f6SSimon J. Gerraty } 531956e45f6SSimon J. Gerraty } 532956e45f6SSimon J. Gerraty 533956e45f6SSimon J. Gerraty static Token 534dba7b0efSSimon J. Gerraty EvalCompareStr(CondParser *par, const char *lhs, 535dba7b0efSSimon J. Gerraty ComparisonOp op, const char *rhs) 536956e45f6SSimon J. Gerraty { 537dba7b0efSSimon J. Gerraty if (op != EQ && op != NE) { 538dba7b0efSSimon J. Gerraty Parse_Error(PARSE_FATAL, 5394fde40d9SSimon J. Gerraty "Comparison with '%s' requires both operands " 5404fde40d9SSimon J. Gerraty "'%s' and '%s' to be numeric", 5414fde40d9SSimon J. Gerraty opname[op], lhs, rhs); 542b0c40a00SSimon J. Gerraty par->printedError = true; 543956e45f6SSimon J. Gerraty return TOK_ERROR; 544956e45f6SSimon J. Gerraty } 545956e45f6SSimon J. Gerraty 5461d3f2ddcSSimon J. Gerraty DEBUG3(COND, "Comparing \"%s\" %s \"%s\"\n", lhs, opname[op], rhs); 547dba7b0efSSimon J. Gerraty return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0)); 548956e45f6SSimon J. Gerraty } 549956e45f6SSimon J. Gerraty 550956e45f6SSimon J. Gerraty /* Evaluate a comparison, such as "${VAR} == 12345". */ 551956e45f6SSimon J. Gerraty static Token 552b0c40a00SSimon J. Gerraty EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted, 553b0c40a00SSimon J. Gerraty ComparisonOp op, const char *rhs, bool rhsQuoted) 554956e45f6SSimon J. Gerraty { 5553955d011SMarcel Moolenaar double left, right; 5563955d011SMarcel Moolenaar 557956e45f6SSimon J. Gerraty if (!rhsQuoted && !lhsQuoted) 558956e45f6SSimon J. Gerraty if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right)) 559dba7b0efSSimon J. Gerraty return ToToken(EvalCompareNum(left, op, right)); 560956e45f6SSimon J. Gerraty 561dba7b0efSSimon J. Gerraty return EvalCompareStr(par, lhs, op, rhs); 562dba7b0efSSimon J. Gerraty } 563dba7b0efSSimon J. Gerraty 564b0c40a00SSimon J. Gerraty static bool 565dba7b0efSSimon J. Gerraty CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op) 566dba7b0efSSimon J. Gerraty { 567dba7b0efSSimon J. Gerraty const char *p = par->p; 568dba7b0efSSimon J. Gerraty 5699f45a3c8SSimon J. Gerraty if (p[0] == '<' && p[1] == '=') 5709f45a3c8SSimon J. Gerraty return par->p += 2, *out_op = LE, true; 5719f45a3c8SSimon J. Gerraty if (p[0] == '<') 5729f45a3c8SSimon J. Gerraty return par->p += 1, *out_op = LT, true; 5739f45a3c8SSimon J. Gerraty if (p[0] == '>' && p[1] == '=') 5749f45a3c8SSimon J. Gerraty return par->p += 2, *out_op = GE, true; 5759f45a3c8SSimon J. Gerraty if (p[0] == '>') 5769f45a3c8SSimon J. Gerraty return par->p += 1, *out_op = GT, true; 5779f45a3c8SSimon J. Gerraty if (p[0] == '=' && p[1] == '=') 5789f45a3c8SSimon J. Gerraty return par->p += 2, *out_op = EQ, true; 5799f45a3c8SSimon J. Gerraty if (p[0] == '!' && p[1] == '=') 5809f45a3c8SSimon J. Gerraty return par->p += 2, *out_op = NE, true; 581b0c40a00SSimon J. Gerraty return false; 582956e45f6SSimon J. Gerraty } 583956e45f6SSimon J. Gerraty 58406b9b3e0SSimon J. Gerraty /* 58506b9b3e0SSimon J. Gerraty * Parse a comparison condition such as: 586956e45f6SSimon J. Gerraty * 587956e45f6SSimon J. Gerraty * 0 588956e45f6SSimon J. Gerraty * ${VAR:Mpattern} 589956e45f6SSimon J. Gerraty * ${VAR} == value 590956e45f6SSimon J. Gerraty * ${VAR:U0} < 12345 591956e45f6SSimon J. Gerraty */ 592956e45f6SSimon J. Gerraty static Token 593b0c40a00SSimon J. Gerraty CondParser_Comparison(CondParser *par, bool doEval) 594956e45f6SSimon J. Gerraty { 595956e45f6SSimon J. Gerraty Token t = TOK_ERROR; 59606b9b3e0SSimon J. Gerraty FStr lhs, rhs; 597dba7b0efSSimon J. Gerraty ComparisonOp op; 598b0c40a00SSimon J. Gerraty bool lhsQuoted, rhsQuoted; 599956e45f6SSimon J. Gerraty 600548bfc56SSimon J. Gerraty lhs = CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhsQuoted); 60106b9b3e0SSimon J. Gerraty if (lhs.str == NULL) 602e2eeea75SSimon J. Gerraty goto done_lhs; 6033955d011SMarcel Moolenaar 604956e45f6SSimon J. Gerraty CondParser_SkipWhitespace(par); 6053955d011SMarcel Moolenaar 606dba7b0efSSimon J. Gerraty if (!CondParser_ComparisonOp(par, &op)) { 607148ee845SSimon J. Gerraty t = ToToken(doEval && EvalTruthy(par, lhs.str, lhsQuoted)); 608e2eeea75SSimon J. Gerraty goto done_lhs; 6093955d011SMarcel Moolenaar } 6103955d011SMarcel Moolenaar 611956e45f6SSimon J. Gerraty CondParser_SkipWhitespace(par); 6123955d011SMarcel Moolenaar 613956e45f6SSimon J. Gerraty if (par->p[0] == '\0') { 614dba7b0efSSimon J. Gerraty Parse_Error(PARSE_FATAL, 61512904384SSimon J. Gerraty "Missing right-hand side of operator '%s'", opname[op]); 616b0c40a00SSimon J. Gerraty par->printedError = true; 617e2eeea75SSimon J. Gerraty goto done_lhs; 6183955d011SMarcel Moolenaar } 6193955d011SMarcel Moolenaar 620548bfc56SSimon J. Gerraty rhs = CondParser_Leaf(par, doEval, true, &rhsQuoted); 6211d3f2ddcSSimon J. Gerraty t = rhs.str == NULL ? TOK_ERROR 6221d3f2ddcSSimon J. Gerraty : !doEval ? TOK_FALSE 6231d3f2ddcSSimon J. Gerraty : EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted); 62406b9b3e0SSimon J. Gerraty FStr_Done(&rhs); 6251d3f2ddcSSimon J. Gerraty 626e2eeea75SSimon J. Gerraty done_lhs: 62706b9b3e0SSimon J. Gerraty FStr_Done(&lhs); 6283955d011SMarcel Moolenaar return t; 6293955d011SMarcel Moolenaar } 6303955d011SMarcel Moolenaar 63106b9b3e0SSimon J. Gerraty /* 63206b9b3e0SSimon J. Gerraty * The argument to empty() is a variable name, optionally followed by 63306b9b3e0SSimon J. Gerraty * variable modifiers. 63406b9b3e0SSimon J. Gerraty */ 63512904384SSimon J. Gerraty static bool 63612904384SSimon J. Gerraty CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token) 6373955d011SMarcel Moolenaar { 638d5e0a182SSimon J. Gerraty const char *p = par->p; 63912904384SSimon J. Gerraty Token tok; 64006b9b3e0SSimon J. Gerraty FStr val; 6413955d011SMarcel Moolenaar 642d5e0a182SSimon J. Gerraty if (!skip_string(&p, "empty")) 64312904384SSimon J. Gerraty return false; 6443955d011SMarcel Moolenaar 645d5e0a182SSimon J. Gerraty cpp_skip_whitespace(&p); 646d5e0a182SSimon J. Gerraty if (*p != '(') 64712904384SSimon J. Gerraty return false; 64812904384SSimon J. Gerraty 649d5e0a182SSimon J. Gerraty p--; /* Make p[1] point to the '('. */ 6508d5c8e21SSimon J. Gerraty val = Var_Parse(&p, SCOPE_CMDLINE, doEval ? VARE_EVAL : VARE_PARSE); 651956e45f6SSimon J. Gerraty /* TODO: handle errors */ 6523955d011SMarcel Moolenaar 65312904384SSimon J. Gerraty if (val.str == var_Error) 65412904384SSimon J. Gerraty tok = TOK_ERROR; 65512904384SSimon J. Gerraty else { 65606b9b3e0SSimon J. Gerraty cpp_skip_whitespace(&val.str); 6579f45a3c8SSimon J. Gerraty tok = ToToken(doEval && val.str[0] == '\0'); 6583955d011SMarcel Moolenaar } 6593955d011SMarcel Moolenaar 66012904384SSimon J. Gerraty FStr_Done(&val); 66112904384SSimon J. Gerraty *out_token = tok; 662d5e0a182SSimon J. Gerraty par->p = p; 66312904384SSimon J. Gerraty return true; 6643955d011SMarcel Moolenaar } 6653955d011SMarcel Moolenaar 6662f2a5ecdSSimon J. Gerraty /* Parse a function call expression, such as 'exists(${file})'. */ 667b0c40a00SSimon J. Gerraty static bool 668b0c40a00SSimon J. Gerraty CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token) 6693955d011SMarcel Moolenaar { 6709f45a3c8SSimon J. Gerraty char *arg; 6719f45a3c8SSimon J. Gerraty const char *p = par->p; 6729f45a3c8SSimon J. Gerraty bool (*fn)(const char *); 6739f45a3c8SSimon J. Gerraty const char *fn_name = p; 674e2eeea75SSimon J. Gerraty 6759f45a3c8SSimon J. Gerraty if (skip_string(&p, "defined")) 6769f45a3c8SSimon J. Gerraty fn = FuncDefined; 6779f45a3c8SSimon J. Gerraty else if (skip_string(&p, "make")) 6789f45a3c8SSimon J. Gerraty fn = FuncMake; 6799f45a3c8SSimon J. Gerraty else if (skip_string(&p, "exists")) 6809f45a3c8SSimon J. Gerraty fn = FuncExists; 6819f45a3c8SSimon J. Gerraty else if (skip_string(&p, "target")) 6829f45a3c8SSimon J. Gerraty fn = FuncTarget; 6839f45a3c8SSimon J. Gerraty else if (skip_string(&p, "commands")) 6849f45a3c8SSimon J. Gerraty fn = FuncCommands; 6859f45a3c8SSimon J. Gerraty else 68612904384SSimon J. Gerraty return false; 687e2eeea75SSimon J. Gerraty 6889f45a3c8SSimon J. Gerraty cpp_skip_whitespace(&p); 6899f45a3c8SSimon J. Gerraty if (*p != '(') 69012904384SSimon J. Gerraty return false; 691e2eeea75SSimon J. Gerraty 6929f45a3c8SSimon J. Gerraty arg = ParseFuncArg(par, &p, doEval, fn_name); 6939f45a3c8SSimon J. Gerraty *out_token = ToToken(doEval && 6949f45a3c8SSimon J. Gerraty arg != NULL && arg[0] != '\0' && fn(arg)); 695e2eeea75SSimon J. Gerraty free(arg); 6969f45a3c8SSimon J. Gerraty 6979f45a3c8SSimon J. Gerraty par->p = p; 698b0c40a00SSimon J. Gerraty return true; 699e2eeea75SSimon J. Gerraty } 700e2eeea75SSimon J. Gerraty 70106b9b3e0SSimon J. Gerraty /* 70212904384SSimon J. Gerraty * Parse a comparison that neither starts with '"' nor '$', such as the 70312904384SSimon J. Gerraty * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without 704d5e0a182SSimon J. Gerraty * operator, which is a number, an expression or a string literal. 70512904384SSimon J. Gerraty * 70612904384SSimon J. Gerraty * TODO: Can this be merged into CondParser_Comparison? 70706b9b3e0SSimon J. Gerraty */ 708e2eeea75SSimon J. Gerraty static Token 709b0c40a00SSimon J. Gerraty CondParser_ComparisonOrLeaf(CondParser *par, bool doEval) 710e2eeea75SSimon J. Gerraty { 7113955d011SMarcel Moolenaar Token t; 7129f45a3c8SSimon J. Gerraty char *arg; 713d5e0a182SSimon J. Gerraty const char *p; 7143955d011SMarcel Moolenaar 715d5e0a182SSimon J. Gerraty p = par->p; 716d5e0a182SSimon J. Gerraty if (ch_isdigit(p[0]) || p[0] == '-' || p[0] == '+') 717956e45f6SSimon J. Gerraty return CondParser_Comparison(par, doEval); 7183955d011SMarcel Moolenaar 7193955d011SMarcel Moolenaar /* 720d5e0a182SSimon J. Gerraty * Most likely we have a bare word to apply the default function to. 721d5e0a182SSimon J. Gerraty * However, ".if a == b" gets here when the "a" is unquoted and 722d5e0a182SSimon J. Gerraty * doesn't start with a '$'. This surprises people. 72306b9b3e0SSimon J. Gerraty * If what follows the function argument is a '=' or '!' then the 72406b9b3e0SSimon J. Gerraty * syntax would be invalid if we did "defined(a)" - so instead treat 72506b9b3e0SSimon J. Gerraty * as an expression. 7263955d011SMarcel Moolenaar */ 727b0c40a00SSimon J. Gerraty /* 728d5e0a182SSimon J. Gerraty * XXX: In edge cases, an expression may be evaluated twice, 7299f45a3c8SSimon J. Gerraty * see cond-token-plain.mk, keyword 'twice'. 730b0c40a00SSimon J. Gerraty */ 731d5e0a182SSimon J. Gerraty arg = ParseWord(&p, doEval); 7329f45a3c8SSimon J. Gerraty assert(arg[0] != '\0'); 733*6a7405f5SSimon J. Gerraty cpp_skip_hspace(&p); 7349f45a3c8SSimon J. Gerraty 7358d5c8e21SSimon J. Gerraty if (*p == '=' || *p == '!' || *p == '<' || *p == '>') { 7368d5c8e21SSimon J. Gerraty free(arg); 737956e45f6SSimon J. Gerraty return CondParser_Comparison(par, doEval); 7388d5c8e21SSimon J. Gerraty } 739d5e0a182SSimon J. Gerraty par->p = p; 7403955d011SMarcel Moolenaar 7413955d011SMarcel Moolenaar /* 7423955d011SMarcel Moolenaar * Evaluate the argument using the default function. 743956e45f6SSimon J. Gerraty * This path always treats .if as .ifdef. To get here, the character 7443955d011SMarcel Moolenaar * after .if must have been taken literally, so the argument cannot 745d5e0a182SSimon J. Gerraty * be empty - even if it contained an expression. 7463955d011SMarcel Moolenaar */ 7479f45a3c8SSimon J. Gerraty t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare); 7483955d011SMarcel Moolenaar free(arg); 7493955d011SMarcel Moolenaar return t; 7503955d011SMarcel Moolenaar } 7513955d011SMarcel Moolenaar 752956e45f6SSimon J. Gerraty /* Return the next token or comparison result from the parser. */ 7533955d011SMarcel Moolenaar static Token 754b0c40a00SSimon J. Gerraty CondParser_Token(CondParser *par, bool doEval) 7553955d011SMarcel Moolenaar { 7563955d011SMarcel Moolenaar Token t; 7573955d011SMarcel Moolenaar 758956e45f6SSimon J. Gerraty t = par->curr; 7593955d011SMarcel Moolenaar if (t != TOK_NONE) { 760956e45f6SSimon J. Gerraty par->curr = TOK_NONE; 7613955d011SMarcel Moolenaar return t; 7623955d011SMarcel Moolenaar } 7633955d011SMarcel Moolenaar 764e2eeea75SSimon J. Gerraty cpp_skip_hspace(&par->p); 7653955d011SMarcel Moolenaar 766956e45f6SSimon J. Gerraty switch (par->p[0]) { 7673955d011SMarcel Moolenaar 7683955d011SMarcel Moolenaar case '(': 769956e45f6SSimon J. Gerraty par->p++; 7703955d011SMarcel Moolenaar return TOK_LPAREN; 7713955d011SMarcel Moolenaar 7723955d011SMarcel Moolenaar case ')': 773956e45f6SSimon J. Gerraty par->p++; 7743955d011SMarcel Moolenaar return TOK_RPAREN; 7753955d011SMarcel Moolenaar 7763955d011SMarcel Moolenaar case '|': 777956e45f6SSimon J. Gerraty par->p++; 778e2eeea75SSimon J. Gerraty if (par->p[0] == '|') 779956e45f6SSimon J. Gerraty par->p++; 78022619282SSimon J. Gerraty else { 781e2eeea75SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Unknown operator '|'"); 782b0c40a00SSimon J. Gerraty par->printedError = true; 783e2eeea75SSimon J. Gerraty return TOK_ERROR; 7843955d011SMarcel Moolenaar } 7853955d011SMarcel Moolenaar return TOK_OR; 7863955d011SMarcel Moolenaar 7873955d011SMarcel Moolenaar case '&': 788956e45f6SSimon J. Gerraty par->p++; 789e2eeea75SSimon J. Gerraty if (par->p[0] == '&') 790956e45f6SSimon J. Gerraty par->p++; 79122619282SSimon J. Gerraty else { 792e2eeea75SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Unknown operator '&'"); 793b0c40a00SSimon J. Gerraty par->printedError = true; 794e2eeea75SSimon J. Gerraty return TOK_ERROR; 7953955d011SMarcel Moolenaar } 7963955d011SMarcel Moolenaar return TOK_AND; 7973955d011SMarcel Moolenaar 7983955d011SMarcel Moolenaar case '!': 799956e45f6SSimon J. Gerraty par->p++; 8003955d011SMarcel Moolenaar return TOK_NOT; 8013955d011SMarcel Moolenaar 802e2eeea75SSimon J. Gerraty case '#': /* XXX: see unit-tests/cond-token-plain.mk */ 803e2eeea75SSimon J. Gerraty case '\n': /* XXX: why should this end the condition? */ 804e2eeea75SSimon J. Gerraty /* Probably obsolete now, from 1993-03-21. */ 8053955d011SMarcel Moolenaar case '\0': 8063955d011SMarcel Moolenaar return TOK_EOF; 8073955d011SMarcel Moolenaar 8083955d011SMarcel Moolenaar case '"': 8093955d011SMarcel Moolenaar case '$': 810956e45f6SSimon J. Gerraty return CondParser_Comparison(par, doEval); 8113955d011SMarcel Moolenaar 8123955d011SMarcel Moolenaar default: 81312904384SSimon J. Gerraty if (CondParser_FuncCallEmpty(par, doEval, &t)) 81412904384SSimon J. Gerraty return t; 815b0c40a00SSimon J. Gerraty if (CondParser_FuncCall(par, doEval, &t)) 816b0c40a00SSimon J. Gerraty return t; 817b0c40a00SSimon J. Gerraty return CondParser_ComparisonOrLeaf(par, doEval); 8183955d011SMarcel Moolenaar } 8193955d011SMarcel Moolenaar } 8203955d011SMarcel Moolenaar 82112904384SSimon J. Gerraty /* Skip the next token if it equals t. */ 82212904384SSimon J. Gerraty static bool 82312904384SSimon J. Gerraty CondParser_Skip(CondParser *par, Token t) 82412904384SSimon J. Gerraty { 82512904384SSimon J. Gerraty Token actual; 82612904384SSimon J. Gerraty 82712904384SSimon J. Gerraty actual = CondParser_Token(par, false); 82812904384SSimon J. Gerraty if (actual == t) 82912904384SSimon J. Gerraty return true; 83012904384SSimon J. Gerraty 83112904384SSimon J. Gerraty assert(par->curr == TOK_NONE); 83212904384SSimon J. Gerraty assert(actual != TOK_NONE); 83312904384SSimon J. Gerraty par->curr = actual; 83412904384SSimon J. Gerraty return false; 83512904384SSimon J. Gerraty } 83612904384SSimon J. Gerraty 83706b9b3e0SSimon J. Gerraty /* 838dba7b0efSSimon J. Gerraty * Term -> '(' Or ')' 839dba7b0efSSimon J. Gerraty * Term -> '!' Term 840dba7b0efSSimon J. Gerraty * Term -> Leaf Operator Leaf 841dba7b0efSSimon J. Gerraty * Term -> Leaf 8423955d011SMarcel Moolenaar */ 843dba7b0efSSimon J. Gerraty static CondResult 844b0c40a00SSimon J. Gerraty CondParser_Term(CondParser *par, bool doEval) 8453955d011SMarcel Moolenaar { 846dba7b0efSSimon J. Gerraty CondResult res; 8473955d011SMarcel Moolenaar Token t; 848c59c3bf3SSimon J. Gerraty bool neg = false; 8493955d011SMarcel Moolenaar 850c59c3bf3SSimon J. Gerraty while ((t = CondParser_Token(par, doEval)) == TOK_NOT) 851c59c3bf3SSimon J. Gerraty neg = !neg; 852c59c3bf3SSimon J. Gerraty 853c59c3bf3SSimon J. Gerraty if (t == TOK_TRUE || t == TOK_FALSE) 854c59c3bf3SSimon J. Gerraty return neg == (t == TOK_FALSE) ? CR_TRUE : CR_FALSE; 8553955d011SMarcel Moolenaar 856dba7b0efSSimon J. Gerraty if (t == TOK_LPAREN) { 857dba7b0efSSimon J. Gerraty res = CondParser_Or(par, doEval); 858dba7b0efSSimon J. Gerraty if (res == CR_ERROR) 859dba7b0efSSimon J. Gerraty return CR_ERROR; 860dba7b0efSSimon J. Gerraty if (CondParser_Token(par, doEval) != TOK_RPAREN) 861dba7b0efSSimon J. Gerraty return CR_ERROR; 862c59c3bf3SSimon J. Gerraty return neg == (res == CR_FALSE) ? CR_TRUE : CR_FALSE; 8633955d011SMarcel Moolenaar } 864dba7b0efSSimon J. Gerraty 865dba7b0efSSimon J. Gerraty return CR_ERROR; 8663955d011SMarcel Moolenaar } 8672c3632d1SSimon J. Gerraty 86806b9b3e0SSimon J. Gerraty /* 86912904384SSimon J. Gerraty * And -> Term ('&&' Term)* 8703955d011SMarcel Moolenaar */ 871dba7b0efSSimon J. Gerraty static CondResult 872b0c40a00SSimon J. Gerraty CondParser_And(CondParser *par, bool doEval) 8733955d011SMarcel Moolenaar { 87412904384SSimon J. Gerraty CondResult res, rhs; 8753955d011SMarcel Moolenaar 87612904384SSimon J. Gerraty res = CR_TRUE; 87712904384SSimon J. Gerraty do { 87812904384SSimon J. Gerraty if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR) 879dba7b0efSSimon J. Gerraty return CR_ERROR; 88012904384SSimon J. Gerraty if (rhs == CR_FALSE) { 88112904384SSimon J. Gerraty res = CR_FALSE; 88212904384SSimon J. Gerraty doEval = false; 8833955d011SMarcel Moolenaar } 88412904384SSimon J. Gerraty } while (CondParser_Skip(par, TOK_AND)); 885dba7b0efSSimon J. Gerraty 886dba7b0efSSimon J. Gerraty return res; 8873955d011SMarcel Moolenaar } 8882c3632d1SSimon J. Gerraty 88906b9b3e0SSimon J. Gerraty /* 89012904384SSimon J. Gerraty * Or -> And ('||' And)* 8913955d011SMarcel Moolenaar */ 892dba7b0efSSimon J. Gerraty static CondResult 893b0c40a00SSimon J. Gerraty CondParser_Or(CondParser *par, bool doEval) 8943955d011SMarcel Moolenaar { 89512904384SSimon J. Gerraty CondResult res, rhs; 8963955d011SMarcel Moolenaar 89712904384SSimon J. Gerraty res = CR_FALSE; 89812904384SSimon J. Gerraty do { 89912904384SSimon J. Gerraty if ((rhs = CondParser_And(par, doEval)) == CR_ERROR) 900dba7b0efSSimon J. Gerraty return CR_ERROR; 90112904384SSimon J. Gerraty if (rhs == CR_TRUE) { 90212904384SSimon J. Gerraty res = CR_TRUE; 90312904384SSimon J. Gerraty doEval = false; 9043955d011SMarcel Moolenaar } 90512904384SSimon J. Gerraty } while (CondParser_Skip(par, TOK_OR)); 906dba7b0efSSimon J. Gerraty 907dba7b0efSSimon J. Gerraty return res; 9083955d011SMarcel Moolenaar } 9093955d011SMarcel Moolenaar 91006b9b3e0SSimon J. Gerraty /* 911d5e0a182SSimon J. Gerraty * Evaluate the condition, including any side effects from the 912956e45f6SSimon J. Gerraty * expressions in the condition. The condition consists of &&, ||, !, 913956e45f6SSimon J. Gerraty * function(arg), comparisons and parenthetical groupings thereof. 9143955d011SMarcel Moolenaar */ 9159f45a3c8SSimon J. Gerraty static CondResult 9169f45a3c8SSimon J. Gerraty CondEvalExpression(const char *cond, bool plain, 91712904384SSimon J. Gerraty bool (*evalBare)(const char *), bool negate, 91812904384SSimon J. Gerraty bool eprint, bool leftUnquotedOK) 9193955d011SMarcel Moolenaar { 920956e45f6SSimon J. Gerraty CondParser par; 9219f45a3c8SSimon J. Gerraty CondResult rval; 922*6a7405f5SSimon J. Gerraty int parseErrorsBefore = parseErrors; 9233955d011SMarcel Moolenaar 924e2eeea75SSimon J. Gerraty cpp_skip_hspace(&cond); 9253955d011SMarcel Moolenaar 926dba7b0efSSimon J. Gerraty par.plain = plain; 927dba7b0efSSimon J. Gerraty par.evalBare = evalBare; 928dba7b0efSSimon J. Gerraty par.negateEvalBare = negate; 92912904384SSimon J. Gerraty par.leftUnquotedOK = leftUnquotedOK; 930956e45f6SSimon J. Gerraty par.p = cond; 931956e45f6SSimon J. Gerraty par.curr = TOK_NONE; 932b0c40a00SSimon J. Gerraty par.printedError = false; 9333955d011SMarcel Moolenaar 934c59c3bf3SSimon J. Gerraty DEBUG1(COND, "CondParser_Eval: %s\n", par.p); 935c59c3bf3SSimon J. Gerraty rval = CondParser_Or(&par, true); 936c59c3bf3SSimon J. Gerraty if (par.curr != TOK_EOF) 937c59c3bf3SSimon J. Gerraty rval = CR_ERROR; 9383955d011SMarcel Moolenaar 939*6a7405f5SSimon J. Gerraty if (rval == CR_ERROR && eprint && !par.printedError 940*6a7405f5SSimon J. Gerraty && parseErrors == parseErrorsBefore) 941*6a7405f5SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Malformed conditional '%s'", cond); 9423955d011SMarcel Moolenaar 9433955d011SMarcel Moolenaar return rval; 9443955d011SMarcel Moolenaar } 9453955d011SMarcel Moolenaar 94606b9b3e0SSimon J. Gerraty /* 94706b9b3e0SSimon J. Gerraty * Evaluate a condition in a :? modifier, such as 94806b9b3e0SSimon J. Gerraty * ${"${VAR}" == value:?yes:no}. 94906b9b3e0SSimon J. Gerraty */ 9509f45a3c8SSimon J. Gerraty CondResult 9519f45a3c8SSimon J. Gerraty Cond_EvalCondition(const char *cond) 952956e45f6SSimon J. Gerraty { 9539f45a3c8SSimon J. Gerraty return CondEvalExpression(cond, true, 95412904384SSimon J. Gerraty FuncDefined, false, false, true); 955956e45f6SSimon J. Gerraty } 9563955d011SMarcel Moolenaar 957b0c40a00SSimon J. Gerraty static bool 95806b9b3e0SSimon J. Gerraty IsEndif(const char *p) 95906b9b3e0SSimon J. Gerraty { 96006b9b3e0SSimon J. Gerraty return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' && 96106b9b3e0SSimon J. Gerraty p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]); 96206b9b3e0SSimon J. Gerraty } 96306b9b3e0SSimon J. Gerraty 964b0c40a00SSimon J. Gerraty static bool 965b0c40a00SSimon J. Gerraty DetermineKindOfConditional(const char **pp, bool *out_plain, 96612904384SSimon J. Gerraty bool (**out_evalBare)(const char *), 967b0c40a00SSimon J. Gerraty bool *out_negate) 968dba7b0efSSimon J. Gerraty { 9699f45a3c8SSimon J. Gerraty const char *p = *pp + 2; 970dba7b0efSSimon J. Gerraty 971b0c40a00SSimon J. Gerraty *out_plain = false; 972dba7b0efSSimon J. Gerraty *out_evalBare = FuncDefined; 9739f45a3c8SSimon J. Gerraty *out_negate = skip_string(&p, "n"); 9749f45a3c8SSimon J. Gerraty 9759f45a3c8SSimon J. Gerraty if (skip_string(&p, "def")) { /* .ifdef and .ifndef */ 9769f45a3c8SSimon J. Gerraty } else if (skip_string(&p, "make")) /* .ifmake and .ifnmake */ 977dba7b0efSSimon J. Gerraty *out_evalBare = FuncMake; 9789f45a3c8SSimon J. Gerraty else if (!*out_negate) /* plain .if */ 979b0c40a00SSimon J. Gerraty *out_plain = true; 9809f45a3c8SSimon J. Gerraty else 9819f45a3c8SSimon J. Gerraty goto unknown_directive; 9829f45a3c8SSimon J. Gerraty if (ch_isalpha(*p)) 9839f45a3c8SSimon J. Gerraty goto unknown_directive; 9849f45a3c8SSimon J. Gerraty 9859f45a3c8SSimon J. Gerraty *pp = p; 9869f45a3c8SSimon J. Gerraty return true; 9879f45a3c8SSimon J. Gerraty 9889f45a3c8SSimon J. Gerraty unknown_directive: 989b0c40a00SSimon J. Gerraty return false; 990dba7b0efSSimon J. Gerraty } 991dba7b0efSSimon J. Gerraty 99206b9b3e0SSimon J. Gerraty /* 99306b9b3e0SSimon J. Gerraty * Evaluate the conditional directive in the line, which is one of: 9943955d011SMarcel Moolenaar * 995e2eeea75SSimon J. Gerraty * .if <cond> 996e2eeea75SSimon J. Gerraty * .ifmake <cond> 997e2eeea75SSimon J. Gerraty * .ifnmake <cond> 998e2eeea75SSimon J. Gerraty * .ifdef <cond> 999e2eeea75SSimon J. Gerraty * .ifndef <cond> 1000e2eeea75SSimon J. Gerraty * .elif <cond> 1001e2eeea75SSimon J. Gerraty * .elifmake <cond> 1002e2eeea75SSimon J. Gerraty * .elifnmake <cond> 1003e2eeea75SSimon J. Gerraty * .elifdef <cond> 1004e2eeea75SSimon J. Gerraty * .elifndef <cond> 1005e2eeea75SSimon J. Gerraty * .else 1006e2eeea75SSimon J. Gerraty * .endif 1007e2eeea75SSimon J. Gerraty * 1008e2eeea75SSimon J. Gerraty * In these directives, <cond> consists of &&, ||, !, function(arg), 1009e2eeea75SSimon J. Gerraty * comparisons, expressions, bare words, numbers and strings, and 1010e2eeea75SSimon J. Gerraty * parenthetical groupings thereof. 1011956e45f6SSimon J. Gerraty * 1012956e45f6SSimon J. Gerraty * Results: 10139f45a3c8SSimon J. Gerraty * CR_TRUE to continue parsing the lines that follow the 1014b0c40a00SSimon J. Gerraty * conditional (when <cond> evaluates to true) 10159f45a3c8SSimon J. Gerraty * CR_FALSE to skip the lines after the conditional 1016b0c40a00SSimon J. Gerraty * (when <cond> evaluates to false, or when a previous 1017d5e0a182SSimon J. Gerraty * branch was already taken) 10189f45a3c8SSimon J. Gerraty * CR_ERROR if the conditional was not valid, either because of 1019956e45f6SSimon J. Gerraty * a syntax error or because some variable was undefined 1020956e45f6SSimon J. Gerraty * or because the condition could not be evaluated 10213955d011SMarcel Moolenaar */ 10229f45a3c8SSimon J. Gerraty CondResult 102306b9b3e0SSimon J. Gerraty Cond_EvalLine(const char *line) 10243955d011SMarcel Moolenaar { 1025e2eeea75SSimon J. Gerraty typedef enum IfState { 1026e2eeea75SSimon J. Gerraty 1027b0c40a00SSimon J. Gerraty /* None of the previous <cond> evaluated to true. */ 1028e2eeea75SSimon J. Gerraty IFS_INITIAL = 0, 1029e2eeea75SSimon J. Gerraty 10309f45a3c8SSimon J. Gerraty /* 10319f45a3c8SSimon J. Gerraty * The previous <cond> evaluated to true. The lines following 10329f45a3c8SSimon J. Gerraty * this condition are interpreted. 10339f45a3c8SSimon J. Gerraty */ 1034e2eeea75SSimon J. Gerraty IFS_ACTIVE = 1 << 0, 1035e2eeea75SSimon J. Gerraty 1036e2eeea75SSimon J. Gerraty /* The previous directive was an '.else'. */ 1037e2eeea75SSimon J. Gerraty IFS_SEEN_ELSE = 1 << 1, 1038e2eeea75SSimon J. Gerraty 1039b0c40a00SSimon J. Gerraty /* One of the previous <cond> evaluated to true. */ 1040e2eeea75SSimon J. Gerraty IFS_WAS_ACTIVE = 1 << 2 1041e2eeea75SSimon J. Gerraty 1042e2eeea75SSimon J. Gerraty } IfState; 1043e2eeea75SSimon J. Gerraty 1044e2eeea75SSimon J. Gerraty static enum IfState *cond_states = NULL; 1045e2eeea75SSimon J. Gerraty static unsigned int cond_states_cap = 128; 10463955d011SMarcel Moolenaar 1047b0c40a00SSimon J. Gerraty bool plain; 104812904384SSimon J. Gerraty bool (*evalBare)(const char *); 1049b0c40a00SSimon J. Gerraty bool negate; 1050b0c40a00SSimon J. Gerraty bool isElif; 10519f45a3c8SSimon J. Gerraty CondResult res; 1052e2eeea75SSimon J. Gerraty IfState state; 1053e2eeea75SSimon J. Gerraty const char *p = line; 10543955d011SMarcel Moolenaar 1055e2eeea75SSimon J. Gerraty if (cond_states == NULL) { 105606b9b3e0SSimon J. Gerraty cond_states = bmake_malloc( 105706b9b3e0SSimon J. Gerraty cond_states_cap * sizeof *cond_states); 1058e2eeea75SSimon J. Gerraty cond_states[0] = IFS_ACTIVE; 105959a02420SSimon J. Gerraty } 10603955d011SMarcel Moolenaar 1061e2eeea75SSimon J. Gerraty p++; /* skip the leading '.' */ 1062e2eeea75SSimon J. Gerraty cpp_skip_hspace(&p); 1063e2eeea75SSimon J. Gerraty 1064d5e0a182SSimon J. Gerraty if (IsEndif(p)) { 106506b9b3e0SSimon J. Gerraty if (p[5] != '\0') { 106606b9b3e0SSimon J. Gerraty Parse_Error(PARSE_FATAL, 106712904384SSimon J. Gerraty "The .endif directive does not take arguments"); 1068e2eeea75SSimon J. Gerraty } 1069e2eeea75SSimon J. Gerraty 10704fde40d9SSimon J. Gerraty if (cond_depth == CurFile_CondMinDepth()) { 1071956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "if-less endif"); 10729f45a3c8SSimon J. Gerraty return CR_TRUE; 10733955d011SMarcel Moolenaar } 1074e2eeea75SSimon J. Gerraty 10753955d011SMarcel Moolenaar /* Return state for previous conditional */ 10763955d011SMarcel Moolenaar cond_depth--; 1077148ee845SSimon J. Gerraty Parse_GuardEndif(); 1078e2eeea75SSimon J. Gerraty return cond_states[cond_depth] & IFS_ACTIVE 10799f45a3c8SSimon J. Gerraty ? CR_TRUE : CR_FALSE; 10803955d011SMarcel Moolenaar } 10813955d011SMarcel Moolenaar 108206b9b3e0SSimon J. Gerraty /* Parse the name of the directive, such as 'if', 'elif', 'endif'. */ 108306b9b3e0SSimon J. Gerraty if (p[0] == 'e') { 1084d5e0a182SSimon J. Gerraty if (p[1] != 'l') 10859f45a3c8SSimon J. Gerraty return CR_ERROR; 108606b9b3e0SSimon J. Gerraty 10873955d011SMarcel Moolenaar /* Quite likely this is 'else' or 'elif' */ 1088e2eeea75SSimon J. Gerraty p += 2; 10899f45a3c8SSimon J. Gerraty if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) { 109006b9b3e0SSimon J. Gerraty if (p[2] != '\0') 1091e2eeea75SSimon J. Gerraty Parse_Error(PARSE_FATAL, 109206b9b3e0SSimon J. Gerraty "The .else directive " 109312904384SSimon J. Gerraty "does not take arguments"); 1094e2eeea75SSimon J. Gerraty 10954fde40d9SSimon J. Gerraty if (cond_depth == CurFile_CondMinDepth()) { 1096956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "if-less else"); 10979f45a3c8SSimon J. Gerraty return CR_TRUE; 10983955d011SMarcel Moolenaar } 1099148ee845SSimon J. Gerraty Parse_GuardElse(); 11003955d011SMarcel Moolenaar 1101e2eeea75SSimon J. Gerraty state = cond_states[cond_depth]; 1102e2eeea75SSimon J. Gerraty if (state == IFS_INITIAL) { 1103e2eeea75SSimon J. Gerraty state = IFS_ACTIVE | IFS_SEEN_ELSE; 1104e2eeea75SSimon J. Gerraty } else { 1105e2eeea75SSimon J. Gerraty if (state & IFS_SEEN_ELSE) 110606b9b3e0SSimon J. Gerraty Parse_Error(PARSE_WARNING, 110706b9b3e0SSimon J. Gerraty "extra else"); 1108e2eeea75SSimon J. Gerraty state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE; 11093955d011SMarcel Moolenaar } 1110e2eeea75SSimon J. Gerraty cond_states[cond_depth] = state; 1111e2eeea75SSimon J. Gerraty 11129f45a3c8SSimon J. Gerraty return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE; 11133955d011SMarcel Moolenaar } 11143955d011SMarcel Moolenaar /* Assume for now it is an elif */ 1115b0c40a00SSimon J. Gerraty isElif = true; 11163955d011SMarcel Moolenaar } else 1117b0c40a00SSimon J. Gerraty isElif = false; 11183955d011SMarcel Moolenaar 1119d5e0a182SSimon J. Gerraty if (p[0] != 'i' || p[1] != 'f') 1120d5e0a182SSimon J. Gerraty return CR_ERROR; 11213955d011SMarcel Moolenaar 1122dba7b0efSSimon J. Gerraty if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate)) 11239f45a3c8SSimon J. Gerraty return CR_ERROR; 11243955d011SMarcel Moolenaar 11253955d011SMarcel Moolenaar if (isElif) { 11264fde40d9SSimon J. Gerraty if (cond_depth == CurFile_CondMinDepth()) { 1127956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "if-less elif"); 11289f45a3c8SSimon J. Gerraty return CR_TRUE; 11293955d011SMarcel Moolenaar } 1130148ee845SSimon J. Gerraty Parse_GuardElse(); 1131e2eeea75SSimon J. Gerraty state = cond_states[cond_depth]; 1132e2eeea75SSimon J. Gerraty if (state & IFS_SEEN_ELSE) { 11333955d011SMarcel Moolenaar Parse_Error(PARSE_WARNING, "extra elif"); 113406b9b3e0SSimon J. Gerraty cond_states[cond_depth] = 113506b9b3e0SSimon J. Gerraty IFS_WAS_ACTIVE | IFS_SEEN_ELSE; 11369f45a3c8SSimon J. Gerraty return CR_FALSE; 11373955d011SMarcel Moolenaar } 1138e2eeea75SSimon J. Gerraty if (state != IFS_INITIAL) { 1139e2eeea75SSimon J. Gerraty cond_states[cond_depth] = IFS_WAS_ACTIVE; 11409f45a3c8SSimon J. Gerraty return CR_FALSE; 11413955d011SMarcel Moolenaar } 11423955d011SMarcel Moolenaar } else { 11433955d011SMarcel Moolenaar /* Normal .if */ 1144e2eeea75SSimon J. Gerraty if (cond_depth + 1 >= cond_states_cap) { 114559a02420SSimon J. Gerraty /* 114659a02420SSimon J. Gerraty * This is rare, but not impossible. 114759a02420SSimon J. Gerraty * In meta mode, dirdeps.mk (only runs at level 0) 114859a02420SSimon J. Gerraty * can need more than the default. 114959a02420SSimon J. Gerraty */ 1150e2eeea75SSimon J. Gerraty cond_states_cap += 32; 1151e2eeea75SSimon J. Gerraty cond_states = bmake_realloc(cond_states, 11529f45a3c8SSimon J. Gerraty cond_states_cap * sizeof *cond_states); 11533955d011SMarcel Moolenaar } 1154e2eeea75SSimon J. Gerraty state = cond_states[cond_depth]; 11553955d011SMarcel Moolenaar cond_depth++; 1156e2eeea75SSimon J. Gerraty if (!(state & IFS_ACTIVE)) { 1157e2eeea75SSimon J. Gerraty cond_states[cond_depth] = IFS_WAS_ACTIVE; 11589f45a3c8SSimon J. Gerraty return CR_FALSE; 11593955d011SMarcel Moolenaar } 11603955d011SMarcel Moolenaar } 11613955d011SMarcel Moolenaar 11629f45a3c8SSimon J. Gerraty res = CondEvalExpression(p, plain, evalBare, negate, true, false); 11639f45a3c8SSimon J. Gerraty if (res == CR_ERROR) { 11649f45a3c8SSimon J. Gerraty /* Syntax error, error message already output. */ 11659f45a3c8SSimon J. Gerraty /* Skip everything to the matching '.endif'. */ 11669f45a3c8SSimon J. Gerraty /* An extra '.else' is not detected in this case. */ 1167e2eeea75SSimon J. Gerraty cond_states[cond_depth] = IFS_WAS_ACTIVE; 11689f45a3c8SSimon J. Gerraty return CR_FALSE; 11693955d011SMarcel Moolenaar } 11703955d011SMarcel Moolenaar 11719f45a3c8SSimon J. Gerraty cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL; 11729f45a3c8SSimon J. Gerraty return res; 11733955d011SMarcel Moolenaar } 11743955d011SMarcel Moolenaar 1175148ee845SSimon J. Gerraty static bool 1176148ee845SSimon J. Gerraty ParseVarnameGuard(const char **pp, const char **varname) 1177148ee845SSimon J. Gerraty { 1178148ee845SSimon J. Gerraty const char *p = *pp; 1179148ee845SSimon J. Gerraty 1180148ee845SSimon J. Gerraty if (ch_isalpha(*p) || *p == '_') { 1181148ee845SSimon J. Gerraty while (ch_isalnum(*p) || *p == '_') 1182148ee845SSimon J. Gerraty p++; 1183148ee845SSimon J. Gerraty *varname = *pp; 1184148ee845SSimon J. Gerraty *pp = p; 1185148ee845SSimon J. Gerraty return true; 1186148ee845SSimon J. Gerraty } 1187148ee845SSimon J. Gerraty return false; 1188148ee845SSimon J. Gerraty } 1189148ee845SSimon J. Gerraty 1190148ee845SSimon J. Gerraty /* Extracts the multiple-inclusion guard from a conditional, if any. */ 1191148ee845SSimon J. Gerraty Guard * 1192148ee845SSimon J. Gerraty Cond_ExtractGuard(const char *line) 1193148ee845SSimon J. Gerraty { 1194148ee845SSimon J. Gerraty const char *p, *varname; 1195148ee845SSimon J. Gerraty Substring dir; 1196148ee845SSimon J. Gerraty Guard *guard; 1197148ee845SSimon J. Gerraty 1198148ee845SSimon J. Gerraty p = line + 1; /* skip the '.' */ 1199148ee845SSimon J. Gerraty cpp_skip_hspace(&p); 1200148ee845SSimon J. Gerraty 1201148ee845SSimon J. Gerraty dir.start = p; 1202148ee845SSimon J. Gerraty while (ch_isalpha(*p)) 1203148ee845SSimon J. Gerraty p++; 1204148ee845SSimon J. Gerraty dir.end = p; 1205148ee845SSimon J. Gerraty cpp_skip_hspace(&p); 1206148ee845SSimon J. Gerraty 1207148ee845SSimon J. Gerraty if (Substring_Equals(dir, "if")) { 1208148ee845SSimon J. Gerraty if (skip_string(&p, "!defined(")) { 1209148ee845SSimon J. Gerraty if (ParseVarnameGuard(&p, &varname) 1210148ee845SSimon J. Gerraty && strcmp(p, ")") == 0) 1211148ee845SSimon J. Gerraty goto found_variable; 1212148ee845SSimon J. Gerraty } else if (skip_string(&p, "!target(")) { 1213148ee845SSimon J. Gerraty const char *arg_p = p; 1214148ee845SSimon J. Gerraty free(ParseWord(&p, false)); 1215148ee845SSimon J. Gerraty if (strcmp(p, ")") == 0) { 1216148ee845SSimon J. Gerraty guard = bmake_malloc(sizeof(*guard)); 1217148ee845SSimon J. Gerraty guard->kind = GK_TARGET; 121898875883SSimon J. Gerraty guard->name = ParseWord(&arg_p, true); 1219148ee845SSimon J. Gerraty return guard; 1220148ee845SSimon J. Gerraty } 1221148ee845SSimon J. Gerraty } 1222148ee845SSimon J. Gerraty } else if (Substring_Equals(dir, "ifndef")) { 1223148ee845SSimon J. Gerraty if (ParseVarnameGuard(&p, &varname) && *p == '\0') 1224148ee845SSimon J. Gerraty goto found_variable; 1225148ee845SSimon J. Gerraty } 1226148ee845SSimon J. Gerraty return NULL; 1227148ee845SSimon J. Gerraty 1228148ee845SSimon J. Gerraty found_variable: 1229148ee845SSimon J. Gerraty guard = bmake_malloc(sizeof(*guard)); 123098875883SSimon J. Gerraty guard->kind = GK_VARIABLE; 1231148ee845SSimon J. Gerraty guard->name = bmake_strsedup(varname, p); 1232148ee845SSimon J. Gerraty return guard; 1233148ee845SSimon J. Gerraty } 1234148ee845SSimon J. Gerraty 12353955d011SMarcel Moolenaar void 12364fde40d9SSimon J. Gerraty Cond_EndFile(void) 12373955d011SMarcel Moolenaar { 12384fde40d9SSimon J. Gerraty unsigned int open_conds = cond_depth - CurFile_CondMinDepth(); 12393955d011SMarcel Moolenaar 12404fde40d9SSimon J. Gerraty if (open_conds != 0) { 124106b9b3e0SSimon J. Gerraty Parse_Error(PARSE_FATAL, "%u open conditional%s", 124206b9b3e0SSimon J. Gerraty open_conds, open_conds == 1 ? "" : "s"); 12434fde40d9SSimon J. Gerraty cond_depth = CurFile_CondMinDepth(); 12443955d011SMarcel Moolenaar } 12453955d011SMarcel Moolenaar } 1246