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