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