1*22619282SSimon J. Gerraty /* $NetBSD: cond.c,v 1.366 2024/07/06 21:21:09 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*22619282SSimon J. Gerraty MAKE_RCSID("$NetBSD: cond.c,v 1.366 2024/07/06 21:21:09 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 == '$') { 224b0c40a00SSimon J. Gerraty VarEvalMode emode = doEval 2258d5c8e21SSimon J. Gerraty ? VARE_EVAL_DEFINED 2268d5c8e21SSimon J. Gerraty : VARE_PARSE; 22798875883SSimon J. Gerraty /* 22898875883SSimon J. Gerraty * TODO: make Var_Parse complain about undefined 22998875883SSimon J. Gerraty * variables. 23098875883SSimon J. Gerraty */ 2318c973ee2SSimon J. Gerraty FStr nestedVal = Var_Parse(&p, SCOPE_CMDLINE, emode); 232956e45f6SSimon J. Gerraty /* TODO: handle errors */ 2331d3f2ddcSSimon J. Gerraty Buf_AddStr(&word, nestedVal.str); 23406b9b3e0SSimon J. Gerraty FStr_Done(&nestedVal); 2353955d011SMarcel Moolenaar continue; 2363955d011SMarcel Moolenaar } 2373955d011SMarcel Moolenaar if (ch == '(') 238d5e0a182SSimon J. Gerraty depth++; 239d5e0a182SSimon J. Gerraty else if (ch == ')' && --depth < 0) 2403955d011SMarcel Moolenaar break; 2411d3f2ddcSSimon J. Gerraty Buf_AddByte(&word, ch); 242956e45f6SSimon J. Gerraty p++; 2433955d011SMarcel Moolenaar } 2443955d011SMarcel Moolenaar 2459f45a3c8SSimon J. Gerraty cpp_skip_hspace(&p); 2469f45a3c8SSimon J. Gerraty *pp = p; 2473955d011SMarcel Moolenaar 2481d3f2ddcSSimon J. Gerraty return Buf_DoneData(&word); 2499f45a3c8SSimon J. Gerraty } 2509f45a3c8SSimon J. Gerraty 2519f45a3c8SSimon J. Gerraty /* Parse the function argument, including the surrounding parentheses. */ 2529f45a3c8SSimon J. Gerraty static char * 2539f45a3c8SSimon J. Gerraty ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func) 2549f45a3c8SSimon J. Gerraty { 2559f45a3c8SSimon J. Gerraty const char *p = *pp; 2569f45a3c8SSimon J. Gerraty char *res; 2579f45a3c8SSimon J. Gerraty 258d5e0a182SSimon J. Gerraty p++; /* skip the '(' */ 2599f45a3c8SSimon J. Gerraty cpp_skip_hspace(&p); 2609f45a3c8SSimon J. Gerraty res = ParseWord(&p, doEval); 261e2eeea75SSimon J. Gerraty cpp_skip_hspace(&p); 2623955d011SMarcel Moolenaar 2639f45a3c8SSimon J. Gerraty if (*p++ != ')') { 2649f45a3c8SSimon J. Gerraty int len = 0; 2659f45a3c8SSimon J. Gerraty while (ch_isalpha(func[len])) 2669f45a3c8SSimon J. Gerraty len++; 2679f45a3c8SSimon J. Gerraty 268dba7b0efSSimon J. Gerraty Parse_Error(PARSE_FATAL, 2699f45a3c8SSimon J. Gerraty "Missing closing parenthesis for %.*s()", len, func); 270b0c40a00SSimon J. Gerraty par->printedError = true; 2719f45a3c8SSimon J. Gerraty free(res); 2729f45a3c8SSimon J. Gerraty return NULL; 2733955d011SMarcel Moolenaar } 2743955d011SMarcel Moolenaar 275956e45f6SSimon J. Gerraty *pp = p; 2769f45a3c8SSimon J. Gerraty return res; 2773955d011SMarcel Moolenaar } 2782c3632d1SSimon J. Gerraty 2791d3f2ddcSSimon J. Gerraty /* See if the given variable is defined. */ 280b0c40a00SSimon J. Gerraty static bool 2819f45a3c8SSimon J. Gerraty FuncDefined(const char *var) 2823955d011SMarcel Moolenaar { 2839f45a3c8SSimon J. Gerraty return Var_Exists(SCOPE_CMDLINE, var); 2843955d011SMarcel Moolenaar } 2852c3632d1SSimon J. Gerraty 2861d3f2ddcSSimon J. Gerraty /* See if a target matching targetPattern is requested to be made. */ 287b0c40a00SSimon J. Gerraty static bool 2881d3f2ddcSSimon J. Gerraty FuncMake(const char *targetPattern) 2893955d011SMarcel Moolenaar { 290956e45f6SSimon J. Gerraty StringListNode *ln; 291148ee845SSimon J. Gerraty bool warned = false; 292956e45f6SSimon J. Gerraty 293148ee845SSimon J. Gerraty for (ln = opts.create.first; ln != NULL; ln = ln->next) { 294148ee845SSimon J. Gerraty StrMatchResult res = Str_Match(ln->datum, targetPattern); 295148ee845SSimon J. Gerraty if (res.error != NULL && !warned) { 296148ee845SSimon J. Gerraty warned = true; 297148ee845SSimon J. Gerraty Parse_Error(PARSE_WARNING, 298148ee845SSimon J. Gerraty "%s in pattern argument '%s' to function 'make'", 299148ee845SSimon J. Gerraty res.error, targetPattern); 300148ee845SSimon J. Gerraty } 301148ee845SSimon J. Gerraty if (res.matched) 302b0c40a00SSimon J. Gerraty return true; 303148ee845SSimon J. Gerraty } 304b0c40a00SSimon J. Gerraty return false; 3053955d011SMarcel Moolenaar } 3062c3632d1SSimon J. Gerraty 3072c3632d1SSimon J. Gerraty /* See if the given file exists. */ 308b0c40a00SSimon J. Gerraty static bool 3099f45a3c8SSimon J. Gerraty FuncExists(const char *file) 3103955d011SMarcel Moolenaar { 311b0c40a00SSimon J. Gerraty bool result; 3123955d011SMarcel Moolenaar char *path; 3133955d011SMarcel Moolenaar 3149f45a3c8SSimon J. Gerraty path = Dir_FindFile(file, &dirSearchPath); 315e2eeea75SSimon J. Gerraty DEBUG2(COND, "exists(%s) result is \"%s\"\n", 3169f45a3c8SSimon J. Gerraty file, path != NULL ? path : ""); 317e2eeea75SSimon J. Gerraty result = path != NULL; 3183955d011SMarcel Moolenaar free(path); 3193841c287SSimon J. Gerraty return result; 3203955d011SMarcel Moolenaar } 3212c3632d1SSimon J. Gerraty 3222c3632d1SSimon J. Gerraty /* See if the given node exists and is an actual target. */ 323b0c40a00SSimon J. Gerraty static bool 3249f45a3c8SSimon J. Gerraty FuncTarget(const char *node) 3253955d011SMarcel Moolenaar { 3269f45a3c8SSimon J. Gerraty GNode *gn = Targ_FindNode(node); 327956e45f6SSimon J. Gerraty return gn != NULL && GNode_IsTarget(gn); 3283955d011SMarcel Moolenaar } 3293955d011SMarcel Moolenaar 33006b9b3e0SSimon J. Gerraty /* 33106b9b3e0SSimon J. Gerraty * See if the given node exists and is an actual target with commands 33206b9b3e0SSimon J. Gerraty * associated with it. 33306b9b3e0SSimon J. Gerraty */ 334b0c40a00SSimon J. Gerraty static bool 3359f45a3c8SSimon J. Gerraty FuncCommands(const char *node) 3363955d011SMarcel Moolenaar { 3379f45a3c8SSimon J. Gerraty GNode *gn = Targ_FindNode(node); 3389f45a3c8SSimon J. Gerraty return gn != NULL && GNode_IsTarget(gn) && 3399f45a3c8SSimon J. Gerraty !Lst_IsEmpty(&gn->commands); 3403955d011SMarcel Moolenaar } 3412c3632d1SSimon J. Gerraty 342e2eeea75SSimon J. Gerraty /* 343148ee845SSimon J. Gerraty * Convert the string to a floating point number. Accepted formats are 3449f45a3c8SSimon J. Gerraty * base-10 integer, base-16 integer and finite floating point numbers. 3453955d011SMarcel Moolenaar */ 346b0c40a00SSimon J. Gerraty static bool 347e2eeea75SSimon J. Gerraty TryParseNumber(const char *str, double *out_value) 3483955d011SMarcel Moolenaar { 349e2eeea75SSimon J. Gerraty char *end; 350e2eeea75SSimon J. Gerraty unsigned long ul_val; 351e2eeea75SSimon J. Gerraty double dbl_val; 3523955d011SMarcel Moolenaar 353e2eeea75SSimon J. Gerraty if (str[0] == '\0') { /* XXX: why is an empty string a number? */ 354e2eeea75SSimon J. Gerraty *out_value = 0.0; 355b0c40a00SSimon J. Gerraty return true; 356ac3446e9SSimon J. Gerraty } 357e2eeea75SSimon J. Gerraty 35812904384SSimon J. Gerraty errno = 0; 359e2eeea75SSimon J. Gerraty ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10); 360e2eeea75SSimon J. Gerraty if (*end == '\0' && errno != ERANGE) { 361e2eeea75SSimon J. Gerraty *out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val; 362b0c40a00SSimon J. Gerraty return true; 3633955d011SMarcel Moolenaar } 3643955d011SMarcel Moolenaar 365e2eeea75SSimon J. Gerraty if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E') 366b0c40a00SSimon J. Gerraty return false; /* skip the expensive strtod call */ 367e2eeea75SSimon J. Gerraty dbl_val = strtod(str, &end); 368e2eeea75SSimon J. Gerraty if (*end != '\0') 369b0c40a00SSimon J. Gerraty return false; 370e2eeea75SSimon J. Gerraty 371e2eeea75SSimon J. Gerraty *out_value = dbl_val; 372b0c40a00SSimon J. Gerraty return true; 3733955d011SMarcel Moolenaar } 3743955d011SMarcel Moolenaar 375b0c40a00SSimon J. Gerraty static bool 376956e45f6SSimon J. Gerraty is_separator(char ch) 377956e45f6SSimon J. Gerraty { 378b0c40a00SSimon J. Gerraty return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' || 379b0c40a00SSimon J. Gerraty ch == '>' || ch == '<' || ch == ')' /* but not '(' */; 380956e45f6SSimon J. Gerraty } 381956e45f6SSimon J. Gerraty 382dba7b0efSSimon J. Gerraty /* 383d5e0a182SSimon J. Gerraty * In a quoted or unquoted string literal or a number, parse an 3848c973ee2SSimon J. Gerraty * expression and add its value to the buffer. 3858c973ee2SSimon J. Gerraty * 3868c973ee2SSimon J. Gerraty * Return whether to continue parsing the leaf. 387dba7b0efSSimon J. Gerraty * 388dba7b0efSSimon J. Gerraty * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX} 389dba7b0efSSimon J. Gerraty */ 390b0c40a00SSimon J. Gerraty static bool 391dba7b0efSSimon J. Gerraty CondParser_StringExpr(CondParser *par, const char *start, 39212904384SSimon J. Gerraty bool doEval, bool quoted, 39312904384SSimon J. Gerraty Buffer *buf, FStr *inout_str) 394dba7b0efSSimon J. Gerraty { 395b0c40a00SSimon J. Gerraty VarEvalMode emode; 3969f45a3c8SSimon J. Gerraty const char *p; 397c59c3bf3SSimon J. Gerraty bool atStart; /* true means an expression outside quotes */ 398dba7b0efSSimon J. Gerraty 3998d5c8e21SSimon J. Gerraty emode = doEval && quoted ? VARE_EVAL 4008d5c8e21SSimon J. Gerraty : doEval ? VARE_EVAL_DEFINED 4018d5c8e21SSimon J. Gerraty : VARE_PARSE; 402dba7b0efSSimon J. Gerraty 4039f45a3c8SSimon J. Gerraty p = par->p; 4049f45a3c8SSimon J. Gerraty atStart = p == start; 4058c973ee2SSimon J. Gerraty *inout_str = Var_Parse(&p, SCOPE_CMDLINE, emode); 406dba7b0efSSimon J. Gerraty /* TODO: handle errors */ 407dba7b0efSSimon J. Gerraty if (inout_str->str == var_Error) { 408dba7b0efSSimon J. Gerraty FStr_Done(inout_str); 409dba7b0efSSimon J. Gerraty *inout_str = FStr_InitRefer(NULL); 410b0c40a00SSimon J. Gerraty return false; 411dba7b0efSSimon J. Gerraty } 4129f45a3c8SSimon J. Gerraty par->p = p; 413dba7b0efSSimon J. Gerraty 414dba7b0efSSimon J. Gerraty if (atStart && is_separator(par->p[0])) 415b0c40a00SSimon J. Gerraty return false; 416dba7b0efSSimon J. Gerraty 417dba7b0efSSimon J. Gerraty Buf_AddStr(buf, inout_str->str); 418dba7b0efSSimon J. Gerraty FStr_Done(inout_str); 419dba7b0efSSimon J. Gerraty *inout_str = FStr_InitRefer(NULL); /* not finished yet */ 420b0c40a00SSimon J. Gerraty return true; 421dba7b0efSSimon J. Gerraty } 422dba7b0efSSimon J. Gerraty 423dba7b0efSSimon J. Gerraty /* 424d5e0a182SSimon J. Gerraty * Parse a string from an expression or an optionally quoted string, 4259f45a3c8SSimon J. Gerraty * on the left-hand and right-hand sides of comparisons. 4263955d011SMarcel Moolenaar * 427548bfc56SSimon J. Gerraty * Return the string without any enclosing quotes, or NULL on error. 428b0c40a00SSimon J. Gerraty * Sets out_quoted if the leaf was a quoted string literal. 4293955d011SMarcel Moolenaar */ 430548bfc56SSimon J. Gerraty static FStr 43112904384SSimon J. Gerraty CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK, 432548bfc56SSimon J. Gerraty bool *out_quoted) 4333955d011SMarcel Moolenaar { 4343955d011SMarcel Moolenaar Buffer buf; 43506b9b3e0SSimon J. Gerraty FStr str; 436b0c40a00SSimon J. Gerraty bool quoted; 4372c3632d1SSimon J. Gerraty const char *start; 4383955d011SMarcel Moolenaar 439e2eeea75SSimon J. Gerraty Buf_Init(&buf); 44006b9b3e0SSimon J. Gerraty str = FStr_InitRefer(NULL); 441e2eeea75SSimon J. Gerraty *out_quoted = quoted = par->p[0] == '"'; 442956e45f6SSimon J. Gerraty start = par->p; 443e2eeea75SSimon J. Gerraty if (quoted) 444956e45f6SSimon J. Gerraty par->p++; 44506b9b3e0SSimon J. Gerraty 44606b9b3e0SSimon J. Gerraty while (par->p[0] != '\0' && str.str == NULL) { 447956e45f6SSimon J. Gerraty switch (par->p[0]) { 4483955d011SMarcel Moolenaar case '\\': 449956e45f6SSimon J. Gerraty par->p++; 450956e45f6SSimon J. Gerraty if (par->p[0] != '\0') { 451956e45f6SSimon J. Gerraty Buf_AddByte(&buf, par->p[0]); 452956e45f6SSimon J. Gerraty par->p++; 4533955d011SMarcel Moolenaar } 454956e45f6SSimon J. Gerraty continue; 4553955d011SMarcel Moolenaar case '"': 456956e45f6SSimon J. Gerraty par->p++; 457dba7b0efSSimon J. Gerraty if (quoted) 4589f45a3c8SSimon J. Gerraty goto return_buf; /* skip the closing quote */ 459dba7b0efSSimon J. Gerraty Buf_AddByte(&buf, '"'); 460956e45f6SSimon J. Gerraty continue; 461e2eeea75SSimon J. Gerraty case ')': /* see is_separator */ 4623955d011SMarcel Moolenaar case '!': 4633955d011SMarcel Moolenaar case '=': 4643955d011SMarcel Moolenaar case '>': 4653955d011SMarcel Moolenaar case '<': 4663955d011SMarcel Moolenaar case ' ': 4673955d011SMarcel Moolenaar case '\t': 468e2eeea75SSimon J. Gerraty if (!quoted) 4699f45a3c8SSimon J. Gerraty goto return_buf; 470956e45f6SSimon J. Gerraty Buf_AddByte(&buf, par->p[0]); 471956e45f6SSimon J. Gerraty par->p++; 472956e45f6SSimon J. Gerraty continue; 4733955d011SMarcel Moolenaar case '$': 474dba7b0efSSimon J. Gerraty if (!CondParser_StringExpr(par, 475dba7b0efSSimon J. Gerraty start, doEval, quoted, &buf, &str)) 4769f45a3c8SSimon J. Gerraty goto return_str; 477956e45f6SSimon J. Gerraty continue; 4783955d011SMarcel Moolenaar default: 47912904384SSimon J. Gerraty if (!unquotedOK && !quoted && *start != '$' && 48006b9b3e0SSimon J. Gerraty !ch_isdigit(*start)) { 48106b9b3e0SSimon J. Gerraty str = FStr_InitRefer(NULL); 4829f45a3c8SSimon J. Gerraty goto return_str; 48328a6bc81SSimon J. Gerraty } 484956e45f6SSimon J. Gerraty Buf_AddByte(&buf, par->p[0]); 485956e45f6SSimon J. Gerraty par->p++; 486956e45f6SSimon J. Gerraty continue; 4873955d011SMarcel Moolenaar } 4883955d011SMarcel Moolenaar } 4899f45a3c8SSimon J. Gerraty return_buf: 490dba7b0efSSimon J. Gerraty str = FStr_InitOwn(buf.data); 49112904384SSimon J. Gerraty buf.data = NULL; 4929f45a3c8SSimon J. Gerraty return_str: 49312904384SSimon J. Gerraty Buf_Done(&buf); 494548bfc56SSimon J. Gerraty return str; 4953955d011SMarcel Moolenaar } 4962c3632d1SSimon J. Gerraty 49706b9b3e0SSimon J. Gerraty /* 49806b9b3e0SSimon J. Gerraty * Evaluate a "comparison without operator", such as in ".if ${VAR}" or 49906b9b3e0SSimon J. Gerraty * ".if 0". 50006b9b3e0SSimon J. Gerraty */ 501b0c40a00SSimon J. Gerraty static bool 502148ee845SSimon J. Gerraty EvalTruthy(CondParser *par, const char *value, bool quoted) 5033955d011SMarcel Moolenaar { 504e2eeea75SSimon J. Gerraty double num; 505956e45f6SSimon J. Gerraty 506e2eeea75SSimon J. Gerraty if (quoted) 507e2eeea75SSimon J. Gerraty return value[0] != '\0'; 508e2eeea75SSimon J. Gerraty if (TryParseNumber(value, &num)) 509e2eeea75SSimon J. Gerraty return num != 0.0; 510dba7b0efSSimon J. Gerraty if (par->plain) 511e2eeea75SSimon J. Gerraty return value[0] != '\0'; 5129f45a3c8SSimon J. Gerraty return par->evalBare(value) != par->negateEvalBare; 513956e45f6SSimon J. Gerraty } 514956e45f6SSimon J. Gerraty 515956e45f6SSimon J. Gerraty /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */ 516b0c40a00SSimon J. Gerraty static bool 517dba7b0efSSimon J. Gerraty EvalCompareNum(double lhs, ComparisonOp op, double rhs) 518956e45f6SSimon J. Gerraty { 5191d3f2ddcSSimon J. Gerraty DEBUG3(COND, "Comparing %f %s %f\n", lhs, opname[op], rhs); 520956e45f6SSimon J. Gerraty 521dba7b0efSSimon J. Gerraty switch (op) { 522dba7b0efSSimon J. Gerraty case LT: 523dba7b0efSSimon J. Gerraty return lhs < rhs; 524dba7b0efSSimon J. Gerraty case LE: 525dba7b0efSSimon J. Gerraty return lhs <= rhs; 526dba7b0efSSimon J. Gerraty case GT: 527dba7b0efSSimon J. Gerraty return lhs > rhs; 528dba7b0efSSimon J. Gerraty case GE: 529dba7b0efSSimon J. Gerraty return lhs >= rhs; 5304fde40d9SSimon J. Gerraty case EQ: 531dba7b0efSSimon J. Gerraty return lhs == rhs; 5324fde40d9SSimon J. Gerraty default: 5334fde40d9SSimon J. Gerraty return lhs != rhs; 534956e45f6SSimon J. Gerraty } 535956e45f6SSimon J. Gerraty } 536956e45f6SSimon J. Gerraty 537956e45f6SSimon J. Gerraty static Token 538dba7b0efSSimon J. Gerraty EvalCompareStr(CondParser *par, const char *lhs, 539dba7b0efSSimon J. Gerraty ComparisonOp op, const char *rhs) 540956e45f6SSimon J. Gerraty { 541dba7b0efSSimon J. Gerraty if (op != EQ && op != NE) { 542dba7b0efSSimon J. Gerraty Parse_Error(PARSE_FATAL, 5434fde40d9SSimon J. Gerraty "Comparison with '%s' requires both operands " 5444fde40d9SSimon J. Gerraty "'%s' and '%s' to be numeric", 5454fde40d9SSimon J. Gerraty opname[op], lhs, rhs); 546b0c40a00SSimon J. Gerraty par->printedError = true; 547956e45f6SSimon J. Gerraty return TOK_ERROR; 548956e45f6SSimon J. Gerraty } 549956e45f6SSimon J. Gerraty 5501d3f2ddcSSimon J. Gerraty DEBUG3(COND, "Comparing \"%s\" %s \"%s\"\n", lhs, opname[op], rhs); 551dba7b0efSSimon J. Gerraty return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0)); 552956e45f6SSimon J. Gerraty } 553956e45f6SSimon J. Gerraty 554956e45f6SSimon J. Gerraty /* Evaluate a comparison, such as "${VAR} == 12345". */ 555956e45f6SSimon J. Gerraty static Token 556b0c40a00SSimon J. Gerraty EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted, 557b0c40a00SSimon J. Gerraty ComparisonOp op, const char *rhs, bool rhsQuoted) 558956e45f6SSimon J. Gerraty { 5593955d011SMarcel Moolenaar double left, right; 5603955d011SMarcel Moolenaar 561956e45f6SSimon J. Gerraty if (!rhsQuoted && !lhsQuoted) 562956e45f6SSimon J. Gerraty if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right)) 563dba7b0efSSimon J. Gerraty return ToToken(EvalCompareNum(left, op, right)); 564956e45f6SSimon J. Gerraty 565dba7b0efSSimon J. Gerraty return EvalCompareStr(par, lhs, op, rhs); 566dba7b0efSSimon J. Gerraty } 567dba7b0efSSimon J. Gerraty 568b0c40a00SSimon J. Gerraty static bool 569dba7b0efSSimon J. Gerraty CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op) 570dba7b0efSSimon J. Gerraty { 571dba7b0efSSimon J. Gerraty const char *p = par->p; 572dba7b0efSSimon J. Gerraty 5739f45a3c8SSimon J. Gerraty if (p[0] == '<' && p[1] == '=') 5749f45a3c8SSimon J. Gerraty return par->p += 2, *out_op = LE, true; 5759f45a3c8SSimon J. Gerraty if (p[0] == '<') 5769f45a3c8SSimon J. Gerraty return par->p += 1, *out_op = LT, true; 5779f45a3c8SSimon J. Gerraty if (p[0] == '>' && p[1] == '=') 5789f45a3c8SSimon J. Gerraty return par->p += 2, *out_op = GE, true; 5799f45a3c8SSimon J. Gerraty if (p[0] == '>') 5809f45a3c8SSimon J. Gerraty return par->p += 1, *out_op = GT, true; 5819f45a3c8SSimon J. Gerraty if (p[0] == '=' && p[1] == '=') 5829f45a3c8SSimon J. Gerraty return par->p += 2, *out_op = EQ, true; 5839f45a3c8SSimon J. Gerraty if (p[0] == '!' && p[1] == '=') 5849f45a3c8SSimon J. Gerraty return par->p += 2, *out_op = NE, true; 585b0c40a00SSimon J. Gerraty return false; 586956e45f6SSimon J. Gerraty } 587956e45f6SSimon J. Gerraty 58806b9b3e0SSimon J. Gerraty /* 58906b9b3e0SSimon J. Gerraty * Parse a comparison condition such as: 590956e45f6SSimon J. Gerraty * 591956e45f6SSimon J. Gerraty * 0 592956e45f6SSimon J. Gerraty * ${VAR:Mpattern} 593956e45f6SSimon J. Gerraty * ${VAR} == value 594956e45f6SSimon J. Gerraty * ${VAR:U0} < 12345 595956e45f6SSimon J. Gerraty */ 596956e45f6SSimon J. Gerraty static Token 597b0c40a00SSimon J. Gerraty CondParser_Comparison(CondParser *par, bool doEval) 598956e45f6SSimon J. Gerraty { 599956e45f6SSimon J. Gerraty Token t = TOK_ERROR; 60006b9b3e0SSimon J. Gerraty FStr lhs, rhs; 601dba7b0efSSimon J. Gerraty ComparisonOp op; 602b0c40a00SSimon J. Gerraty bool lhsQuoted, rhsQuoted; 603956e45f6SSimon J. Gerraty 604548bfc56SSimon J. Gerraty lhs = CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhsQuoted); 60506b9b3e0SSimon J. Gerraty if (lhs.str == NULL) 606e2eeea75SSimon J. Gerraty goto done_lhs; 6073955d011SMarcel Moolenaar 608956e45f6SSimon J. Gerraty CondParser_SkipWhitespace(par); 6093955d011SMarcel Moolenaar 610dba7b0efSSimon J. Gerraty if (!CondParser_ComparisonOp(par, &op)) { 611148ee845SSimon J. Gerraty t = ToToken(doEval && EvalTruthy(par, lhs.str, lhsQuoted)); 612e2eeea75SSimon J. Gerraty goto done_lhs; 6133955d011SMarcel Moolenaar } 6143955d011SMarcel Moolenaar 615956e45f6SSimon J. Gerraty CondParser_SkipWhitespace(par); 6163955d011SMarcel Moolenaar 617956e45f6SSimon J. Gerraty if (par->p[0] == '\0') { 618dba7b0efSSimon J. Gerraty Parse_Error(PARSE_FATAL, 61912904384SSimon J. Gerraty "Missing right-hand side of operator '%s'", opname[op]); 620b0c40a00SSimon J. Gerraty par->printedError = true; 621e2eeea75SSimon J. Gerraty goto done_lhs; 6223955d011SMarcel Moolenaar } 6233955d011SMarcel Moolenaar 624548bfc56SSimon J. Gerraty rhs = CondParser_Leaf(par, doEval, true, &rhsQuoted); 6251d3f2ddcSSimon J. Gerraty t = rhs.str == NULL ? TOK_ERROR 6261d3f2ddcSSimon J. Gerraty : !doEval ? TOK_FALSE 6271d3f2ddcSSimon J. Gerraty : EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted); 62806b9b3e0SSimon J. Gerraty FStr_Done(&rhs); 6291d3f2ddcSSimon J. Gerraty 630e2eeea75SSimon J. Gerraty done_lhs: 63106b9b3e0SSimon J. Gerraty FStr_Done(&lhs); 6323955d011SMarcel Moolenaar return t; 6333955d011SMarcel Moolenaar } 6343955d011SMarcel Moolenaar 63506b9b3e0SSimon J. Gerraty /* 63606b9b3e0SSimon J. Gerraty * The argument to empty() is a variable name, optionally followed by 63706b9b3e0SSimon J. Gerraty * variable modifiers. 63806b9b3e0SSimon J. Gerraty */ 63912904384SSimon J. Gerraty static bool 64012904384SSimon J. Gerraty CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token) 6413955d011SMarcel Moolenaar { 642d5e0a182SSimon J. Gerraty const char *p = par->p; 64312904384SSimon J. Gerraty Token tok; 64406b9b3e0SSimon J. Gerraty FStr val; 6453955d011SMarcel Moolenaar 646d5e0a182SSimon J. Gerraty if (!skip_string(&p, "empty")) 64712904384SSimon J. Gerraty return false; 6483955d011SMarcel Moolenaar 649d5e0a182SSimon J. Gerraty cpp_skip_whitespace(&p); 650d5e0a182SSimon J. Gerraty if (*p != '(') 65112904384SSimon J. Gerraty return false; 65212904384SSimon J. Gerraty 653d5e0a182SSimon J. Gerraty p--; /* Make p[1] point to the '('. */ 6548d5c8e21SSimon J. Gerraty val = Var_Parse(&p, SCOPE_CMDLINE, doEval ? VARE_EVAL : VARE_PARSE); 655956e45f6SSimon J. Gerraty /* TODO: handle errors */ 6563955d011SMarcel Moolenaar 65712904384SSimon J. Gerraty if (val.str == var_Error) 65812904384SSimon J. Gerraty tok = TOK_ERROR; 65912904384SSimon J. Gerraty else { 66006b9b3e0SSimon J. Gerraty cpp_skip_whitespace(&val.str); 6619f45a3c8SSimon J. Gerraty tok = ToToken(doEval && val.str[0] == '\0'); 6623955d011SMarcel Moolenaar } 6633955d011SMarcel Moolenaar 66412904384SSimon J. Gerraty FStr_Done(&val); 66512904384SSimon J. Gerraty *out_token = tok; 666d5e0a182SSimon J. Gerraty par->p = p; 66712904384SSimon J. Gerraty return true; 6683955d011SMarcel Moolenaar } 6693955d011SMarcel Moolenaar 6702f2a5ecdSSimon J. Gerraty /* Parse a function call expression, such as 'exists(${file})'. */ 671b0c40a00SSimon J. Gerraty static bool 672b0c40a00SSimon J. Gerraty CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token) 6733955d011SMarcel Moolenaar { 6749f45a3c8SSimon J. Gerraty char *arg; 6759f45a3c8SSimon J. Gerraty const char *p = par->p; 6769f45a3c8SSimon J. Gerraty bool (*fn)(const char *); 6779f45a3c8SSimon J. Gerraty const char *fn_name = p; 678e2eeea75SSimon J. Gerraty 6799f45a3c8SSimon J. Gerraty if (skip_string(&p, "defined")) 6809f45a3c8SSimon J. Gerraty fn = FuncDefined; 6819f45a3c8SSimon J. Gerraty else if (skip_string(&p, "make")) 6829f45a3c8SSimon J. Gerraty fn = FuncMake; 6839f45a3c8SSimon J. Gerraty else if (skip_string(&p, "exists")) 6849f45a3c8SSimon J. Gerraty fn = FuncExists; 6859f45a3c8SSimon J. Gerraty else if (skip_string(&p, "target")) 6869f45a3c8SSimon J. Gerraty fn = FuncTarget; 6879f45a3c8SSimon J. Gerraty else if (skip_string(&p, "commands")) 6889f45a3c8SSimon J. Gerraty fn = FuncCommands; 6899f45a3c8SSimon J. Gerraty else 69012904384SSimon J. Gerraty return false; 691e2eeea75SSimon J. Gerraty 6929f45a3c8SSimon J. Gerraty cpp_skip_whitespace(&p); 6939f45a3c8SSimon J. Gerraty if (*p != '(') 69412904384SSimon J. Gerraty return false; 695e2eeea75SSimon J. Gerraty 6969f45a3c8SSimon J. Gerraty arg = ParseFuncArg(par, &p, doEval, fn_name); 6979f45a3c8SSimon J. Gerraty *out_token = ToToken(doEval && 6989f45a3c8SSimon J. Gerraty arg != NULL && arg[0] != '\0' && fn(arg)); 699e2eeea75SSimon J. Gerraty free(arg); 7009f45a3c8SSimon J. Gerraty 7019f45a3c8SSimon J. Gerraty par->p = p; 702b0c40a00SSimon J. Gerraty return true; 703e2eeea75SSimon J. Gerraty } 704e2eeea75SSimon J. Gerraty 70506b9b3e0SSimon J. Gerraty /* 70612904384SSimon J. Gerraty * Parse a comparison that neither starts with '"' nor '$', such as the 70712904384SSimon J. Gerraty * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without 708d5e0a182SSimon J. Gerraty * operator, which is a number, an expression or a string literal. 70912904384SSimon J. Gerraty * 71012904384SSimon J. Gerraty * TODO: Can this be merged into CondParser_Comparison? 71106b9b3e0SSimon J. Gerraty */ 712e2eeea75SSimon J. Gerraty static Token 713b0c40a00SSimon J. Gerraty CondParser_ComparisonOrLeaf(CondParser *par, bool doEval) 714e2eeea75SSimon J. Gerraty { 7153955d011SMarcel Moolenaar Token t; 7169f45a3c8SSimon J. Gerraty char *arg; 717d5e0a182SSimon J. Gerraty const char *p; 7183955d011SMarcel Moolenaar 719d5e0a182SSimon J. Gerraty p = par->p; 720d5e0a182SSimon J. Gerraty if (ch_isdigit(p[0]) || p[0] == '-' || p[0] == '+') 721956e45f6SSimon J. Gerraty return CondParser_Comparison(par, doEval); 7223955d011SMarcel Moolenaar 7233955d011SMarcel Moolenaar /* 724d5e0a182SSimon J. Gerraty * Most likely we have a bare word to apply the default function to. 725d5e0a182SSimon J. Gerraty * However, ".if a == b" gets here when the "a" is unquoted and 726d5e0a182SSimon J. Gerraty * doesn't start with a '$'. This surprises people. 72706b9b3e0SSimon J. Gerraty * If what follows the function argument is a '=' or '!' then the 72806b9b3e0SSimon J. Gerraty * syntax would be invalid if we did "defined(a)" - so instead treat 72906b9b3e0SSimon J. Gerraty * as an expression. 7303955d011SMarcel Moolenaar */ 731b0c40a00SSimon J. Gerraty /* 732d5e0a182SSimon J. Gerraty * XXX: In edge cases, an expression may be evaluated twice, 7339f45a3c8SSimon J. Gerraty * see cond-token-plain.mk, keyword 'twice'. 734b0c40a00SSimon J. Gerraty */ 735d5e0a182SSimon J. Gerraty arg = ParseWord(&p, doEval); 7369f45a3c8SSimon J. Gerraty assert(arg[0] != '\0'); 7379f45a3c8SSimon J. Gerraty 7388d5c8e21SSimon J. Gerraty if (*p == '=' || *p == '!' || *p == '<' || *p == '>') { 7398d5c8e21SSimon J. Gerraty free(arg); 740956e45f6SSimon J. Gerraty return CondParser_Comparison(par, doEval); 7418d5c8e21SSimon J. Gerraty } 742d5e0a182SSimon J. Gerraty par->p = p; 7433955d011SMarcel Moolenaar 7443955d011SMarcel Moolenaar /* 7453955d011SMarcel Moolenaar * Evaluate the argument using the default function. 746956e45f6SSimon J. Gerraty * This path always treats .if as .ifdef. To get here, the character 7473955d011SMarcel Moolenaar * after .if must have been taken literally, so the argument cannot 748d5e0a182SSimon J. Gerraty * be empty - even if it contained an expression. 7493955d011SMarcel Moolenaar */ 7509f45a3c8SSimon J. Gerraty t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare); 7513955d011SMarcel Moolenaar free(arg); 7523955d011SMarcel Moolenaar return t; 7533955d011SMarcel Moolenaar } 7543955d011SMarcel Moolenaar 755956e45f6SSimon J. Gerraty /* Return the next token or comparison result from the parser. */ 7563955d011SMarcel Moolenaar static Token 757b0c40a00SSimon J. Gerraty CondParser_Token(CondParser *par, bool doEval) 7583955d011SMarcel Moolenaar { 7593955d011SMarcel Moolenaar Token t; 7603955d011SMarcel Moolenaar 761956e45f6SSimon J. Gerraty t = par->curr; 7623955d011SMarcel Moolenaar if (t != TOK_NONE) { 763956e45f6SSimon J. Gerraty par->curr = TOK_NONE; 7643955d011SMarcel Moolenaar return t; 7653955d011SMarcel Moolenaar } 7663955d011SMarcel Moolenaar 767e2eeea75SSimon J. Gerraty cpp_skip_hspace(&par->p); 7683955d011SMarcel Moolenaar 769956e45f6SSimon J. Gerraty switch (par->p[0]) { 7703955d011SMarcel Moolenaar 7713955d011SMarcel Moolenaar case '(': 772956e45f6SSimon J. Gerraty par->p++; 7733955d011SMarcel Moolenaar return TOK_LPAREN; 7743955d011SMarcel Moolenaar 7753955d011SMarcel Moolenaar case ')': 776956e45f6SSimon J. Gerraty par->p++; 7773955d011SMarcel Moolenaar return TOK_RPAREN; 7783955d011SMarcel Moolenaar 7793955d011SMarcel Moolenaar case '|': 780956e45f6SSimon J. Gerraty par->p++; 781e2eeea75SSimon J. Gerraty if (par->p[0] == '|') 782956e45f6SSimon J. Gerraty par->p++; 783*22619282SSimon J. Gerraty else { 784e2eeea75SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Unknown operator '|'"); 785b0c40a00SSimon J. Gerraty par->printedError = true; 786e2eeea75SSimon J. Gerraty return TOK_ERROR; 7873955d011SMarcel Moolenaar } 7883955d011SMarcel Moolenaar return TOK_OR; 7893955d011SMarcel Moolenaar 7903955d011SMarcel Moolenaar case '&': 791956e45f6SSimon J. Gerraty par->p++; 792e2eeea75SSimon J. Gerraty if (par->p[0] == '&') 793956e45f6SSimon J. Gerraty par->p++; 794*22619282SSimon J. Gerraty else { 795e2eeea75SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Unknown operator '&'"); 796b0c40a00SSimon J. Gerraty par->printedError = true; 797e2eeea75SSimon J. Gerraty return TOK_ERROR; 7983955d011SMarcel Moolenaar } 7993955d011SMarcel Moolenaar return TOK_AND; 8003955d011SMarcel Moolenaar 8013955d011SMarcel Moolenaar case '!': 802956e45f6SSimon J. Gerraty par->p++; 8033955d011SMarcel Moolenaar return TOK_NOT; 8043955d011SMarcel Moolenaar 805e2eeea75SSimon J. Gerraty case '#': /* XXX: see unit-tests/cond-token-plain.mk */ 806e2eeea75SSimon J. Gerraty case '\n': /* XXX: why should this end the condition? */ 807e2eeea75SSimon J. Gerraty /* Probably obsolete now, from 1993-03-21. */ 8083955d011SMarcel Moolenaar case '\0': 8093955d011SMarcel Moolenaar return TOK_EOF; 8103955d011SMarcel Moolenaar 8113955d011SMarcel Moolenaar case '"': 8123955d011SMarcel Moolenaar case '$': 813956e45f6SSimon J. Gerraty return CondParser_Comparison(par, doEval); 8143955d011SMarcel Moolenaar 8153955d011SMarcel Moolenaar default: 81612904384SSimon J. Gerraty if (CondParser_FuncCallEmpty(par, doEval, &t)) 81712904384SSimon J. Gerraty return t; 818b0c40a00SSimon J. Gerraty if (CondParser_FuncCall(par, doEval, &t)) 819b0c40a00SSimon J. Gerraty return t; 820b0c40a00SSimon J. Gerraty return CondParser_ComparisonOrLeaf(par, doEval); 8213955d011SMarcel Moolenaar } 8223955d011SMarcel Moolenaar } 8233955d011SMarcel Moolenaar 82412904384SSimon J. Gerraty /* Skip the next token if it equals t. */ 82512904384SSimon J. Gerraty static bool 82612904384SSimon J. Gerraty CondParser_Skip(CondParser *par, Token t) 82712904384SSimon J. Gerraty { 82812904384SSimon J. Gerraty Token actual; 82912904384SSimon J. Gerraty 83012904384SSimon J. Gerraty actual = CondParser_Token(par, false); 83112904384SSimon J. Gerraty if (actual == t) 83212904384SSimon J. Gerraty return true; 83312904384SSimon J. Gerraty 83412904384SSimon J. Gerraty assert(par->curr == TOK_NONE); 83512904384SSimon J. Gerraty assert(actual != TOK_NONE); 83612904384SSimon J. Gerraty par->curr = actual; 83712904384SSimon J. Gerraty return false; 83812904384SSimon J. Gerraty } 83912904384SSimon J. Gerraty 84006b9b3e0SSimon J. Gerraty /* 841dba7b0efSSimon J. Gerraty * Term -> '(' Or ')' 842dba7b0efSSimon J. Gerraty * Term -> '!' Term 843dba7b0efSSimon J. Gerraty * Term -> Leaf Operator Leaf 844dba7b0efSSimon J. Gerraty * Term -> Leaf 8453955d011SMarcel Moolenaar */ 846dba7b0efSSimon J. Gerraty static CondResult 847b0c40a00SSimon J. Gerraty CondParser_Term(CondParser *par, bool doEval) 8483955d011SMarcel Moolenaar { 849dba7b0efSSimon J. Gerraty CondResult res; 8503955d011SMarcel Moolenaar Token t; 851c59c3bf3SSimon J. Gerraty bool neg = false; 8523955d011SMarcel Moolenaar 853c59c3bf3SSimon J. Gerraty while ((t = CondParser_Token(par, doEval)) == TOK_NOT) 854c59c3bf3SSimon J. Gerraty neg = !neg; 855c59c3bf3SSimon J. Gerraty 856c59c3bf3SSimon J. Gerraty if (t == TOK_TRUE || t == TOK_FALSE) 857c59c3bf3SSimon J. Gerraty return neg == (t == TOK_FALSE) ? CR_TRUE : CR_FALSE; 8583955d011SMarcel Moolenaar 859dba7b0efSSimon J. Gerraty if (t == TOK_LPAREN) { 860dba7b0efSSimon J. Gerraty res = CondParser_Or(par, doEval); 861dba7b0efSSimon J. Gerraty if (res == CR_ERROR) 862dba7b0efSSimon J. Gerraty return CR_ERROR; 863dba7b0efSSimon J. Gerraty if (CondParser_Token(par, doEval) != TOK_RPAREN) 864dba7b0efSSimon J. Gerraty return CR_ERROR; 865c59c3bf3SSimon J. Gerraty return neg == (res == CR_FALSE) ? CR_TRUE : CR_FALSE; 8663955d011SMarcel Moolenaar } 867dba7b0efSSimon J. Gerraty 868dba7b0efSSimon J. Gerraty return CR_ERROR; 8693955d011SMarcel Moolenaar } 8702c3632d1SSimon J. Gerraty 87106b9b3e0SSimon J. Gerraty /* 87212904384SSimon J. Gerraty * And -> Term ('&&' Term)* 8733955d011SMarcel Moolenaar */ 874dba7b0efSSimon J. Gerraty static CondResult 875b0c40a00SSimon J. Gerraty CondParser_And(CondParser *par, bool doEval) 8763955d011SMarcel Moolenaar { 87712904384SSimon J. Gerraty CondResult res, rhs; 8783955d011SMarcel Moolenaar 87912904384SSimon J. Gerraty res = CR_TRUE; 88012904384SSimon J. Gerraty do { 88112904384SSimon J. Gerraty if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR) 882dba7b0efSSimon J. Gerraty return CR_ERROR; 88312904384SSimon J. Gerraty if (rhs == CR_FALSE) { 88412904384SSimon J. Gerraty res = CR_FALSE; 88512904384SSimon J. Gerraty doEval = false; 8863955d011SMarcel Moolenaar } 88712904384SSimon J. Gerraty } while (CondParser_Skip(par, TOK_AND)); 888dba7b0efSSimon J. Gerraty 889dba7b0efSSimon J. Gerraty return res; 8903955d011SMarcel Moolenaar } 8912c3632d1SSimon J. Gerraty 89206b9b3e0SSimon J. Gerraty /* 89312904384SSimon J. Gerraty * Or -> And ('||' And)* 8943955d011SMarcel Moolenaar */ 895dba7b0efSSimon J. Gerraty static CondResult 896b0c40a00SSimon J. Gerraty CondParser_Or(CondParser *par, bool doEval) 8973955d011SMarcel Moolenaar { 89812904384SSimon J. Gerraty CondResult res, rhs; 8993955d011SMarcel Moolenaar 90012904384SSimon J. Gerraty res = CR_FALSE; 90112904384SSimon J. Gerraty do { 90212904384SSimon J. Gerraty if ((rhs = CondParser_And(par, doEval)) == CR_ERROR) 903dba7b0efSSimon J. Gerraty return CR_ERROR; 90412904384SSimon J. Gerraty if (rhs == CR_TRUE) { 90512904384SSimon J. Gerraty res = CR_TRUE; 90612904384SSimon J. Gerraty doEval = false; 9073955d011SMarcel Moolenaar } 90812904384SSimon J. Gerraty } while (CondParser_Skip(par, TOK_OR)); 909dba7b0efSSimon J. Gerraty 910dba7b0efSSimon J. Gerraty return res; 9113955d011SMarcel Moolenaar } 9123955d011SMarcel Moolenaar 91306b9b3e0SSimon J. Gerraty /* 914d5e0a182SSimon J. Gerraty * Evaluate the condition, including any side effects from the 915956e45f6SSimon J. Gerraty * expressions in the condition. The condition consists of &&, ||, !, 916956e45f6SSimon J. Gerraty * function(arg), comparisons and parenthetical groupings thereof. 9173955d011SMarcel Moolenaar */ 9189f45a3c8SSimon J. Gerraty static CondResult 9199f45a3c8SSimon J. Gerraty CondEvalExpression(const char *cond, bool plain, 92012904384SSimon J. Gerraty bool (*evalBare)(const char *), bool negate, 92112904384SSimon J. Gerraty bool eprint, bool leftUnquotedOK) 9223955d011SMarcel Moolenaar { 923956e45f6SSimon J. Gerraty CondParser par; 9249f45a3c8SSimon J. Gerraty CondResult rval; 9253955d011SMarcel Moolenaar 926e2eeea75SSimon J. Gerraty cpp_skip_hspace(&cond); 9273955d011SMarcel Moolenaar 928dba7b0efSSimon J. Gerraty par.plain = plain; 929dba7b0efSSimon J. Gerraty par.evalBare = evalBare; 930dba7b0efSSimon J. Gerraty par.negateEvalBare = negate; 93112904384SSimon J. Gerraty par.leftUnquotedOK = leftUnquotedOK; 932956e45f6SSimon J. Gerraty par.p = cond; 933956e45f6SSimon J. Gerraty par.curr = TOK_NONE; 934b0c40a00SSimon J. Gerraty par.printedError = false; 9353955d011SMarcel Moolenaar 936c59c3bf3SSimon J. Gerraty DEBUG1(COND, "CondParser_Eval: %s\n", par.p); 937c59c3bf3SSimon J. Gerraty rval = CondParser_Or(&par, true); 938c59c3bf3SSimon J. Gerraty if (par.curr != TOK_EOF) 939c59c3bf3SSimon J. Gerraty rval = CR_ERROR; 9403955d011SMarcel Moolenaar 9419f45a3c8SSimon J. Gerraty if (rval == CR_ERROR && eprint && !par.printedError) 942956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond); 9433955d011SMarcel Moolenaar 9443955d011SMarcel Moolenaar return rval; 9453955d011SMarcel Moolenaar } 9463955d011SMarcel Moolenaar 94706b9b3e0SSimon J. Gerraty /* 94806b9b3e0SSimon J. Gerraty * Evaluate a condition in a :? modifier, such as 94906b9b3e0SSimon J. Gerraty * ${"${VAR}" == value:?yes:no}. 95006b9b3e0SSimon J. Gerraty */ 9519f45a3c8SSimon J. Gerraty CondResult 9529f45a3c8SSimon J. Gerraty Cond_EvalCondition(const char *cond) 953956e45f6SSimon J. Gerraty { 9549f45a3c8SSimon J. Gerraty return CondEvalExpression(cond, true, 95512904384SSimon J. Gerraty FuncDefined, false, false, true); 956956e45f6SSimon J. Gerraty } 9573955d011SMarcel Moolenaar 958b0c40a00SSimon J. Gerraty static bool 95906b9b3e0SSimon J. Gerraty IsEndif(const char *p) 96006b9b3e0SSimon J. Gerraty { 96106b9b3e0SSimon J. Gerraty return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' && 96206b9b3e0SSimon J. Gerraty p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]); 96306b9b3e0SSimon J. Gerraty } 96406b9b3e0SSimon J. Gerraty 965b0c40a00SSimon J. Gerraty static bool 966b0c40a00SSimon J. Gerraty DetermineKindOfConditional(const char **pp, bool *out_plain, 96712904384SSimon J. Gerraty bool (**out_evalBare)(const char *), 968b0c40a00SSimon J. Gerraty bool *out_negate) 969dba7b0efSSimon J. Gerraty { 9709f45a3c8SSimon J. Gerraty const char *p = *pp + 2; 971dba7b0efSSimon J. Gerraty 972b0c40a00SSimon J. Gerraty *out_plain = false; 973dba7b0efSSimon J. Gerraty *out_evalBare = FuncDefined; 9749f45a3c8SSimon J. Gerraty *out_negate = skip_string(&p, "n"); 9759f45a3c8SSimon J. Gerraty 9769f45a3c8SSimon J. Gerraty if (skip_string(&p, "def")) { /* .ifdef and .ifndef */ 9779f45a3c8SSimon J. Gerraty } else if (skip_string(&p, "make")) /* .ifmake and .ifnmake */ 978dba7b0efSSimon J. Gerraty *out_evalBare = FuncMake; 9799f45a3c8SSimon J. Gerraty else if (!*out_negate) /* plain .if */ 980b0c40a00SSimon J. Gerraty *out_plain = true; 9819f45a3c8SSimon J. Gerraty else 9829f45a3c8SSimon J. Gerraty goto unknown_directive; 9839f45a3c8SSimon J. Gerraty if (ch_isalpha(*p)) 9849f45a3c8SSimon J. Gerraty goto unknown_directive; 9859f45a3c8SSimon J. Gerraty 9869f45a3c8SSimon J. Gerraty *pp = p; 9879f45a3c8SSimon J. Gerraty return true; 9889f45a3c8SSimon J. Gerraty 9899f45a3c8SSimon J. Gerraty unknown_directive: 990b0c40a00SSimon J. Gerraty return false; 991dba7b0efSSimon J. Gerraty } 992dba7b0efSSimon J. Gerraty 99306b9b3e0SSimon J. Gerraty /* 99406b9b3e0SSimon J. Gerraty * Evaluate the conditional directive in the line, which is one of: 9953955d011SMarcel Moolenaar * 996e2eeea75SSimon J. Gerraty * .if <cond> 997e2eeea75SSimon J. Gerraty * .ifmake <cond> 998e2eeea75SSimon J. Gerraty * .ifnmake <cond> 999e2eeea75SSimon J. Gerraty * .ifdef <cond> 1000e2eeea75SSimon J. Gerraty * .ifndef <cond> 1001e2eeea75SSimon J. Gerraty * .elif <cond> 1002e2eeea75SSimon J. Gerraty * .elifmake <cond> 1003e2eeea75SSimon J. Gerraty * .elifnmake <cond> 1004e2eeea75SSimon J. Gerraty * .elifdef <cond> 1005e2eeea75SSimon J. Gerraty * .elifndef <cond> 1006e2eeea75SSimon J. Gerraty * .else 1007e2eeea75SSimon J. Gerraty * .endif 1008e2eeea75SSimon J. Gerraty * 1009e2eeea75SSimon J. Gerraty * In these directives, <cond> consists of &&, ||, !, function(arg), 1010e2eeea75SSimon J. Gerraty * comparisons, expressions, bare words, numbers and strings, and 1011e2eeea75SSimon J. Gerraty * parenthetical groupings thereof. 1012956e45f6SSimon J. Gerraty * 1013956e45f6SSimon J. Gerraty * Results: 10149f45a3c8SSimon J. Gerraty * CR_TRUE to continue parsing the lines that follow the 1015b0c40a00SSimon J. Gerraty * conditional (when <cond> evaluates to true) 10169f45a3c8SSimon J. Gerraty * CR_FALSE to skip the lines after the conditional 1017b0c40a00SSimon J. Gerraty * (when <cond> evaluates to false, or when a previous 1018d5e0a182SSimon J. Gerraty * branch was already taken) 10199f45a3c8SSimon J. Gerraty * CR_ERROR if the conditional was not valid, either because of 1020956e45f6SSimon J. Gerraty * a syntax error or because some variable was undefined 1021956e45f6SSimon J. Gerraty * or because the condition could not be evaluated 10223955d011SMarcel Moolenaar */ 10239f45a3c8SSimon J. Gerraty CondResult 102406b9b3e0SSimon J. Gerraty Cond_EvalLine(const char *line) 10253955d011SMarcel Moolenaar { 1026e2eeea75SSimon J. Gerraty typedef enum IfState { 1027e2eeea75SSimon J. Gerraty 1028b0c40a00SSimon J. Gerraty /* None of the previous <cond> evaluated to true. */ 1029e2eeea75SSimon J. Gerraty IFS_INITIAL = 0, 1030e2eeea75SSimon J. Gerraty 10319f45a3c8SSimon J. Gerraty /* 10329f45a3c8SSimon J. Gerraty * The previous <cond> evaluated to true. The lines following 10339f45a3c8SSimon J. Gerraty * this condition are interpreted. 10349f45a3c8SSimon J. Gerraty */ 1035e2eeea75SSimon J. Gerraty IFS_ACTIVE = 1 << 0, 1036e2eeea75SSimon J. Gerraty 1037e2eeea75SSimon J. Gerraty /* The previous directive was an '.else'. */ 1038e2eeea75SSimon J. Gerraty IFS_SEEN_ELSE = 1 << 1, 1039e2eeea75SSimon J. Gerraty 1040b0c40a00SSimon J. Gerraty /* One of the previous <cond> evaluated to true. */ 1041e2eeea75SSimon J. Gerraty IFS_WAS_ACTIVE = 1 << 2 1042e2eeea75SSimon J. Gerraty 1043e2eeea75SSimon J. Gerraty } IfState; 1044e2eeea75SSimon J. Gerraty 1045e2eeea75SSimon J. Gerraty static enum IfState *cond_states = NULL; 1046e2eeea75SSimon J. Gerraty static unsigned int cond_states_cap = 128; 10473955d011SMarcel Moolenaar 1048b0c40a00SSimon J. Gerraty bool plain; 104912904384SSimon J. Gerraty bool (*evalBare)(const char *); 1050b0c40a00SSimon J. Gerraty bool negate; 1051b0c40a00SSimon J. Gerraty bool isElif; 10529f45a3c8SSimon J. Gerraty CondResult res; 1053e2eeea75SSimon J. Gerraty IfState state; 1054e2eeea75SSimon J. Gerraty const char *p = line; 10553955d011SMarcel Moolenaar 1056e2eeea75SSimon J. Gerraty if (cond_states == NULL) { 105706b9b3e0SSimon J. Gerraty cond_states = bmake_malloc( 105806b9b3e0SSimon J. Gerraty cond_states_cap * sizeof *cond_states); 1059e2eeea75SSimon J. Gerraty cond_states[0] = IFS_ACTIVE; 106059a02420SSimon J. Gerraty } 10613955d011SMarcel Moolenaar 1062e2eeea75SSimon J. Gerraty p++; /* skip the leading '.' */ 1063e2eeea75SSimon J. Gerraty cpp_skip_hspace(&p); 1064e2eeea75SSimon J. Gerraty 1065d5e0a182SSimon J. Gerraty if (IsEndif(p)) { 106606b9b3e0SSimon J. Gerraty if (p[5] != '\0') { 106706b9b3e0SSimon J. Gerraty Parse_Error(PARSE_FATAL, 106812904384SSimon J. Gerraty "The .endif directive does not take arguments"); 1069e2eeea75SSimon J. Gerraty } 1070e2eeea75SSimon J. Gerraty 10714fde40d9SSimon J. Gerraty if (cond_depth == CurFile_CondMinDepth()) { 1072956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "if-less endif"); 10739f45a3c8SSimon J. Gerraty return CR_TRUE; 10743955d011SMarcel Moolenaar } 1075e2eeea75SSimon J. Gerraty 10763955d011SMarcel Moolenaar /* Return state for previous conditional */ 10773955d011SMarcel Moolenaar cond_depth--; 1078148ee845SSimon J. Gerraty Parse_GuardEndif(); 1079e2eeea75SSimon J. Gerraty return cond_states[cond_depth] & IFS_ACTIVE 10809f45a3c8SSimon J. Gerraty ? CR_TRUE : CR_FALSE; 10813955d011SMarcel Moolenaar } 10823955d011SMarcel Moolenaar 108306b9b3e0SSimon J. Gerraty /* Parse the name of the directive, such as 'if', 'elif', 'endif'. */ 108406b9b3e0SSimon J. Gerraty if (p[0] == 'e') { 1085d5e0a182SSimon J. Gerraty if (p[1] != 'l') 10869f45a3c8SSimon J. Gerraty return CR_ERROR; 108706b9b3e0SSimon J. Gerraty 10883955d011SMarcel Moolenaar /* Quite likely this is 'else' or 'elif' */ 1089e2eeea75SSimon J. Gerraty p += 2; 10909f45a3c8SSimon J. Gerraty if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) { 109106b9b3e0SSimon J. Gerraty if (p[2] != '\0') 1092e2eeea75SSimon J. Gerraty Parse_Error(PARSE_FATAL, 109306b9b3e0SSimon J. Gerraty "The .else directive " 109412904384SSimon J. Gerraty "does not take arguments"); 1095e2eeea75SSimon J. Gerraty 10964fde40d9SSimon J. Gerraty if (cond_depth == CurFile_CondMinDepth()) { 1097956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "if-less else"); 10989f45a3c8SSimon J. Gerraty return CR_TRUE; 10993955d011SMarcel Moolenaar } 1100148ee845SSimon J. Gerraty Parse_GuardElse(); 11013955d011SMarcel Moolenaar 1102e2eeea75SSimon J. Gerraty state = cond_states[cond_depth]; 1103e2eeea75SSimon J. Gerraty if (state == IFS_INITIAL) { 1104e2eeea75SSimon J. Gerraty state = IFS_ACTIVE | IFS_SEEN_ELSE; 1105e2eeea75SSimon J. Gerraty } else { 1106e2eeea75SSimon J. Gerraty if (state & IFS_SEEN_ELSE) 110706b9b3e0SSimon J. Gerraty Parse_Error(PARSE_WARNING, 110806b9b3e0SSimon J. Gerraty "extra else"); 1109e2eeea75SSimon J. Gerraty state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE; 11103955d011SMarcel Moolenaar } 1111e2eeea75SSimon J. Gerraty cond_states[cond_depth] = state; 1112e2eeea75SSimon J. Gerraty 11139f45a3c8SSimon J. Gerraty return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE; 11143955d011SMarcel Moolenaar } 11153955d011SMarcel Moolenaar /* Assume for now it is an elif */ 1116b0c40a00SSimon J. Gerraty isElif = true; 11173955d011SMarcel Moolenaar } else 1118b0c40a00SSimon J. Gerraty isElif = false; 11193955d011SMarcel Moolenaar 1120d5e0a182SSimon J. Gerraty if (p[0] != 'i' || p[1] != 'f') 1121d5e0a182SSimon J. Gerraty return CR_ERROR; 11223955d011SMarcel Moolenaar 1123dba7b0efSSimon J. Gerraty if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate)) 11249f45a3c8SSimon J. Gerraty return CR_ERROR; 11253955d011SMarcel Moolenaar 11263955d011SMarcel Moolenaar if (isElif) { 11274fde40d9SSimon J. Gerraty if (cond_depth == CurFile_CondMinDepth()) { 1128956e45f6SSimon J. Gerraty Parse_Error(PARSE_FATAL, "if-less elif"); 11299f45a3c8SSimon J. Gerraty return CR_TRUE; 11303955d011SMarcel Moolenaar } 1131148ee845SSimon J. Gerraty Parse_GuardElse(); 1132e2eeea75SSimon J. Gerraty state = cond_states[cond_depth]; 1133e2eeea75SSimon J. Gerraty if (state & IFS_SEEN_ELSE) { 11343955d011SMarcel Moolenaar Parse_Error(PARSE_WARNING, "extra elif"); 113506b9b3e0SSimon J. Gerraty cond_states[cond_depth] = 113606b9b3e0SSimon J. Gerraty IFS_WAS_ACTIVE | IFS_SEEN_ELSE; 11379f45a3c8SSimon J. Gerraty return CR_FALSE; 11383955d011SMarcel Moolenaar } 1139e2eeea75SSimon J. Gerraty if (state != IFS_INITIAL) { 1140e2eeea75SSimon J. Gerraty cond_states[cond_depth] = IFS_WAS_ACTIVE; 11419f45a3c8SSimon J. Gerraty return CR_FALSE; 11423955d011SMarcel Moolenaar } 11433955d011SMarcel Moolenaar } else { 11443955d011SMarcel Moolenaar /* Normal .if */ 1145e2eeea75SSimon J. Gerraty if (cond_depth + 1 >= cond_states_cap) { 114659a02420SSimon J. Gerraty /* 114759a02420SSimon J. Gerraty * This is rare, but not impossible. 114859a02420SSimon J. Gerraty * In meta mode, dirdeps.mk (only runs at level 0) 114959a02420SSimon J. Gerraty * can need more than the default. 115059a02420SSimon J. Gerraty */ 1151e2eeea75SSimon J. Gerraty cond_states_cap += 32; 1152e2eeea75SSimon J. Gerraty cond_states = bmake_realloc(cond_states, 11539f45a3c8SSimon J. Gerraty cond_states_cap * sizeof *cond_states); 11543955d011SMarcel Moolenaar } 1155e2eeea75SSimon J. Gerraty state = cond_states[cond_depth]; 11563955d011SMarcel Moolenaar cond_depth++; 1157e2eeea75SSimon J. Gerraty if (!(state & IFS_ACTIVE)) { 1158e2eeea75SSimon J. Gerraty cond_states[cond_depth] = IFS_WAS_ACTIVE; 11599f45a3c8SSimon J. Gerraty return CR_FALSE; 11603955d011SMarcel Moolenaar } 11613955d011SMarcel Moolenaar } 11623955d011SMarcel Moolenaar 11639f45a3c8SSimon J. Gerraty res = CondEvalExpression(p, plain, evalBare, negate, true, false); 11649f45a3c8SSimon J. Gerraty if (res == CR_ERROR) { 11659f45a3c8SSimon J. Gerraty /* Syntax error, error message already output. */ 11669f45a3c8SSimon J. Gerraty /* Skip everything to the matching '.endif'. */ 11679f45a3c8SSimon J. Gerraty /* An extra '.else' is not detected in this case. */ 1168e2eeea75SSimon J. Gerraty cond_states[cond_depth] = IFS_WAS_ACTIVE; 11699f45a3c8SSimon J. Gerraty return CR_FALSE; 11703955d011SMarcel Moolenaar } 11713955d011SMarcel Moolenaar 11729f45a3c8SSimon J. Gerraty cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL; 11739f45a3c8SSimon J. Gerraty return res; 11743955d011SMarcel Moolenaar } 11753955d011SMarcel Moolenaar 1176148ee845SSimon J. Gerraty static bool 1177148ee845SSimon J. Gerraty ParseVarnameGuard(const char **pp, const char **varname) 1178148ee845SSimon J. Gerraty { 1179148ee845SSimon J. Gerraty const char *p = *pp; 1180148ee845SSimon J. Gerraty 1181148ee845SSimon J. Gerraty if (ch_isalpha(*p) || *p == '_') { 1182148ee845SSimon J. Gerraty while (ch_isalnum(*p) || *p == '_') 1183148ee845SSimon J. Gerraty p++; 1184148ee845SSimon J. Gerraty *varname = *pp; 1185148ee845SSimon J. Gerraty *pp = p; 1186148ee845SSimon J. Gerraty return true; 1187148ee845SSimon J. Gerraty } 1188148ee845SSimon J. Gerraty return false; 1189148ee845SSimon J. Gerraty } 1190148ee845SSimon J. Gerraty 1191148ee845SSimon J. Gerraty /* Extracts the multiple-inclusion guard from a conditional, if any. */ 1192148ee845SSimon J. Gerraty Guard * 1193148ee845SSimon J. Gerraty Cond_ExtractGuard(const char *line) 1194148ee845SSimon J. Gerraty { 1195148ee845SSimon J. Gerraty const char *p, *varname; 1196148ee845SSimon J. Gerraty Substring dir; 1197148ee845SSimon J. Gerraty Guard *guard; 1198148ee845SSimon J. Gerraty 1199148ee845SSimon J. Gerraty p = line + 1; /* skip the '.' */ 1200148ee845SSimon J. Gerraty cpp_skip_hspace(&p); 1201148ee845SSimon J. Gerraty 1202148ee845SSimon J. Gerraty dir.start = p; 1203148ee845SSimon J. Gerraty while (ch_isalpha(*p)) 1204148ee845SSimon J. Gerraty p++; 1205148ee845SSimon J. Gerraty dir.end = p; 1206148ee845SSimon J. Gerraty cpp_skip_hspace(&p); 1207148ee845SSimon J. Gerraty 1208148ee845SSimon J. Gerraty if (Substring_Equals(dir, "if")) { 1209148ee845SSimon J. Gerraty if (skip_string(&p, "!defined(")) { 1210148ee845SSimon J. Gerraty if (ParseVarnameGuard(&p, &varname) 1211148ee845SSimon J. Gerraty && strcmp(p, ")") == 0) 1212148ee845SSimon J. Gerraty goto found_variable; 1213148ee845SSimon J. Gerraty } else if (skip_string(&p, "!target(")) { 1214148ee845SSimon J. Gerraty const char *arg_p = p; 1215148ee845SSimon J. Gerraty free(ParseWord(&p, false)); 1216148ee845SSimon J. Gerraty if (strcmp(p, ")") == 0) { 1217148ee845SSimon J. Gerraty guard = bmake_malloc(sizeof(*guard)); 1218148ee845SSimon J. Gerraty guard->kind = GK_TARGET; 121998875883SSimon J. Gerraty guard->name = ParseWord(&arg_p, true); 1220148ee845SSimon J. Gerraty return guard; 1221148ee845SSimon J. Gerraty } 1222148ee845SSimon J. Gerraty } 1223148ee845SSimon J. Gerraty } else if (Substring_Equals(dir, "ifndef")) { 1224148ee845SSimon J. Gerraty if (ParseVarnameGuard(&p, &varname) && *p == '\0') 1225148ee845SSimon J. Gerraty goto found_variable; 1226148ee845SSimon J. Gerraty } 1227148ee845SSimon J. Gerraty return NULL; 1228148ee845SSimon J. Gerraty 1229148ee845SSimon J. Gerraty found_variable: 1230148ee845SSimon J. Gerraty guard = bmake_malloc(sizeof(*guard)); 123198875883SSimon J. Gerraty guard->kind = GK_VARIABLE; 1232148ee845SSimon J. Gerraty guard->name = bmake_strsedup(varname, p); 1233148ee845SSimon J. Gerraty return guard; 1234148ee845SSimon J. Gerraty } 1235148ee845SSimon J. Gerraty 12363955d011SMarcel Moolenaar void 12374fde40d9SSimon J. Gerraty Cond_EndFile(void) 12383955d011SMarcel Moolenaar { 12394fde40d9SSimon J. Gerraty unsigned int open_conds = cond_depth - CurFile_CondMinDepth(); 12403955d011SMarcel Moolenaar 12414fde40d9SSimon J. Gerraty if (open_conds != 0) { 124206b9b3e0SSimon J. Gerraty Parse_Error(PARSE_FATAL, "%u open conditional%s", 124306b9b3e0SSimon J. Gerraty open_conds, open_conds == 1 ? "" : "s"); 12444fde40d9SSimon J. Gerraty cond_depth = CurFile_CondMinDepth(); 12453955d011SMarcel Moolenaar } 12463955d011SMarcel Moolenaar } 1247