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