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