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