1 1.378 rillig /* $NetBSD: cond.c,v 1.378 2025/07/06 07:56:16 rillig Exp $ */ 2 1.6 christos 3 1.1 cgd /* 4 1.1 cgd * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 5 1.17 agc * All rights reserved. 6 1.17 agc * 7 1.17 agc * This code is derived from software contributed to Berkeley by 8 1.17 agc * Adam de Boor. 9 1.17 agc * 10 1.17 agc * Redistribution and use in source and binary forms, with or without 11 1.17 agc * modification, are permitted provided that the following conditions 12 1.17 agc * are met: 13 1.17 agc * 1. Redistributions of source code must retain the above copyright 14 1.17 agc * notice, this list of conditions and the following disclaimer. 15 1.17 agc * 2. Redistributions in binary form must reproduce the above copyright 16 1.17 agc * notice, this list of conditions and the following disclaimer in the 17 1.17 agc * documentation and/or other materials provided with the distribution. 18 1.17 agc * 3. Neither the name of the University nor the names of its contributors 19 1.17 agc * may be used to endorse or promote products derived from this software 20 1.17 agc * without specific prior written permission. 21 1.17 agc * 22 1.17 agc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.17 agc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.17 agc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.17 agc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.17 agc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.17 agc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.17 agc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.17 agc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.17 agc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.17 agc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.17 agc * SUCH DAMAGE. 33 1.17 agc */ 34 1.17 agc 35 1.17 agc /* 36 1.1 cgd * Copyright (c) 1988, 1989 by Adam de Boor 37 1.1 cgd * Copyright (c) 1989 by Berkeley Softworks 38 1.1 cgd * All rights reserved. 39 1.1 cgd * 40 1.1 cgd * This code is derived from software contributed to Berkeley by 41 1.1 cgd * Adam de Boor. 42 1.1 cgd * 43 1.1 cgd * Redistribution and use in source and binary forms, with or without 44 1.1 cgd * modification, are permitted provided that the following conditions 45 1.1 cgd * are met: 46 1.1 cgd * 1. Redistributions of source code must retain the above copyright 47 1.1 cgd * notice, this list of conditions and the following disclaimer. 48 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 49 1.1 cgd * notice, this list of conditions and the following disclaimer in the 50 1.1 cgd * documentation and/or other materials provided with the distribution. 51 1.1 cgd * 3. All advertising materials mentioning features or use of this software 52 1.1 cgd * must display the following acknowledgement: 53 1.1 cgd * This product includes software developed by the University of 54 1.1 cgd * California, Berkeley and its contributors. 55 1.1 cgd * 4. Neither the name of the University nor the names of its contributors 56 1.1 cgd * may be used to endorse or promote products derived from this software 57 1.1 cgd * without specific prior written permission. 58 1.1 cgd * 59 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69 1.1 cgd * SUCH DAMAGE. 70 1.1 cgd */ 71 1.1 cgd 72 1.233 rillig /* 73 1.233 rillig * Handling of conditionals in a makefile. 74 1.1 cgd * 75 1.1 cgd * Interface: 76 1.209 rillig * Cond_EvalLine Evaluate the conditional directive, such as 77 1.209 rillig * '.if <cond>', '.elifnmake <cond>', '.else', '.endif'. 78 1.1 cgd * 79 1.142 rillig * Cond_EvalCondition 80 1.376 rillig * Evaluate a condition, either from one of the .if 81 1.376 rillig * directives, or from a ':?then:else' modifier. 82 1.117 rillig * 83 1.357 rillig * Cond_EndFile At the end of reading a makefile, ensure that the 84 1.342 rillig * conditional directives are well-balanced. 85 1.1 cgd */ 86 1.1 cgd 87 1.99 rillig #include <errno.h> 88 1.99 rillig 89 1.99 rillig #include "make.h" 90 1.99 rillig #include "dir.h" 91 1.1 cgd 92 1.144 rillig /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */ 93 1.378 rillig MAKE_RCSID("$NetBSD: cond.c,v 1.378 2025/07/06 07:56:16 rillig Exp $"); 94 1.144 rillig 95 1.1 cgd /* 96 1.324 rillig * Conditional expressions conform to this grammar: 97 1.285 rillig * Or -> And ('||' And)* 98 1.288 rillig * And -> Term ('&&' Term)* 99 1.239 rillig * Term -> Function '(' Argument ')' 100 1.376 rillig * Term -> Leaf ComparisonOp Leaf 101 1.239 rillig * Term -> Leaf 102 1.239 rillig * Term -> '(' Or ')' 103 1.239 rillig * Term -> '!' Term 104 1.239 rillig * Leaf -> "string" 105 1.239 rillig * Leaf -> Number 106 1.239 rillig * Leaf -> VariableExpression 107 1.324 rillig * Leaf -> BareWord 108 1.376 rillig * ComparisonOp -> '==' | '!=' | '>' | '<' | '>=' | '<=' 109 1.1 cgd * 110 1.324 rillig * BareWord is an unquoted string literal, its evaluation depends on the kind 111 1.324 rillig * of '.if' directive. 112 1.1 cgd * 113 1.324 rillig * The tokens are scanned by CondParser_Token, which returns: 114 1.239 rillig * TOK_AND for '&&' 115 1.239 rillig * TOK_OR for '||' 116 1.121 rillig * TOK_NOT for '!' 117 1.121 rillig * TOK_LPAREN for '(' 118 1.121 rillig * TOK_RPAREN for ')' 119 1.239 rillig * 120 1.121 rillig * Other terminal symbols are evaluated using either the default function or 121 1.307 rillig * the function given in the terminal, they return either TOK_TRUE, TOK_FALSE 122 1.307 rillig * or TOK_ERROR. 123 1.1 cgd */ 124 1.164 rillig typedef enum Token { 125 1.241 rillig TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT, 126 1.215 rillig TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR 127 1.1 cgd } Token; 128 1.1 cgd 129 1.251 rillig typedef enum ComparisonOp { 130 1.251 rillig LT, LE, GT, GE, EQ, NE 131 1.251 rillig } ComparisonOp; 132 1.251 rillig 133 1.150 rillig typedef struct CondParser { 134 1.243 rillig 135 1.243 rillig /* 136 1.243 rillig * The plain '.if ${VAR}' evaluates to true if the value of the 137 1.345 rillig * expression has length > 0 and is not numerically zero. The other 138 1.345 rillig * '.if' variants delegate to evalBare instead, for example '.ifdef 139 1.345 rillig * ${VAR}' is equivalent to '.if defined(${VAR})', checking whether 140 1.345 rillig * the variable named by the expression '${VAR}' is defined. 141 1.243 rillig */ 142 1.260 rillig bool plain; 143 1.243 rillig 144 1.243 rillig /* The function to apply on unquoted bare words. */ 145 1.297 rillig bool (*evalBare)(const char *); 146 1.260 rillig bool negateEvalBare; 147 1.243 rillig 148 1.276 rillig /* 149 1.277 rillig * Whether the left-hand side of a comparison may be an unquoted 150 1.276 rillig * string. This is allowed for expressions of the form 151 1.276 rillig * ${condition:?:}, see ApplyModifier_IfElse. Such a condition is 152 1.276 rillig * expanded before it is evaluated, due to ease of implementation. 153 1.276 rillig * This means that at the point where the condition is evaluated, 154 1.276 rillig * make cannot know anymore whether the left-hand side had originally 155 1.355 rillig * been an expression or a plain word. 156 1.276 rillig * 157 1.333 rillig * In conditional directives like '.if', the left-hand side must 158 1.376 rillig * either be a defined expression, a quoted string or a number. 159 1.276 rillig */ 160 1.277 rillig bool leftUnquotedOK; 161 1.276 rillig 162 1.215 rillig const char *p; /* The remaining condition to parse */ 163 1.376 rillig Token curr; /* The push-back token, or TOK_NONE */ 164 1.121 rillig } CondParser; 165 1.1 cgd 166 1.346 rillig static CondResult CondParser_Or(CondParser *, bool); 167 1.1 cgd 168 1.373 rillig unsigned cond_depth = 0; /* current .if nesting level */ 169 1.1 cgd 170 1.268 rillig /* Names for ComparisonOp. */ 171 1.278 rillig static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" }; 172 1.251 rillig 173 1.314 rillig MAKE_INLINE bool 174 1.314 rillig skip_string(const char **pp, const char *str) 175 1.26 christos { 176 1.314 rillig size_t len = strlen(str); 177 1.314 rillig bool ok = strncmp(*pp, str, len) == 0; 178 1.314 rillig if (ok) 179 1.314 rillig *pp += len; 180 1.314 rillig return ok; 181 1.26 christos } 182 1.26 christos 183 1.183 rillig static Token 184 1.260 rillig ToToken(bool cond) 185 1.183 rillig { 186 1.215 rillig return cond ? TOK_TRUE : TOK_FALSE; 187 1.183 rillig } 188 1.183 rillig 189 1.116 rillig static void 190 1.121 rillig CondParser_SkipWhitespace(CondParser *par) 191 1.116 rillig { 192 1.215 rillig cpp_skip_whitespace(&par->p); 193 1.116 rillig } 194 1.116 rillig 195 1.233 rillig /* 196 1.301 rillig * Parse a single word, taking into account balanced parentheses as well as 197 1.301 rillig * embedded expressions. Used for the argument of a built-in function as 198 1.301 rillig * well as for bare words, which are then passed to the default function. 199 1.233 rillig */ 200 1.318 rillig static char * 201 1.319 rillig ParseWord(const char **pp, bool doEval) 202 1.215 rillig { 203 1.215 rillig const char *p = *pp; 204 1.332 rillig Buffer word; 205 1.356 rillig int depth; 206 1.215 rillig 207 1.358 rillig Buf_Init(&word); 208 1.215 rillig 209 1.356 rillig depth = 0; 210 1.215 rillig for (;;) { 211 1.215 rillig char ch = *p; 212 1.215 rillig if (ch == '\0' || ch == ' ' || ch == '\t') 213 1.215 rillig break; 214 1.356 rillig if ((ch == '&' || ch == '|') && depth == 0) 215 1.215 rillig break; 216 1.322 rillig if (ch == '$') { 217 1.371 rillig VarEvalMode emode = doEval ? VARE_EVAL : VARE_PARSE; 218 1.344 rillig FStr nestedVal = Var_Parse(&p, SCOPE_CMDLINE, emode); 219 1.215 rillig /* TODO: handle errors */ 220 1.332 rillig Buf_AddStr(&word, nestedVal.str); 221 1.229 rillig FStr_Done(&nestedVal); 222 1.215 rillig continue; 223 1.215 rillig } 224 1.215 rillig if (ch == '(') 225 1.356 rillig depth++; 226 1.356 rillig else if (ch == ')' && --depth < 0) 227 1.215 rillig break; 228 1.332 rillig Buf_AddByte(&word, ch); 229 1.215 rillig p++; 230 1.215 rillig } 231 1.50 dsl 232 1.319 rillig *pp = p; 233 1.215 rillig 234 1.332 rillig return Buf_DoneData(&word); 235 1.319 rillig } 236 1.319 rillig 237 1.319 rillig /* Parse the function argument, including the surrounding parentheses. */ 238 1.319 rillig static char * 239 1.372 rillig ParseFuncArg(const char **pp, bool doEval, const char *func) 240 1.319 rillig { 241 1.369 rillig const char *p = *pp, *argStart, *argEnd; 242 1.319 rillig char *res; 243 1.319 rillig 244 1.359 rillig p++; /* skip the '(' */ 245 1.319 rillig cpp_skip_hspace(&p); 246 1.369 rillig argStart = p; 247 1.319 rillig res = ParseWord(&p, doEval); 248 1.369 rillig argEnd = p; 249 1.215 rillig cpp_skip_hspace(&p); 250 1.215 rillig 251 1.319 rillig if (*p++ != ')') { 252 1.314 rillig int len = 0; 253 1.314 rillig while (ch_isalpha(func[len])) 254 1.314 rillig len++; 255 1.314 rillig 256 1.253 rillig Parse_Error(PARSE_FATAL, 257 1.374 rillig "Missing \")\" after argument \"%.*s\" for \"%.*s\"", 258 1.369 rillig (int)(argEnd - argStart), argStart, len, func); 259 1.318 rillig free(res); 260 1.318 rillig return NULL; 261 1.215 rillig } 262 1.215 rillig 263 1.215 rillig *pp = p; 264 1.318 rillig return res; 265 1.1 cgd } 266 1.90 rillig 267 1.329 rillig /* See if the given variable is defined. */ 268 1.260 rillig static bool 269 1.324 rillig FuncDefined(const char *var) 270 1.1 cgd { 271 1.326 rillig return Var_Exists(SCOPE_CMDLINE, var); 272 1.1 cgd } 273 1.90 rillig 274 1.328 rillig /* See if a target matching targetPattern is requested to be made. */ 275 1.260 rillig static bool 276 1.328 rillig FuncMake(const char *targetPattern) 277 1.1 cgd { 278 1.215 rillig StringListNode *ln; 279 1.352 rillig bool warned = false; 280 1.166 rillig 281 1.352 rillig for (ln = opts.create.first; ln != NULL; ln = ln->next) { 282 1.352 rillig StrMatchResult res = Str_Match(ln->datum, targetPattern); 283 1.352 rillig if (res.error != NULL && !warned) { 284 1.352 rillig warned = true; 285 1.352 rillig Parse_Error(PARSE_WARNING, 286 1.374 rillig "%s in pattern argument \"%s\" " 287 1.374 rillig "to function \"make\"", 288 1.352 rillig res.error, targetPattern); 289 1.352 rillig } 290 1.352 rillig if (res.matched) 291 1.260 rillig return true; 292 1.352 rillig } 293 1.260 rillig return false; 294 1.1 cgd } 295 1.90 rillig 296 1.90 rillig /* See if the given file exists. */ 297 1.260 rillig static bool 298 1.324 rillig FuncExists(const char *file) 299 1.1 cgd { 300 1.260 rillig bool result; 301 1.215 rillig char *path; 302 1.1 cgd 303 1.324 rillig path = Dir_FindFile(file, &dirSearchPath); 304 1.215 rillig result = path != NULL; 305 1.377 rillig if (result) 306 1.377 rillig DEBUG2(COND, "\"%s\" exists in \"%s\"\n", file, path); 307 1.377 rillig else 308 1.377 rillig DEBUG1(COND, "\"%s\" does not exist\n", file); 309 1.215 rillig free(path); 310 1.215 rillig return result; 311 1.1 cgd } 312 1.90 rillig 313 1.376 rillig /* See if the given node is an actual target. */ 314 1.260 rillig static bool 315 1.324 rillig FuncTarget(const char *node) 316 1.1 cgd { 317 1.324 rillig GNode *gn = Targ_FindNode(node); 318 1.215 rillig return gn != NULL && GNode_IsTarget(gn); 319 1.1 cgd } 320 1.1 cgd 321 1.376 rillig /* See if the given node is an actual target with commands. */ 322 1.260 rillig static bool 323 1.324 rillig FuncCommands(const char *node) 324 1.12 christos { 325 1.324 rillig GNode *gn = Targ_FindNode(node); 326 1.324 rillig return gn != NULL && GNode_IsTarget(gn) && 327 1.324 rillig !Lst_IsEmpty(&gn->commands); 328 1.12 christos } 329 1.90 rillig 330 1.189 rillig /* 331 1.345 rillig * Convert the string to a floating point number. Accepted formats are 332 1.324 rillig * base-10 integer, base-16 integer and finite floating point numbers. 333 1.1 cgd */ 334 1.260 rillig static bool 335 1.189 rillig TryParseNumber(const char *str, double *out_value) 336 1.1 cgd { 337 1.215 rillig char *end; 338 1.215 rillig unsigned long ul_val; 339 1.215 rillig double dbl_val; 340 1.215 rillig 341 1.215 rillig if (str[0] == '\0') { /* XXX: why is an empty string a number? */ 342 1.215 rillig *out_value = 0.0; 343 1.260 rillig return true; 344 1.215 rillig } 345 1.189 rillig 346 1.270 rillig errno = 0; 347 1.215 rillig ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10); 348 1.215 rillig if (*end == '\0' && errno != ERANGE) { 349 1.215 rillig *out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val; 350 1.260 rillig return true; 351 1.215 rillig } 352 1.46 dsl 353 1.215 rillig if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E') 354 1.260 rillig return false; /* skip the expensive strtod call */ 355 1.215 rillig dbl_val = strtod(str, &end); 356 1.215 rillig if (*end != '\0') 357 1.260 rillig return false; 358 1.189 rillig 359 1.215 rillig *out_value = dbl_val; 360 1.260 rillig return true; 361 1.1 cgd } 362 1.46 dsl 363 1.260 rillig static bool 364 1.140 rillig is_separator(char ch) 365 1.140 rillig { 366 1.267 rillig return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' || 367 1.267 rillig ch == '>' || ch == '<' || ch == ')' /* but not '(' */; 368 1.140 rillig } 369 1.140 rillig 370 1.242 rillig /* 371 1.355 rillig * In a quoted or unquoted string literal or a number, parse an 372 1.343 rillig * expression and add its value to the buffer. 373 1.343 rillig * 374 1.343 rillig * Return whether to continue parsing the leaf. 375 1.248 rillig * 376 1.376 rillig * Examples: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX} 377 1.248 rillig */ 378 1.260 rillig static bool 379 1.248 rillig CondParser_StringExpr(CondParser *par, const char *start, 380 1.274 rillig bool doEval, bool quoted, 381 1.376 rillig Buffer *buf, FStr *out_str) 382 1.248 rillig { 383 1.261 rillig VarEvalMode emode; 384 1.308 rillig const char *p; 385 1.376 rillig bool outsideQuotes; 386 1.248 rillig 387 1.365 rillig emode = doEval && quoted ? VARE_EVAL 388 1.371 rillig : doEval ? VARE_EVAL_DEFINED_LOUD 389 1.365 rillig : VARE_PARSE; 390 1.248 rillig 391 1.308 rillig p = par->p; 392 1.376 rillig outsideQuotes = p == start; 393 1.376 rillig *out_str = Var_Parse(&p, SCOPE_CMDLINE, emode); 394 1.376 rillig if (out_str->str == var_Error) { 395 1.376 rillig FStr_Done(out_str); 396 1.376 rillig *out_str = FStr_InitRefer(NULL); 397 1.260 rillig return false; 398 1.248 rillig } 399 1.308 rillig par->p = p; 400 1.248 rillig 401 1.376 rillig if (outsideQuotes && is_separator(par->p[0])) 402 1.260 rillig return false; 403 1.248 rillig 404 1.376 rillig Buf_AddStr(buf, out_str->str); 405 1.376 rillig FStr_Done(out_str); 406 1.376 rillig *out_str = FStr_InitRefer(NULL); /* not finished yet */ 407 1.260 rillig return true; 408 1.248 rillig } 409 1.248 rillig 410 1.248 rillig /* 411 1.355 rillig * Parse a string from an expression or an optionally quoted string, 412 1.307 rillig * on the left-hand and right-hand sides of comparisons. 413 1.23 sjg * 414 1.363 rillig * Return the string without any enclosing quotes, or NULL on error. 415 1.376 rillig * Set out_quoted if the leaf was a quoted string literal. 416 1.23 sjg */ 417 1.363 rillig static FStr 418 1.277 rillig CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK, 419 1.363 rillig bool *out_quoted) 420 1.23 sjg { 421 1.215 rillig Buffer buf; 422 1.228 rillig FStr str; 423 1.260 rillig bool quoted; 424 1.215 rillig const char *start; 425 1.215 rillig 426 1.215 rillig Buf_Init(&buf); 427 1.228 rillig str = FStr_InitRefer(NULL); 428 1.215 rillig *out_quoted = quoted = par->p[0] == '"'; 429 1.215 rillig start = par->p; 430 1.215 rillig if (quoted) 431 1.122 rillig par->p++; 432 1.227 rillig 433 1.228 rillig while (par->p[0] != '\0' && str.str == NULL) { 434 1.215 rillig switch (par->p[0]) { 435 1.215 rillig case '\\': 436 1.215 rillig par->p++; 437 1.215 rillig if (par->p[0] != '\0') { 438 1.215 rillig Buf_AddByte(&buf, par->p[0]); 439 1.215 rillig par->p++; 440 1.378 rillig } else 441 1.378 rillig Parse_Error(PARSE_FATAL, 442 1.378 rillig "Unfinished backslash escape sequence"); 443 1.215 rillig continue; 444 1.215 rillig case '"': 445 1.215 rillig par->p++; 446 1.246 rillig if (quoted) 447 1.378 rillig goto return_buf; 448 1.246 rillig Buf_AddByte(&buf, '"'); 449 1.215 rillig continue; 450 1.215 rillig case ')': /* see is_separator */ 451 1.215 rillig case '!': 452 1.215 rillig case '=': 453 1.215 rillig case '>': 454 1.215 rillig case '<': 455 1.215 rillig case ' ': 456 1.215 rillig case '\t': 457 1.215 rillig if (!quoted) 458 1.327 rillig goto return_buf; 459 1.215 rillig Buf_AddByte(&buf, par->p[0]); 460 1.215 rillig par->p++; 461 1.215 rillig continue; 462 1.215 rillig case '$': 463 1.248 rillig if (!CondParser_StringExpr(par, 464 1.248 rillig start, doEval, quoted, &buf, &str)) 465 1.327 rillig goto return_str; 466 1.215 rillig continue; 467 1.215 rillig default: 468 1.277 rillig if (!unquotedOK && !quoted && *start != '$' && 469 1.215 rillig !ch_isdigit(*start)) { 470 1.228 rillig str = FStr_InitRefer(NULL); 471 1.327 rillig goto return_str; 472 1.215 rillig } 473 1.215 rillig Buf_AddByte(&buf, par->p[0]); 474 1.215 rillig par->p++; 475 1.215 rillig continue; 476 1.30 christos } 477 1.23 sjg } 478 1.378 rillig if (quoted) 479 1.378 rillig Parse_Error(PARSE_FATAL, 480 1.378 rillig "Unfinished string literal \"%s\"", start); 481 1.327 rillig return_buf: 482 1.254 rillig str = FStr_InitOwn(buf.data); 483 1.303 rillig buf.data = NULL; 484 1.327 rillig return_str: 485 1.303 rillig Buf_Done(&buf); 486 1.363 rillig return str; 487 1.23 sjg } 488 1.89 rillig 489 1.233 rillig /* 490 1.233 rillig * Evaluate a "comparison without operator", such as in ".if ${VAR}" or 491 1.233 rillig * ".if 0". 492 1.233 rillig */ 493 1.260 rillig static bool 494 1.345 rillig EvalTruthy(CondParser *par, const char *value, bool quoted) 495 1.131 rillig { 496 1.215 rillig double num; 497 1.131 rillig 498 1.215 rillig if (quoted) 499 1.215 rillig return value[0] != '\0'; 500 1.215 rillig if (TryParseNumber(value, &num)) 501 1.215 rillig return num != 0.0; 502 1.243 rillig if (par->plain) 503 1.215 rillig return value[0] != '\0'; 504 1.325 rillig return par->evalBare(value) != par->negateEvalBare; 505 1.131 rillig } 506 1.131 rillig 507 1.133 rillig /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */ 508 1.260 rillig static bool 509 1.251 rillig EvalCompareNum(double lhs, ComparisonOp op, double rhs) 510 1.130 rillig { 511 1.331 rillig DEBUG3(COND, "Comparing %f %s %f\n", lhs, opname[op], rhs); 512 1.130 rillig 513 1.251 rillig switch (op) { 514 1.251 rillig case LT: 515 1.251 rillig return lhs < rhs; 516 1.251 rillig case LE: 517 1.251 rillig return lhs <= rhs; 518 1.251 rillig case GT: 519 1.251 rillig return lhs > rhs; 520 1.251 rillig case GE: 521 1.251 rillig return lhs >= rhs; 522 1.337 rillig case EQ: 523 1.337 rillig return lhs == rhs; 524 1.337 rillig default: 525 1.251 rillig return lhs != rhs; 526 1.215 rillig } 527 1.130 rillig } 528 1.130 rillig 529 1.133 rillig static Token 530 1.372 rillig EvalCompareStr(const char *lhs, ComparisonOp op, const char *rhs) 531 1.133 rillig { 532 1.251 rillig if (op != EQ && op != NE) { 533 1.252 rillig Parse_Error(PARSE_FATAL, 534 1.374 rillig "Comparison with \"%s\" requires both operands " 535 1.374 rillig "\"%s\" and \"%s\" to be numeric", 536 1.336 rillig opname[op], lhs, rhs); 537 1.215 rillig return TOK_ERROR; 538 1.215 rillig } 539 1.133 rillig 540 1.331 rillig DEBUG3(COND, "Comparing \"%s\" %s \"%s\"\n", lhs, opname[op], rhs); 541 1.251 rillig return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0)); 542 1.133 rillig } 543 1.133 rillig 544 1.133 rillig /* Evaluate a comparison, such as "${VAR} == 12345". */ 545 1.133 rillig static Token 546 1.372 rillig EvalCompare(const char *lhs, bool lhsQuoted, 547 1.260 rillig ComparisonOp op, const char *rhs, bool rhsQuoted) 548 1.133 rillig { 549 1.215 rillig double left, right; 550 1.133 rillig 551 1.215 rillig if (!rhsQuoted && !lhsQuoted) 552 1.215 rillig if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right)) 553 1.251 rillig return ToToken(EvalCompareNum(left, op, right)); 554 1.133 rillig 555 1.372 rillig return EvalCompareStr(lhs, op, rhs); 556 1.133 rillig } 557 1.133 rillig 558 1.260 rillig static bool 559 1.251 rillig CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op) 560 1.251 rillig { 561 1.251 rillig const char *p = par->p; 562 1.251 rillig 563 1.304 rillig if (p[0] == '<' && p[1] == '=') 564 1.304 rillig return par->p += 2, *out_op = LE, true; 565 1.304 rillig if (p[0] == '<') 566 1.304 rillig return par->p += 1, *out_op = LT, true; 567 1.304 rillig if (p[0] == '>' && p[1] == '=') 568 1.304 rillig return par->p += 2, *out_op = GE, true; 569 1.304 rillig if (p[0] == '>') 570 1.304 rillig return par->p += 1, *out_op = GT, true; 571 1.304 rillig if (p[0] == '=' && p[1] == '=') 572 1.304 rillig return par->p += 2, *out_op = EQ, true; 573 1.304 rillig if (p[0] == '!' && p[1] == '=') 574 1.304 rillig return par->p += 2, *out_op = NE, true; 575 1.260 rillig return false; 576 1.251 rillig } 577 1.251 rillig 578 1.233 rillig /* 579 1.233 rillig * Parse a comparison condition such as: 580 1.129 rillig * 581 1.129 rillig * 0 582 1.129 rillig * ${VAR:Mpattern} 583 1.129 rillig * ${VAR} == value 584 1.129 rillig * ${VAR:U0} < 12345 585 1.129 rillig */ 586 1.1 cgd static Token 587 1.260 rillig CondParser_Comparison(CondParser *par, bool doEval) 588 1.44 dsl { 589 1.215 rillig Token t = TOK_ERROR; 590 1.228 rillig FStr lhs, rhs; 591 1.251 rillig ComparisonOp op; 592 1.260 rillig bool lhsQuoted, rhsQuoted; 593 1.215 rillig 594 1.363 rillig lhs = CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhsQuoted); 595 1.228 rillig if (lhs.str == NULL) 596 1.215 rillig goto done_lhs; 597 1.215 rillig 598 1.215 rillig CondParser_SkipWhitespace(par); 599 1.215 rillig 600 1.251 rillig if (!CondParser_ComparisonOp(par, &op)) { 601 1.345 rillig t = ToToken(doEval && EvalTruthy(par, lhs.str, lhsQuoted)); 602 1.215 rillig goto done_lhs; 603 1.215 rillig } 604 1.215 rillig 605 1.215 rillig CondParser_SkipWhitespace(par); 606 1.215 rillig 607 1.215 rillig if (par->p[0] == '\0') { 608 1.252 rillig Parse_Error(PARSE_FATAL, 609 1.374 rillig "Missing right-hand side of operator \"%s\"", opname[op]); 610 1.215 rillig goto done_lhs; 611 1.215 rillig } 612 1.215 rillig 613 1.363 rillig rhs = CondParser_Leaf(par, doEval, true, &rhsQuoted); 614 1.330 rillig t = rhs.str == NULL ? TOK_ERROR 615 1.330 rillig : !doEval ? TOK_FALSE 616 1.372 rillig : EvalCompare(lhs.str, lhsQuoted, op, rhs.str, rhsQuoted); 617 1.330 rillig FStr_Done(&rhs); 618 1.79 sjg 619 1.185 rillig done_lhs: 620 1.228 rillig FStr_Done(&lhs); 621 1.215 rillig return t; 622 1.44 dsl } 623 1.44 dsl 624 1.233 rillig /* 625 1.233 rillig * The argument to empty() is a variable name, optionally followed by 626 1.233 rillig * variable modifiers. 627 1.233 rillig */ 628 1.295 rillig static bool 629 1.295 rillig CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token) 630 1.47 dsl { 631 1.356 rillig const char *p = par->p; 632 1.295 rillig Token tok; 633 1.229 rillig FStr val; 634 1.215 rillig 635 1.356 rillig if (!skip_string(&p, "empty")) 636 1.295 rillig return false; 637 1.295 rillig 638 1.356 rillig cpp_skip_whitespace(&p); 639 1.356 rillig if (*p != '(') 640 1.295 rillig return false; 641 1.295 rillig 642 1.356 rillig p--; /* Make p[1] point to the '('. */ 643 1.365 rillig val = Var_Parse(&p, SCOPE_CMDLINE, doEval ? VARE_EVAL : VARE_PARSE); 644 1.215 rillig /* TODO: handle errors */ 645 1.215 rillig 646 1.293 rillig if (val.str == var_Error) 647 1.293 rillig tok = TOK_ERROR; 648 1.293 rillig else { 649 1.293 rillig cpp_skip_whitespace(&val.str); 650 1.317 rillig tok = ToToken(doEval && val.str[0] == '\0'); 651 1.215 rillig } 652 1.215 rillig 653 1.229 rillig FStr_Done(&val); 654 1.294 rillig *out_token = tok; 655 1.356 rillig par->p = p; 656 1.290 rillig return true; 657 1.290 rillig } 658 1.290 rillig 659 1.334 rillig /* Parse a function call expression, such as 'exists(${file})'. */ 660 1.260 rillig static bool 661 1.263 rillig CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token) 662 1.44 dsl { 663 1.318 rillig char *arg; 664 1.314 rillig const char *p = par->p; 665 1.314 rillig bool (*fn)(const char *); 666 1.314 rillig const char *fn_name = p; 667 1.215 rillig 668 1.314 rillig if (skip_string(&p, "defined")) 669 1.314 rillig fn = FuncDefined; 670 1.314 rillig else if (skip_string(&p, "make")) 671 1.314 rillig fn = FuncMake; 672 1.314 rillig else if (skip_string(&p, "exists")) 673 1.314 rillig fn = FuncExists; 674 1.314 rillig else if (skip_string(&p, "target")) 675 1.314 rillig fn = FuncTarget; 676 1.314 rillig else if (skip_string(&p, "commands")) 677 1.314 rillig fn = FuncCommands; 678 1.314 rillig else 679 1.314 rillig return false; 680 1.272 rillig 681 1.314 rillig cpp_skip_whitespace(&p); 682 1.314 rillig if (*p != '(') 683 1.272 rillig return false; 684 1.215 rillig 685 1.372 rillig arg = ParseFuncArg(&p, doEval, fn_name); 686 1.319 rillig *out_token = ToToken(doEval && 687 1.319 rillig arg != NULL && arg[0] != '\0' && fn(arg)); 688 1.318 rillig free(arg); 689 1.200 rillig 690 1.314 rillig par->p = p; 691 1.272 rillig return true; 692 1.196 rillig } 693 1.196 rillig 694 1.233 rillig /* 695 1.302 rillig * Parse a comparison that neither starts with '"' nor '$', such as the 696 1.302 rillig * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without 697 1.355 rillig * operator, which is a number, an expression or a string literal. 698 1.302 rillig * 699 1.302 rillig * TODO: Can this be merged into CondParser_Comparison? 700 1.233 rillig */ 701 1.196 rillig static Token 702 1.266 rillig CondParser_ComparisonOrLeaf(CondParser *par, bool doEval) 703 1.196 rillig { 704 1.215 rillig Token t; 705 1.323 rillig char *arg; 706 1.356 rillig const char *p; 707 1.215 rillig 708 1.356 rillig p = par->p; 709 1.356 rillig if (ch_isdigit(p[0]) || p[0] == '-' || p[0] == '+') 710 1.215 rillig return CondParser_Comparison(par, doEval); 711 1.196 rillig 712 1.215 rillig /* 713 1.359 rillig * Most likely we have a bare word to apply the default function to. 714 1.357 rillig * However, ".if a == b" gets here when the "a" is unquoted and 715 1.357 rillig * doesn't start with a '$'. This surprises people. 716 1.215 rillig * If what follows the function argument is a '=' or '!' then the 717 1.215 rillig * syntax would be invalid if we did "defined(a)" - so instead treat 718 1.215 rillig * as an expression. 719 1.215 rillig */ 720 1.266 rillig /* 721 1.355 rillig * XXX: In edge cases, an expression may be evaluated twice, 722 1.321 rillig * see cond-token-plain.mk, keyword 'twice'. 723 1.266 rillig */ 724 1.356 rillig arg = ParseWord(&p, doEval); 725 1.319 rillig assert(arg[0] != '\0'); 726 1.369 rillig cpp_skip_hspace(&p); 727 1.318 rillig 728 1.364 rillig if (*p == '=' || *p == '!' || *p == '<' || *p == '>') { 729 1.364 rillig free(arg); 730 1.215 rillig return CondParser_Comparison(par, doEval); 731 1.364 rillig } 732 1.356 rillig par->p = p; 733 1.215 rillig 734 1.215 rillig /* 735 1.215 rillig * Evaluate the argument using the default function. 736 1.215 rillig * This path always treats .if as .ifdef. To get here, the character 737 1.215 rillig * after .if must have been taken literally, so the argument cannot 738 1.357 rillig * be empty - even if it contained an expression. 739 1.215 rillig */ 740 1.325 rillig t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare); 741 1.215 rillig free(arg); 742 1.44 dsl return t; 743 1.44 dsl } 744 1.44 dsl 745 1.121 rillig /* Return the next token or comparison result from the parser. */ 746 1.44 dsl static Token 747 1.260 rillig CondParser_Token(CondParser *par, bool doEval) 748 1.1 cgd { 749 1.215 rillig Token t; 750 1.215 rillig 751 1.215 rillig t = par->curr; 752 1.215 rillig if (t != TOK_NONE) { 753 1.215 rillig par->curr = TOK_NONE; 754 1.215 rillig return t; 755 1.215 rillig } 756 1.215 rillig 757 1.215 rillig cpp_skip_hspace(&par->p); 758 1.215 rillig 759 1.215 rillig switch (par->p[0]) { 760 1.215 rillig 761 1.215 rillig case '(': 762 1.215 rillig par->p++; 763 1.215 rillig return TOK_LPAREN; 764 1.215 rillig 765 1.215 rillig case ')': 766 1.215 rillig par->p++; 767 1.215 rillig return TOK_RPAREN; 768 1.215 rillig 769 1.215 rillig case '|': 770 1.215 rillig par->p++; 771 1.215 rillig if (par->p[0] == '|') 772 1.215 rillig par->p++; 773 1.366 rillig else { 774 1.374 rillig Parse_Error(PARSE_FATAL, "Unknown operator \"|\""); 775 1.215 rillig return TOK_ERROR; 776 1.215 rillig } 777 1.215 rillig return TOK_OR; 778 1.215 rillig 779 1.215 rillig case '&': 780 1.215 rillig par->p++; 781 1.215 rillig if (par->p[0] == '&') 782 1.215 rillig par->p++; 783 1.366 rillig else { 784 1.374 rillig Parse_Error(PARSE_FATAL, "Unknown operator \"&\""); 785 1.215 rillig return TOK_ERROR; 786 1.215 rillig } 787 1.215 rillig return TOK_AND; 788 1.47 dsl 789 1.215 rillig case '!': 790 1.215 rillig par->p++; 791 1.215 rillig return TOK_NOT; 792 1.47 dsl 793 1.215 rillig case '#': /* XXX: see unit-tests/cond-token-plain.mk */ 794 1.215 rillig case '\n': /* XXX: why should this end the condition? */ 795 1.215 rillig /* Probably obsolete now, from 1993-03-21. */ 796 1.215 rillig case '\0': 797 1.215 rillig return TOK_EOF; 798 1.47 dsl 799 1.215 rillig case '"': 800 1.215 rillig case '$': 801 1.215 rillig return CondParser_Comparison(par, doEval); 802 1.47 dsl 803 1.215 rillig default: 804 1.290 rillig if (CondParser_FuncCallEmpty(par, doEval, &t)) 805 1.290 rillig return t; 806 1.264 rillig if (CondParser_FuncCall(par, doEval, &t)) 807 1.264 rillig return t; 808 1.266 rillig return CondParser_ComparisonOrLeaf(par, doEval); 809 1.215 rillig } 810 1.1 cgd } 811 1.47 dsl 812 1.289 rillig /* Skip the next token if it equals t. */ 813 1.289 rillig static bool 814 1.289 rillig CondParser_Skip(CondParser *par, Token t) 815 1.289 rillig { 816 1.289 rillig Token actual; 817 1.289 rillig 818 1.289 rillig actual = CondParser_Token(par, false); 819 1.289 rillig if (actual == t) 820 1.289 rillig return true; 821 1.289 rillig 822 1.289 rillig assert(par->curr == TOK_NONE); 823 1.289 rillig assert(actual != TOK_NONE); 824 1.289 rillig par->curr = actual; 825 1.289 rillig return false; 826 1.289 rillig } 827 1.289 rillig 828 1.233 rillig /* 829 1.239 rillig * Term -> '(' Or ')' 830 1.239 rillig * Term -> '!' Term 831 1.376 rillig * Term -> Leaf ComparisonOp Leaf 832 1.239 rillig * Term -> Leaf 833 1.1 cgd */ 834 1.241 rillig static CondResult 835 1.260 rillig CondParser_Term(CondParser *par, bool doEval) 836 1.1 cgd { 837 1.241 rillig CondResult res; 838 1.215 rillig Token t; 839 1.361 rillig bool neg = false; 840 1.1 cgd 841 1.361 rillig while ((t = CondParser_Token(par, doEval)) == TOK_NOT) 842 1.361 rillig neg = !neg; 843 1.361 rillig 844 1.361 rillig if (t == TOK_TRUE || t == TOK_FALSE) 845 1.361 rillig return neg == (t == TOK_FALSE) ? CR_TRUE : CR_FALSE; 846 1.1 cgd 847 1.237 rillig if (t == TOK_LPAREN) { 848 1.241 rillig res = CondParser_Or(par, doEval); 849 1.241 rillig if (res == CR_ERROR) 850 1.241 rillig return CR_ERROR; 851 1.239 rillig if (CondParser_Token(par, doEval) != TOK_RPAREN) 852 1.241 rillig return CR_ERROR; 853 1.361 rillig return neg == (res == CR_FALSE) ? CR_TRUE : CR_FALSE; 854 1.239 rillig } 855 1.236 rillig 856 1.241 rillig return CR_ERROR; 857 1.1 cgd } 858 1.90 rillig 859 1.233 rillig /* 860 1.288 rillig * And -> Term ('&&' Term)* 861 1.1 cgd */ 862 1.241 rillig static CondResult 863 1.260 rillig CondParser_And(CondParser *par, bool doEval) 864 1.1 cgd { 865 1.288 rillig CondResult res, rhs; 866 1.1 cgd 867 1.288 rillig res = CR_TRUE; 868 1.288 rillig do { 869 1.288 rillig if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR) 870 1.241 rillig return CR_ERROR; 871 1.288 rillig if (rhs == CR_FALSE) { 872 1.288 rillig res = CR_FALSE; 873 1.288 rillig doEval = false; 874 1.288 rillig } 875 1.289 rillig } while (CondParser_Skip(par, TOK_AND)); 876 1.239 rillig 877 1.239 rillig return res; 878 1.1 cgd } 879 1.90 rillig 880 1.233 rillig /* 881 1.285 rillig * Or -> And ('||' And)* 882 1.1 cgd */ 883 1.241 rillig static CondResult 884 1.260 rillig CondParser_Or(CondParser *par, bool doEval) 885 1.1 cgd { 886 1.285 rillig CondResult res, rhs; 887 1.1 cgd 888 1.285 rillig res = CR_FALSE; 889 1.285 rillig do { 890 1.285 rillig if ((rhs = CondParser_And(par, doEval)) == CR_ERROR) 891 1.241 rillig return CR_ERROR; 892 1.285 rillig if (rhs == CR_TRUE) { 893 1.285 rillig res = CR_TRUE; 894 1.285 rillig doEval = false; 895 1.285 rillig } 896 1.289 rillig } while (CondParser_Skip(par, TOK_OR)); 897 1.239 rillig 898 1.239 rillig return res; 899 1.1 cgd } 900 1.10 christos 901 1.233 rillig /* 902 1.355 rillig * Evaluate the condition, including any side effects from the 903 1.128 rillig * expressions in the condition. The condition consists of &&, ||, !, 904 1.128 rillig * function(arg), comparisons and parenthetical groupings thereof. 905 1.10 christos */ 906 1.312 rillig static CondResult 907 1.310 rillig CondEvalExpression(const char *cond, bool plain, 908 1.297 rillig bool (*evalBare)(const char *), bool negate, 909 1.277 rillig bool eprint, bool leftUnquotedOK) 910 1.10 christos { 911 1.215 rillig CondParser par; 912 1.312 rillig CondResult rval; 913 1.370 rillig int parseErrorsBefore = parseErrors; 914 1.10 christos 915 1.215 rillig cpp_skip_hspace(&cond); 916 1.10 christos 917 1.243 rillig par.plain = plain; 918 1.243 rillig par.evalBare = evalBare; 919 1.243 rillig par.negateEvalBare = negate; 920 1.277 rillig par.leftUnquotedOK = leftUnquotedOK; 921 1.215 rillig par.p = cond; 922 1.215 rillig par.curr = TOK_NONE; 923 1.10 christos 924 1.360 rillig DEBUG1(COND, "CondParser_Eval: %s\n", par.p); 925 1.360 rillig rval = CondParser_Or(&par, true); 926 1.360 rillig if (par.curr != TOK_EOF) 927 1.360 rillig rval = CR_ERROR; 928 1.56 dsl 929 1.375 rillig if (parseErrors != parseErrorsBefore) 930 1.375 rillig rval = CR_ERROR; 931 1.375 rillig else if (rval == CR_ERROR && eprint) 932 1.374 rillig Parse_Error(PARSE_FATAL, "Malformed conditional \"%s\"", cond); 933 1.56 dsl 934 1.215 rillig return rval; 935 1.56 dsl } 936 1.56 dsl 937 1.233 rillig /* 938 1.233 rillig * Evaluate a condition in a :? modifier, such as 939 1.233 rillig * ${"${VAR}" == value:?yes:no}. 940 1.233 rillig */ 941 1.312 rillig CondResult 942 1.310 rillig Cond_EvalCondition(const char *cond) 943 1.142 rillig { 944 1.310 rillig return CondEvalExpression(cond, true, 945 1.277 rillig FuncDefined, false, false, true); 946 1.142 rillig } 947 1.90 rillig 948 1.260 rillig static bool 949 1.224 rillig IsEndif(const char *p) 950 1.224 rillig { 951 1.224 rillig return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' && 952 1.224 rillig p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]); 953 1.224 rillig } 954 1.224 rillig 955 1.260 rillig static bool 956 1.260 rillig DetermineKindOfConditional(const char **pp, bool *out_plain, 957 1.297 rillig bool (**out_evalBare)(const char *), 958 1.260 rillig bool *out_negate) 959 1.244 rillig { 960 1.314 rillig const char *p = *pp + 2; 961 1.244 rillig 962 1.260 rillig *out_plain = false; 963 1.244 rillig *out_evalBare = FuncDefined; 964 1.314 rillig *out_negate = skip_string(&p, "n"); 965 1.314 rillig 966 1.314 rillig if (skip_string(&p, "def")) { /* .ifdef and .ifndef */ 967 1.314 rillig } else if (skip_string(&p, "make")) /* .ifmake and .ifnmake */ 968 1.244 rillig *out_evalBare = FuncMake; 969 1.314 rillig else if (!*out_negate) /* plain .if */ 970 1.260 rillig *out_plain = true; 971 1.314 rillig else 972 1.314 rillig goto unknown_directive; 973 1.314 rillig if (ch_isalpha(*p)) 974 1.314 rillig goto unknown_directive; 975 1.244 rillig 976 1.244 rillig *pp = p; 977 1.260 rillig return true; 978 1.314 rillig 979 1.314 rillig unknown_directive: 980 1.314 rillig return false; 981 1.244 rillig } 982 1.244 rillig 983 1.233 rillig /* 984 1.233 rillig * Evaluate the conditional directive in the line, which is one of: 985 1.207 rillig * 986 1.206 rillig * .if <cond> 987 1.206 rillig * .ifmake <cond> 988 1.206 rillig * .ifnmake <cond> 989 1.206 rillig * .ifdef <cond> 990 1.206 rillig * .ifndef <cond> 991 1.206 rillig * .elif <cond> 992 1.206 rillig * .elifmake <cond> 993 1.206 rillig * .elifnmake <cond> 994 1.206 rillig * .elifdef <cond> 995 1.206 rillig * .elifndef <cond> 996 1.206 rillig * .else 997 1.206 rillig * .endif 998 1.207 rillig * 999 1.207 rillig * In these directives, <cond> consists of &&, ||, !, function(arg), 1000 1.207 rillig * comparisons, expressions, bare words, numbers and strings, and 1001 1.207 rillig * parenthetical groupings thereof. 1002 1.1 cgd * 1003 1.108 rillig * Results: 1004 1.311 rillig * CR_TRUE to continue parsing the lines that follow the 1005 1.260 rillig * conditional (when <cond> evaluates to true) 1006 1.311 rillig * CR_FALSE to skip the lines after the conditional 1007 1.260 rillig * (when <cond> evaluates to false, or when a previous 1008 1.357 rillig * branch was already taken) 1009 1.311 rillig * CR_ERROR if the conditional was not valid, either because of 1010 1.108 rillig * a syntax error or because some variable was undefined 1011 1.108 rillig * or because the condition could not be evaluated 1012 1.1 cgd */ 1013 1.312 rillig CondResult 1014 1.222 rillig Cond_EvalLine(const char *line) 1015 1.1 cgd { 1016 1.215 rillig typedef enum IfState { 1017 1.215 rillig 1018 1.260 rillig /* None of the previous <cond> evaluated to true. */ 1019 1.215 rillig IFS_INITIAL = 0, 1020 1.215 rillig 1021 1.306 rillig /* 1022 1.306 rillig * The previous <cond> evaluated to true. The lines following 1023 1.306 rillig * this condition are interpreted. 1024 1.306 rillig */ 1025 1.215 rillig IFS_ACTIVE = 1 << 0, 1026 1.215 rillig 1027 1.215 rillig /* The previous directive was an '.else'. */ 1028 1.215 rillig IFS_SEEN_ELSE = 1 << 1, 1029 1.215 rillig 1030 1.260 rillig /* One of the previous <cond> evaluated to true. */ 1031 1.215 rillig IFS_WAS_ACTIVE = 1 << 2 1032 1.207 rillig 1033 1.215 rillig } IfState; 1034 1.213 rillig 1035 1.215 rillig static enum IfState *cond_states = NULL; 1036 1.373 rillig static unsigned cond_states_cap = 128; 1037 1.215 rillig 1038 1.260 rillig bool plain; 1039 1.297 rillig bool (*evalBare)(const char *); 1040 1.260 rillig bool negate; 1041 1.260 rillig bool isElif; 1042 1.310 rillig CondResult res; 1043 1.215 rillig IfState state; 1044 1.215 rillig const char *p = line; 1045 1.215 rillig 1046 1.215 rillig if (cond_states == NULL) { 1047 1.215 rillig cond_states = bmake_malloc( 1048 1.215 rillig cond_states_cap * sizeof *cond_states); 1049 1.215 rillig cond_states[0] = IFS_ACTIVE; 1050 1.215 rillig } 1051 1.215 rillig 1052 1.215 rillig p++; /* skip the leading '.' */ 1053 1.215 rillig cpp_skip_hspace(&p); 1054 1.215 rillig 1055 1.357 rillig if (IsEndif(p)) { 1056 1.225 rillig if (p[5] != '\0') { 1057 1.225 rillig Parse_Error(PARSE_FATAL, 1058 1.282 rillig "The .endif directive does not take arguments"); 1059 1.225 rillig } 1060 1.224 rillig 1061 1.342 rillig if (cond_depth == CurFile_CondMinDepth()) { 1062 1.224 rillig Parse_Error(PARSE_FATAL, "if-less endif"); 1063 1.311 rillig return CR_TRUE; 1064 1.224 rillig } 1065 1.224 rillig 1066 1.224 rillig /* Return state for previous conditional */ 1067 1.224 rillig cond_depth--; 1068 1.347 rillig Parse_GuardEndif(); 1069 1.224 rillig return cond_states[cond_depth] & IFS_ACTIVE 1070 1.311 rillig ? CR_TRUE : CR_FALSE; 1071 1.224 rillig } 1072 1.224 rillig 1073 1.215 rillig /* Parse the name of the directive, such as 'if', 'elif', 'endif'. */ 1074 1.215 rillig if (p[0] == 'e') { 1075 1.357 rillig if (p[1] != 'l') 1076 1.311 rillig return CR_ERROR; 1077 1.211 rillig 1078 1.215 rillig /* Quite likely this is 'else' or 'elif' */ 1079 1.215 rillig p += 2; 1080 1.314 rillig if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) { 1081 1.226 rillig if (p[2] != '\0') 1082 1.215 rillig Parse_Error(PARSE_FATAL, 1083 1.282 rillig "The .else directive " 1084 1.282 rillig "does not take arguments"); 1085 1.215 rillig 1086 1.342 rillig if (cond_depth == CurFile_CondMinDepth()) { 1087 1.215 rillig Parse_Error(PARSE_FATAL, "if-less else"); 1088 1.311 rillig return CR_TRUE; 1089 1.215 rillig } 1090 1.347 rillig Parse_GuardElse(); 1091 1.215 rillig 1092 1.215 rillig state = cond_states[cond_depth]; 1093 1.215 rillig if (state == IFS_INITIAL) { 1094 1.215 rillig state = IFS_ACTIVE | IFS_SEEN_ELSE; 1095 1.215 rillig } else { 1096 1.215 rillig if (state & IFS_SEEN_ELSE) 1097 1.215 rillig Parse_Error(PARSE_WARNING, 1098 1.305 rillig "extra else"); 1099 1.215 rillig state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE; 1100 1.215 rillig } 1101 1.215 rillig cond_states[cond_depth] = state; 1102 1.211 rillig 1103 1.311 rillig return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE; 1104 1.215 rillig } 1105 1.215 rillig /* Assume for now it is an elif */ 1106 1.260 rillig isElif = true; 1107 1.215 rillig } else 1108 1.260 rillig isElif = false; 1109 1.211 rillig 1110 1.357 rillig if (p[0] != 'i' || p[1] != 'f') 1111 1.357 rillig return CR_ERROR; 1112 1.36 dsl 1113 1.244 rillig if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate)) 1114 1.311 rillig return CR_ERROR; 1115 1.206 rillig 1116 1.215 rillig if (isElif) { 1117 1.342 rillig if (cond_depth == CurFile_CondMinDepth()) { 1118 1.215 rillig Parse_Error(PARSE_FATAL, "if-less elif"); 1119 1.311 rillig return CR_TRUE; 1120 1.215 rillig } 1121 1.347 rillig Parse_GuardElse(); 1122 1.215 rillig state = cond_states[cond_depth]; 1123 1.215 rillig if (state & IFS_SEEN_ELSE) { 1124 1.215 rillig Parse_Error(PARSE_WARNING, "extra elif"); 1125 1.215 rillig cond_states[cond_depth] = 1126 1.215 rillig IFS_WAS_ACTIVE | IFS_SEEN_ELSE; 1127 1.311 rillig return CR_FALSE; 1128 1.215 rillig } 1129 1.215 rillig if (state != IFS_INITIAL) { 1130 1.215 rillig cond_states[cond_depth] = IFS_WAS_ACTIVE; 1131 1.311 rillig return CR_FALSE; 1132 1.215 rillig } 1133 1.215 rillig } else { 1134 1.215 rillig /* Normal .if */ 1135 1.215 rillig if (cond_depth + 1 >= cond_states_cap) { 1136 1.215 rillig /* 1137 1.215 rillig * This is rare, but not impossible. 1138 1.215 rillig * In meta mode, dirdeps.mk (only runs at level 0) 1139 1.215 rillig * can need more than the default. 1140 1.215 rillig */ 1141 1.215 rillig cond_states_cap += 32; 1142 1.215 rillig cond_states = bmake_realloc(cond_states, 1143 1.305 rillig cond_states_cap * sizeof *cond_states); 1144 1.215 rillig } 1145 1.215 rillig state = cond_states[cond_depth]; 1146 1.215 rillig cond_depth++; 1147 1.215 rillig if (!(state & IFS_ACTIVE)) { 1148 1.215 rillig cond_states[cond_depth] = IFS_WAS_ACTIVE; 1149 1.311 rillig return CR_FALSE; 1150 1.215 rillig } 1151 1.215 rillig } 1152 1.215 rillig 1153 1.310 rillig res = CondEvalExpression(p, plain, evalBare, negate, true, false); 1154 1.311 rillig if (res == CR_ERROR) { 1155 1.324 rillig /* Syntax error, error message already output. */ 1156 1.324 rillig /* Skip everything to the matching '.endif'. */ 1157 1.324 rillig /* An extra '.else' is not detected in this case. */ 1158 1.215 rillig cond_states[cond_depth] = IFS_WAS_ACTIVE; 1159 1.311 rillig return CR_FALSE; 1160 1.215 rillig } 1161 1.215 rillig 1162 1.313 rillig cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL; 1163 1.313 rillig return res; 1164 1.1 cgd } 1165 1.10 christos 1166 1.347 rillig static bool 1167 1.348 rillig ParseVarnameGuard(const char **pp, const char **varname) 1168 1.347 rillig { 1169 1.347 rillig const char *p = *pp; 1170 1.347 rillig 1171 1.347 rillig if (ch_isalpha(*p) || *p == '_') { 1172 1.347 rillig while (ch_isalnum(*p) || *p == '_') 1173 1.347 rillig p++; 1174 1.348 rillig *varname = *pp; 1175 1.347 rillig *pp = p; 1176 1.347 rillig return true; 1177 1.347 rillig } 1178 1.347 rillig return false; 1179 1.347 rillig } 1180 1.347 rillig 1181 1.350 rillig /* Extracts the multiple-inclusion guard from a conditional, if any. */ 1182 1.350 rillig Guard * 1183 1.347 rillig Cond_ExtractGuard(const char *line) 1184 1.347 rillig { 1185 1.353 rillig const char *p, *varname; 1186 1.348 rillig Substring dir; 1187 1.350 rillig Guard *guard; 1188 1.347 rillig 1189 1.349 rillig p = line + 1; /* skip the '.' */ 1190 1.347 rillig cpp_skip_hspace(&p); 1191 1.347 rillig 1192 1.348 rillig dir.start = p; 1193 1.347 rillig while (ch_isalpha(*p)) 1194 1.347 rillig p++; 1195 1.348 rillig dir.end = p; 1196 1.347 rillig cpp_skip_hspace(&p); 1197 1.347 rillig 1198 1.350 rillig if (Substring_Equals(dir, "if")) { 1199 1.350 rillig if (skip_string(&p, "!defined(")) { 1200 1.353 rillig if (ParseVarnameGuard(&p, &varname) 1201 1.350 rillig && strcmp(p, ")") == 0) 1202 1.350 rillig goto found_variable; 1203 1.350 rillig } else if (skip_string(&p, "!target(")) { 1204 1.353 rillig const char *arg_p = p; 1205 1.351 sjg free(ParseWord(&p, false)); 1206 1.351 sjg if (strcmp(p, ")") == 0) { 1207 1.351 sjg guard = bmake_malloc(sizeof(*guard)); 1208 1.351 sjg guard->kind = GK_TARGET; 1209 1.354 rillig guard->name = ParseWord(&arg_p, true); 1210 1.351 sjg return guard; 1211 1.351 sjg } 1212 1.350 rillig } 1213 1.350 rillig } else if (Substring_Equals(dir, "ifndef")) { 1214 1.353 rillig if (ParseVarnameGuard(&p, &varname) && *p == '\0') 1215 1.350 rillig goto found_variable; 1216 1.350 rillig } 1217 1.347 rillig return NULL; 1218 1.350 rillig 1219 1.350 rillig found_variable: 1220 1.350 rillig guard = bmake_malloc(sizeof(*guard)); 1221 1.354 rillig guard->kind = GK_VARIABLE; 1222 1.353 rillig guard->name = bmake_strsedup(varname, p); 1223 1.350 rillig return guard; 1224 1.347 rillig } 1225 1.347 rillig 1226 1.1 cgd void 1227 1.342 rillig Cond_EndFile(void) 1228 1.1 cgd { 1229 1.373 rillig unsigned open_conds = cond_depth - CurFile_CondMinDepth(); 1230 1.37 dsl 1231 1.339 rillig if (open_conds != 0) { 1232 1.215 rillig Parse_Error(PARSE_FATAL, "%u open conditional%s", 1233 1.305 rillig open_conds, open_conds == 1 ? "" : "s"); 1234 1.342 rillig cond_depth = CurFile_CondMinDepth(); 1235 1.215 rillig } 1236 1.335 sjg } 1237