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