1 /* $NetBSD: cond.c,v 1.173 2020/10/30 20:30:44 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 /* Handling of conditionals in a makefile. 73 * 74 * Interface: 75 * Cond_EvalLine Evaluate the conditional. 76 * 77 * Cond_EvalCondition 78 * Evaluate the conditional, which is either the argument 79 * of one of the .if directives or the condition in a 80 * ':?then:else' variable modifier. 81 * 82 * Cond_save_depth 83 * Cond_restore_depth 84 * Save and restore the nesting of the conditions, at 85 * the start and end of including another makefile, to 86 * ensure that in each makefile the conditional 87 * directives are well-balanced. 88 */ 89 90 #include <errno.h> 91 92 #include "make.h" 93 #include "dir.h" 94 95 /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */ 96 MAKE_RCSID("$NetBSD: cond.c,v 1.173 2020/10/30 20:30:44 rillig Exp $"); 97 98 /* 99 * The parsing of conditional expressions is based on this grammar: 100 * E -> F || E 101 * E -> F 102 * F -> T && F 103 * F -> T 104 * T -> defined(variable) 105 * T -> make(target) 106 * T -> exists(file) 107 * T -> empty(varspec) 108 * T -> target(name) 109 * T -> commands(name) 110 * T -> symbol 111 * T -> $(varspec) op value 112 * T -> $(varspec) == "string" 113 * T -> $(varspec) != "string" 114 * T -> "string" 115 * T -> ( E ) 116 * T -> ! T 117 * op -> == | != | > | < | >= | <= 118 * 119 * 'symbol' is some other symbol to which the default function is applied. 120 * 121 * The tokens are scanned by CondToken, which returns: 122 * TOK_AND for '&' or '&&' 123 * TOK_OR for '|' or '||' 124 * TOK_NOT for '!' 125 * TOK_LPAREN for '(' 126 * TOK_RPAREN for ')' 127 * Other terminal symbols are evaluated using either the default function or 128 * the function given in the terminal, they return either TOK_TRUE or 129 * TOK_FALSE. 130 * 131 * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons. 132 * 133 * All non-terminal functions (CondParser_Expr, CondParser_Factor and 134 * CondParser_Term) return either TOK_FALSE, TOK_TRUE, or TOK_ERROR on error. 135 */ 136 typedef enum Token { 137 TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT, 138 TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR 139 } Token; 140 141 typedef struct CondParser { 142 const struct If *if_info; /* Info for current statement */ 143 const char *p; /* The remaining condition to parse */ 144 Token curr; /* Single push-back token used in parsing */ 145 146 /* Whether an error message has already been printed for this condition. 147 * The first available error message is usually the most specific one, 148 * therefore it makes sense to suppress the standard "Malformed 149 * conditional" message. */ 150 Boolean printedError; 151 } CondParser; 152 153 static Token CondParser_Expr(CondParser *par, Boolean); 154 155 static unsigned int cond_depth = 0; /* current .if nesting level */ 156 static unsigned int cond_min_depth = 0; /* depth at makefile open */ 157 158 /* 159 * Indicate when we should be strict about lhs of comparisons. 160 * In strict mode, the lhs must be a variable expression or a string literal 161 * in quotes. In non-strict mode it may also be an unquoted string literal. 162 * 163 * TRUE when CondEvalExpression is called from Cond_EvalLine (.if etc) 164 * FALSE when CondEvalExpression is called from ApplyModifier_IfElse 165 * since lhs is already expanded and we cannot tell if 166 * it was a variable reference or not. 167 */ 168 static Boolean lhsStrict; 169 170 static int 171 is_token(const char *str, const char *tok, size_t len) 172 { 173 return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]); 174 } 175 176 /* Push back the most recent token read. We only need one level of this. */ 177 static void 178 CondParser_PushBack(CondParser *par, Token t) 179 { 180 assert(par->curr == TOK_NONE); 181 assert(t != TOK_NONE); 182 183 par->curr = t; 184 } 185 186 static void 187 CondParser_SkipWhitespace(CondParser *par) 188 { 189 cpp_skip_whitespace(&par->p); 190 } 191 192 /* Parse the argument of a built-in function. 193 * 194 * Arguments: 195 * *pp initially points at the '(', 196 * upon successful return it points right after the ')'. 197 * 198 * *out_arg receives the argument as string. 199 * 200 * func says whether the argument belongs to an actual function, or 201 * whether the parsed argument is passed to the default function. 202 * 203 * Return the length of the argument. */ 204 static size_t 205 ParseFuncArg(const char **pp, Boolean doEval, const char *func, 206 char **out_arg) { 207 const char *p = *pp; 208 Buffer argBuf; 209 int paren_depth; 210 size_t argLen; 211 212 if (func != NULL) 213 p++; /* Skip opening '(' - verified by caller */ 214 215 if (*p == '\0') { 216 /* 217 * No arguments whatsoever. Because 'make' and 'defined' aren't really 218 * "reserved words", we don't print a message. I think this is better 219 * than hitting the user with a warning message every time s/he uses 220 * the word 'make' or 'defined' at the beginning of a symbol... 221 */ 222 *out_arg = NULL; 223 return 0; 224 } 225 226 while (*p == ' ' || *p == '\t') { 227 p++; 228 } 229 230 Buf_Init(&argBuf, 16); 231 232 paren_depth = 0; 233 for (;;) { 234 char ch = *p; 235 if (ch == 0 || ch == ' ' || ch == '\t') 236 break; 237 if ((ch == '&' || ch == '|') && paren_depth == 0) 238 break; 239 if (*p == '$') { 240 /* 241 * Parse the variable spec and install it as part of the argument 242 * if it's valid. We tell Var_Parse to complain on an undefined 243 * variable, so we don't need to do it. Nor do we return an error, 244 * though perhaps we should... 245 */ 246 void *nestedVal_freeIt; 247 VarEvalFlags eflags = VARE_UNDEFERR | (doEval ? VARE_WANTRES : 0); 248 const char *nestedVal; 249 (void)Var_Parse(&p, VAR_CMDLINE, eflags, &nestedVal, 250 &nestedVal_freeIt); 251 /* TODO: handle errors */ 252 Buf_AddStr(&argBuf, nestedVal); 253 free(nestedVal_freeIt); 254 continue; 255 } 256 if (ch == '(') 257 paren_depth++; 258 else if (ch == ')' && --paren_depth < 0) 259 break; 260 Buf_AddByte(&argBuf, *p); 261 p++; 262 } 263 264 *out_arg = Buf_GetAll(&argBuf, &argLen); 265 Buf_Destroy(&argBuf, FALSE); 266 267 while (*p == ' ' || *p == '\t') { 268 p++; 269 } 270 271 if (func != NULL && *p++ != ')') { 272 Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", 273 func); 274 /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */ 275 return 0; 276 } 277 278 *pp = p; 279 return argLen; 280 } 281 282 /* Test whether the given variable is defined. */ 283 static Boolean 284 FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg) 285 { 286 void *freeIt; 287 Boolean result = Var_Value(arg, VAR_CMDLINE, &freeIt) != NULL; 288 bmake_free(freeIt); 289 return result; 290 } 291 292 /* See if the given target is being made. */ 293 static Boolean 294 FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg) 295 { 296 StringListNode *ln; 297 298 for (ln = opts.create->first; ln != NULL; ln = ln->next) 299 if (Str_Match(ln->datum, arg)) 300 return TRUE; 301 return FALSE; 302 } 303 304 /* See if the given file exists. */ 305 static Boolean 306 FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg) 307 { 308 Boolean result; 309 char *path; 310 311 path = Dir_FindFile(arg, dirSearchPath); 312 DEBUG2(COND, "exists(%s) result is \"%s\"\n", arg, path ? path : ""); 313 if (path != NULL) { 314 result = TRUE; 315 free(path); 316 } else { 317 result = FALSE; 318 } 319 return result; 320 } 321 322 /* See if the given node exists and is an actual target. */ 323 static Boolean 324 FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg) 325 { 326 GNode *gn = Targ_FindNode(arg); 327 return gn != NULL && GNode_IsTarget(gn); 328 } 329 330 /* See if the given node exists and is an actual target with commands 331 * associated with it. */ 332 static Boolean 333 FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg) 334 { 335 GNode *gn = Targ_FindNode(arg); 336 return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(gn->commands); 337 } 338 339 /*- 340 * Convert the given number into a double. 341 * We try a base 10 or 16 integer conversion first, if that fails 342 * then we try a floating point conversion instead. 343 * 344 * Results: 345 * Sets 'value' to double value of string. 346 * Returns TRUE if the conversion succeeded. 347 */ 348 static Boolean 349 TryParseNumber(const char *str, double *value) 350 { 351 char *eptr, ech; 352 unsigned long l_val; 353 double d_val; 354 355 errno = 0; 356 if (!*str) { 357 *value = 0.0; 358 return TRUE; 359 } 360 l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10); 361 ech = *eptr; 362 if (ech == '\0' && errno != ERANGE) { 363 d_val = str[0] == '-' ? -(double)-l_val : (double)l_val; 364 } else { 365 if (ech != '\0' && ech != '.' && ech != 'e' && ech != 'E') 366 return FALSE; 367 d_val = strtod(str, &eptr); 368 if (*eptr) 369 return FALSE; 370 } 371 372 *value = d_val; 373 return TRUE; 374 } 375 376 static Boolean 377 is_separator(char ch) 378 { 379 return ch == '\0' || ch_isspace(ch) || strchr("!=><)", ch) != NULL; 380 } 381 382 /*- 383 * Parse a string from a variable reference or an optionally quoted 384 * string. This is called for the lhs and rhs of string comparisons. 385 * 386 * Results: 387 * Returns the string, absent any quotes, or NULL on error. 388 * Sets quoted if the string was quoted. 389 * Sets freeIt if needed. 390 */ 391 /* coverity:[+alloc : arg-*4] */ 392 static const char * 393 CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS, 394 Boolean *quoted, void **freeIt) 395 { 396 Buffer buf; 397 const char *str; 398 Boolean atStart; 399 const char *nested_p; 400 Boolean qt; 401 const char *start; 402 VarEvalFlags eflags; 403 VarParseResult parseResult; 404 405 Buf_Init(&buf, 0); 406 str = NULL; 407 *freeIt = NULL; 408 *quoted = qt = par->p[0] == '"' ? 1 : 0; 409 start = par->p; 410 if (qt) 411 par->p++; 412 while (par->p[0] && str == NULL) { 413 switch (par->p[0]) { 414 case '\\': 415 par->p++; 416 if (par->p[0] != '\0') { 417 Buf_AddByte(&buf, par->p[0]); 418 par->p++; 419 } 420 continue; 421 case '"': 422 if (qt) { 423 par->p++; /* we don't want the quotes */ 424 goto got_str; 425 } 426 Buf_AddByte(&buf, par->p[0]); /* likely? */ 427 par->p++; 428 continue; 429 case ')': 430 case '!': 431 case '=': 432 case '>': 433 case '<': 434 case ' ': 435 case '\t': 436 if (!qt) 437 goto got_str; 438 Buf_AddByte(&buf, par->p[0]); 439 par->p++; 440 continue; 441 case '$': 442 /* if we are in quotes, an undefined variable is ok */ 443 eflags = ((!qt && doEval) ? VARE_UNDEFERR : 0) | 444 (doEval ? VARE_WANTRES : 0); 445 nested_p = par->p; 446 atStart = nested_p == start; 447 parseResult = Var_Parse(&nested_p, VAR_CMDLINE, eflags, &str, 448 freeIt); 449 /* TODO: handle errors */ 450 if (str == var_Error) { 451 if (parseResult & VPR_ANY_MSG) 452 par->printedError = TRUE; 453 if (*freeIt) { 454 free(*freeIt); 455 *freeIt = NULL; 456 } 457 /* 458 * Even if !doEval, we still report syntax errors, which 459 * is what getting var_Error back with !doEval means. 460 */ 461 str = NULL; 462 goto cleanup; 463 } 464 par->p = nested_p; 465 466 /* 467 * If the '$' started the string literal (which means no quotes), 468 * and the variable expression is followed by a space, looks like 469 * a comparison operator or is the end of the expression, we are 470 * done. 471 */ 472 if (atStart && is_separator(par->p[0])) 473 goto cleanup; 474 475 Buf_AddStr(&buf, str); 476 if (*freeIt) { 477 free(*freeIt); 478 *freeIt = NULL; 479 } 480 str = NULL; /* not finished yet */ 481 continue; 482 default: 483 if (strictLHS && !qt && *start != '$' && !ch_isdigit(*start)) { 484 /* lhs must be quoted, a variable reference or number */ 485 if (*freeIt) { 486 free(*freeIt); 487 *freeIt = NULL; 488 } 489 str = NULL; 490 goto cleanup; 491 } 492 Buf_AddByte(&buf, par->p[0]); 493 par->p++; 494 continue; 495 } 496 } 497 got_str: 498 *freeIt = Buf_GetAll(&buf, NULL); 499 str = *freeIt; 500 cleanup: 501 Buf_Destroy(&buf, FALSE); 502 return str; 503 } 504 505 /* The different forms of .if directives. */ 506 static const struct If { 507 const char *form; /* Form of if */ 508 size_t formlen; /* Length of form */ 509 Boolean doNot; /* TRUE if default function should be negated */ 510 Boolean (*defProc)(size_t, const char *); /* Default function to apply */ 511 } ifs[] = { 512 { "def", 3, FALSE, FuncDefined }, 513 { "ndef", 4, TRUE, FuncDefined }, 514 { "make", 4, FALSE, FuncMake }, 515 { "nmake", 5, TRUE, FuncMake }, 516 { "", 0, FALSE, FuncDefined }, 517 { NULL, 0, FALSE, NULL } 518 }; 519 520 /* Evaluate a "comparison without operator", such as in ".if ${VAR}" or 521 * ".if 0". */ 522 static Token 523 EvalNotEmpty(CondParser *par, const char *lhs, Boolean lhsQuoted) 524 { 525 double left; 526 527 /* For .ifxxx "..." check for non-empty string. */ 528 if (lhsQuoted) 529 return lhs[0] != '\0'; 530 531 /* For .ifxxx <number> compare against zero */ 532 if (TryParseNumber(lhs, &left)) 533 return left != 0.0; 534 535 /* For .if ${...} check for non-empty string (defProc is ifdef). */ 536 if (par->if_info->form[0] == '\0') 537 return lhs[0] != 0; 538 539 /* Otherwise action default test ... */ 540 return par->if_info->defProc(strlen(lhs), lhs) == !par->if_info->doNot; 541 } 542 543 /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */ 544 static Token 545 EvalCompareNum(double lhs, const char *op, double rhs) 546 { 547 DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, op); 548 549 switch (op[0]) { 550 case '!': 551 if (op[1] != '=') { 552 Parse_Error(PARSE_WARNING, "Unknown operator"); 553 /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */ 554 return TOK_ERROR; 555 } 556 return lhs != rhs; 557 case '=': 558 if (op[1] != '=') { 559 Parse_Error(PARSE_WARNING, "Unknown operator"); 560 /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */ 561 return TOK_ERROR; 562 } 563 return lhs == rhs; 564 case '<': 565 return op[1] == '=' ? lhs <= rhs : lhs < rhs; 566 case '>': 567 return op[1] == '=' ? lhs >= rhs : lhs > rhs; 568 } 569 return TOK_ERROR; 570 } 571 572 static Token 573 EvalCompareStr(const char *lhs, const char *op, const char *rhs) 574 { 575 if (!((op[0] == '!' || op[0] == '=') && op[1] == '=')) { 576 Parse_Error(PARSE_WARNING, 577 "String comparison operator must be either == or !="); 578 /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */ 579 return TOK_ERROR; 580 } 581 582 DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", lhs, rhs, op); 583 return (*op == '=') == (strcmp(lhs, rhs) == 0); 584 } 585 586 /* Evaluate a comparison, such as "${VAR} == 12345". */ 587 static Token 588 EvalCompare(const char *lhs, Boolean lhsQuoted, const char *op, 589 const char *rhs, Boolean rhsQuoted) 590 { 591 double left, right; 592 593 if (!rhsQuoted && !lhsQuoted) 594 if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right)) 595 return EvalCompareNum(left, op, right); 596 597 return EvalCompareStr(lhs, op, rhs); 598 } 599 600 /* Parse a comparison condition such as: 601 * 602 * 0 603 * ${VAR:Mpattern} 604 * ${VAR} == value 605 * ${VAR:U0} < 12345 606 */ 607 static Token 608 CondParser_Comparison(CondParser *par, Boolean doEval) 609 { 610 Token t = TOK_ERROR; 611 const char *lhs, *op, *rhs; 612 void *lhsFree, *rhsFree; 613 Boolean lhsQuoted, rhsQuoted; 614 615 rhs = NULL; 616 lhsFree = rhsFree = NULL; 617 lhsQuoted = rhsQuoted = FALSE; 618 619 /* 620 * Parse the variable spec and skip over it, saving its 621 * value in lhs. 622 */ 623 lhs = CondParser_String(par, doEval, lhsStrict, &lhsQuoted, &lhsFree); 624 if (!lhs) 625 goto done; 626 627 CondParser_SkipWhitespace(par); 628 629 /* 630 * Make sure the operator is a valid one. If it isn't a 631 * known relational operator, pretend we got a 632 * != 0 comparison. 633 */ 634 op = par->p; 635 switch (par->p[0]) { 636 case '!': 637 case '=': 638 case '<': 639 case '>': 640 if (par->p[1] == '=') { 641 par->p += 2; 642 } else { 643 par->p++; 644 } 645 break; 646 default: 647 t = doEval ? EvalNotEmpty(par, lhs, lhsQuoted) : TOK_FALSE; 648 goto done; 649 } 650 651 CondParser_SkipWhitespace(par); 652 653 if (par->p[0] == '\0') { 654 Parse_Error(PARSE_WARNING, "Missing right-hand-side of operator"); 655 /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */ 656 goto done; 657 } 658 659 rhs = CondParser_String(par, doEval, FALSE, &rhsQuoted, &rhsFree); 660 if (rhs == NULL) 661 goto done; 662 663 if (!doEval) { 664 t = TOK_FALSE; 665 goto done; 666 } 667 668 t = EvalCompare(lhs, lhsQuoted, op, rhs, rhsQuoted); 669 670 done: 671 free(lhsFree); 672 free(rhsFree); 673 return t; 674 } 675 676 static size_t 677 ParseEmptyArg(const char **linePtr, Boolean doEval, 678 const char *func MAKE_ATTR_UNUSED, char **argPtr) 679 { 680 void *val_freeIt; 681 const char *val; 682 size_t magic_res; 683 684 /* We do all the work here and return the result as the length */ 685 *argPtr = NULL; 686 687 (*linePtr)--; /* Make (*linePtr)[1] point to the '('. */ 688 (void)Var_Parse(linePtr, VAR_CMDLINE, doEval ? VARE_WANTRES : 0, 689 &val, &val_freeIt); 690 /* TODO: handle errors */ 691 /* If successful, *linePtr points beyond the closing ')' now. */ 692 693 if (val == var_Error) { 694 free(val_freeIt); 695 return (size_t)-1; 696 } 697 698 /* A variable is empty when it just contains spaces... 4/15/92, christos */ 699 cpp_skip_whitespace(&val); 700 701 /* 702 * For consistency with the other functions we can't generate the 703 * true/false here. 704 */ 705 magic_res = *val != '\0' ? 2 : 1; 706 free(val_freeIt); 707 return magic_res; 708 } 709 710 static Boolean 711 FuncEmpty(size_t arglen, const char *arg MAKE_ATTR_UNUSED) 712 { 713 /* Magic values ahead, see ParseEmptyArg. */ 714 return arglen == 1; 715 } 716 717 static Token 718 CondParser_Func(CondParser *par, Boolean doEval) 719 { 720 static const struct fn_def { 721 const char *fn_name; 722 size_t fn_name_len; 723 size_t (*fn_parse)(const char **, Boolean, const char *, char **); 724 Boolean (*fn_eval)(size_t, const char *); 725 } fn_defs[] = { 726 { "defined", 7, ParseFuncArg, FuncDefined }, 727 { "make", 4, ParseFuncArg, FuncMake }, 728 { "exists", 6, ParseFuncArg, FuncExists }, 729 { "empty", 5, ParseEmptyArg, FuncEmpty }, 730 { "target", 6, ParseFuncArg, FuncTarget }, 731 { "commands", 8, ParseFuncArg, FuncCommands }, 732 { NULL, 0, NULL, NULL }, 733 }; 734 const struct fn_def *fn_def; 735 Token t; 736 char *arg = NULL; 737 size_t arglen; 738 const char *cp = par->p; 739 const char *cp1; 740 741 for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) { 742 if (!is_token(cp, fn_def->fn_name, fn_def->fn_name_len)) 743 continue; 744 cp += fn_def->fn_name_len; 745 /* There can only be whitespace before the '(' */ 746 cpp_skip_whitespace(&cp); 747 if (*cp != '(') 748 break; 749 750 arglen = fn_def->fn_parse(&cp, doEval, fn_def->fn_name, &arg); 751 if (arglen == 0 || arglen == (size_t)-1) { 752 par->p = cp; 753 return arglen == 0 ? TOK_FALSE : TOK_ERROR; 754 } 755 /* Evaluate the argument using the required function. */ 756 t = !doEval || fn_def->fn_eval(arglen, arg); 757 free(arg); 758 par->p = cp; 759 return t; 760 } 761 762 /* Push anything numeric through the compare expression */ 763 cp = par->p; 764 if (ch_isdigit(cp[0]) || strchr("+-", cp[0])) 765 return CondParser_Comparison(par, doEval); 766 767 /* 768 * Most likely we have a naked token to apply the default function to. 769 * However ".if a == b" gets here when the "a" is unquoted and doesn't 770 * start with a '$'. This surprises people. 771 * If what follows the function argument is a '=' or '!' then the syntax 772 * would be invalid if we did "defined(a)" - so instead treat as an 773 * expression. 774 */ 775 arglen = ParseFuncArg(&cp, doEval, NULL, &arg); 776 cp1 = cp; 777 cpp_skip_whitespace(&cp1); 778 if (*cp1 == '=' || *cp1 == '!') 779 return CondParser_Comparison(par, doEval); 780 par->p = cp; 781 782 /* 783 * Evaluate the argument using the default function. 784 * This path always treats .if as .ifdef. To get here, the character 785 * after .if must have been taken literally, so the argument cannot 786 * be empty - even if it contained a variable expansion. 787 */ 788 t = !doEval || par->if_info->defProc(arglen, arg) == !par->if_info->doNot; 789 free(arg); 790 return t; 791 } 792 793 /* Return the next token or comparison result from the parser. */ 794 static Token 795 CondParser_Token(CondParser *par, Boolean doEval) 796 { 797 Token t; 798 799 t = par->curr; 800 if (t != TOK_NONE) { 801 par->curr = TOK_NONE; 802 return t; 803 } 804 805 while (par->p[0] == ' ' || par->p[0] == '\t') { 806 par->p++; 807 } 808 809 switch (par->p[0]) { 810 811 case '(': 812 par->p++; 813 return TOK_LPAREN; 814 815 case ')': 816 par->p++; 817 return TOK_RPAREN; 818 819 case '|': 820 par->p++; 821 if (par->p[0] == '|') { 822 par->p++; 823 } 824 return TOK_OR; 825 826 case '&': 827 par->p++; 828 if (par->p[0] == '&') { 829 par->p++; 830 } 831 return TOK_AND; 832 833 case '!': 834 par->p++; 835 return TOK_NOT; 836 837 case '#': 838 case '\n': 839 case '\0': 840 return TOK_EOF; 841 842 case '"': 843 case '$': 844 return CondParser_Comparison(par, doEval); 845 846 default: 847 return CondParser_Func(par, doEval); 848 } 849 } 850 851 /* Parse a single term in the expression. This consists of a terminal symbol 852 * or TOK_NOT and a term (not including the binary operators): 853 * 854 * T -> defined(variable) | make(target) | exists(file) | symbol 855 * T -> ! T | ( E ) 856 * 857 * Results: 858 * TOK_TRUE, TOK_FALSE or TOK_ERROR. 859 */ 860 static Token 861 CondParser_Term(CondParser *par, Boolean doEval) 862 { 863 Token t; 864 865 t = CondParser_Token(par, doEval); 866 867 if (t == TOK_EOF) { 868 /* 869 * If we reached the end of the expression, the expression 870 * is malformed... 871 */ 872 t = TOK_ERROR; 873 } else if (t == TOK_LPAREN) { 874 /* 875 * T -> ( E ) 876 */ 877 t = CondParser_Expr(par, doEval); 878 if (t != TOK_ERROR) { 879 if (CondParser_Token(par, doEval) != TOK_RPAREN) { 880 t = TOK_ERROR; 881 } 882 } 883 } else if (t == TOK_NOT) { 884 t = CondParser_Term(par, doEval); 885 if (t == TOK_TRUE) { 886 t = TOK_FALSE; 887 } else if (t == TOK_FALSE) { 888 t = TOK_TRUE; 889 } 890 } 891 return t; 892 } 893 894 /* Parse a conjunctive factor (nice name, wot?) 895 * 896 * F -> T && F | T 897 * 898 * Results: 899 * TOK_TRUE, TOK_FALSE or TOK_ERROR 900 */ 901 static Token 902 CondParser_Factor(CondParser *par, Boolean doEval) 903 { 904 Token l, o; 905 906 l = CondParser_Term(par, doEval); 907 if (l != TOK_ERROR) { 908 o = CondParser_Token(par, doEval); 909 910 if (o == TOK_AND) { 911 /* 912 * F -> T && F 913 * 914 * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we 915 * have to parse the r.h.s. anyway (to throw it away). 916 * If T is TOK_TRUE, the result is the r.h.s., be it a TOK_ERROR 917 * or not. 918 */ 919 if (l == TOK_TRUE) { 920 l = CondParser_Factor(par, doEval); 921 } else { 922 (void)CondParser_Factor(par, FALSE); 923 } 924 } else { 925 /* 926 * F -> T 927 */ 928 CondParser_PushBack(par, o); 929 } 930 } 931 return l; 932 } 933 934 /* Main expression production. 935 * 936 * E -> F || E | F 937 * 938 * Results: 939 * TOK_TRUE, TOK_FALSE or TOK_ERROR. 940 */ 941 static Token 942 CondParser_Expr(CondParser *par, Boolean doEval) 943 { 944 Token l, o; 945 946 l = CondParser_Factor(par, doEval); 947 if (l != TOK_ERROR) { 948 o = CondParser_Token(par, doEval); 949 950 if (o == TOK_OR) { 951 /* 952 * E -> F || E 953 * 954 * A similar thing occurs for ||, except that here we make sure 955 * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s. 956 * Once again, if l is TOK_FALSE, the result is the r.h.s. and once 957 * again if l is TOK_TRUE, we parse the r.h.s. to throw it away. 958 */ 959 if (l == TOK_FALSE) { 960 l = CondParser_Expr(par, doEval); 961 } else { 962 (void)CondParser_Expr(par, FALSE); 963 } 964 } else { 965 /* 966 * E -> F 967 */ 968 CondParser_PushBack(par, o); 969 } 970 } 971 return l; 972 } 973 974 static CondEvalResult 975 CondParser_Eval(CondParser *par, Boolean *value) 976 { 977 Token res; 978 979 DEBUG1(COND, "CondParser_Eval: %s\n", par->p); 980 981 res = CondParser_Expr(par, TRUE); 982 if (res != TOK_FALSE && res != TOK_TRUE) 983 return COND_INVALID; 984 985 if (CondParser_Token(par, TRUE /* XXX: Why TRUE? */) != TOK_EOF) 986 return COND_INVALID; 987 988 *value = res == TOK_TRUE; 989 return COND_PARSE; 990 } 991 992 /* Evaluate the condition, including any side effects from the variable 993 * expressions in the condition. The condition consists of &&, ||, !, 994 * function(arg), comparisons and parenthetical groupings thereof. 995 * 996 * Results: 997 * COND_PARSE if the condition was valid grammatically 998 * COND_INVALID if not a valid conditional. 999 * 1000 * (*value) is set to the boolean value of the condition 1001 */ 1002 static CondEvalResult 1003 CondEvalExpression(const struct If *info, const char *cond, Boolean *value, 1004 Boolean eprint, Boolean strictLHS) 1005 { 1006 static const struct If *dflt_info; 1007 CondParser par; 1008 int rval; 1009 1010 lhsStrict = strictLHS; 1011 1012 while (*cond == ' ' || *cond == '\t') 1013 cond++; 1014 1015 if (info == NULL && (info = dflt_info) == NULL) { 1016 /* Scan for the entry for .if - it can't be first */ 1017 for (info = ifs;; info++) 1018 if (info->form[0] == 0) 1019 break; 1020 dflt_info = info; 1021 } 1022 assert(info != NULL); 1023 1024 par.if_info = info; 1025 par.p = cond; 1026 par.curr = TOK_NONE; 1027 par.printedError = FALSE; 1028 1029 rval = CondParser_Eval(&par, value); 1030 1031 if (rval == COND_INVALID && eprint && !par.printedError) 1032 Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond); 1033 1034 return rval; 1035 } 1036 1037 CondEvalResult 1038 Cond_EvalCondition(const char *cond, Boolean *out_value) 1039 { 1040 return CondEvalExpression(NULL, cond, out_value, FALSE, FALSE); 1041 } 1042 1043 /* Evaluate the conditional in the passed line. The line looks like this: 1044 * .<cond-type> <expr> 1045 * In this line, <cond-type> is any of if, ifmake, ifnmake, ifdef, ifndef, 1046 * elif, elifmake, elifnmake, elifdef, elifndef. 1047 * In this line, <expr> consists of &&, ||, !, function(arg), comparisons 1048 * and parenthetical groupings thereof. 1049 * 1050 * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order 1051 * to detect spurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF), 1052 * otherwise .else could be treated as '.elif 1'. 1053 * 1054 * Results: 1055 * COND_PARSE to continue parsing the lines after the conditional 1056 * (when .if or .else returns TRUE) 1057 * COND_SKIP to skip the lines after the conditional 1058 * (when .if or .elif returns FALSE, or when a previous 1059 * branch has already been taken) 1060 * COND_INVALID if the conditional was not valid, either because of 1061 * a syntax error or because some variable was undefined 1062 * or because the condition could not be evaluated 1063 */ 1064 CondEvalResult 1065 Cond_EvalLine(const char *line) 1066 { 1067 enum { MAXIF = 128 }; /* maximum depth of .if'ing */ 1068 enum { MAXIF_BUMP = 32 }; /* how much to grow by */ 1069 enum if_states { 1070 IF_ACTIVE, /* .if or .elif part active */ 1071 ELSE_ACTIVE, /* .else part active */ 1072 SEARCH_FOR_ELIF, /* searching for .elif/else to execute */ 1073 SKIP_TO_ELSE, /* has been true, but not seen '.else' */ 1074 SKIP_TO_ENDIF /* nothing else to execute */ 1075 }; 1076 static enum if_states *cond_state = NULL; 1077 static unsigned int max_if_depth = MAXIF; 1078 1079 const struct If *ifp; 1080 Boolean isElif; 1081 Boolean value; 1082 enum if_states state; 1083 1084 if (!cond_state) { 1085 cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state)); 1086 cond_state[0] = IF_ACTIVE; 1087 } 1088 /* skip leading character (the '.') and any whitespace */ 1089 for (line++; *line == ' ' || *line == '\t'; line++) 1090 continue; 1091 1092 /* Find what type of if we're dealing with. */ 1093 if (line[0] == 'e') { 1094 if (line[1] != 'l') { 1095 if (!is_token(line + 1, "ndif", 4)) 1096 return COND_INVALID; 1097 /* End of conditional section */ 1098 if (cond_depth == cond_min_depth) { 1099 Parse_Error(PARSE_FATAL, "if-less endif"); 1100 return COND_PARSE; 1101 } 1102 /* Return state for previous conditional */ 1103 cond_depth--; 1104 return cond_state[cond_depth] <= ELSE_ACTIVE 1105 ? COND_PARSE : COND_SKIP; 1106 } 1107 1108 /* Quite likely this is 'else' or 'elif' */ 1109 line += 2; 1110 if (is_token(line, "se", 2)) { 1111 /* It is else... */ 1112 if (cond_depth == cond_min_depth) { 1113 Parse_Error(PARSE_FATAL, "if-less else"); 1114 return COND_PARSE; 1115 } 1116 1117 state = cond_state[cond_depth]; 1118 switch (state) { 1119 case SEARCH_FOR_ELIF: 1120 state = ELSE_ACTIVE; 1121 break; 1122 case ELSE_ACTIVE: 1123 case SKIP_TO_ENDIF: 1124 Parse_Error(PARSE_WARNING, "extra else"); 1125 /* FALLTHROUGH */ 1126 default: 1127 case IF_ACTIVE: 1128 case SKIP_TO_ELSE: 1129 state = SKIP_TO_ENDIF; 1130 break; 1131 } 1132 cond_state[cond_depth] = state; 1133 return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; 1134 } 1135 /* Assume for now it is an elif */ 1136 isElif = TRUE; 1137 } else 1138 isElif = FALSE; 1139 1140 if (line[0] != 'i' || line[1] != 'f') 1141 /* Not an ifxxx or elifxxx line */ 1142 return COND_INVALID; 1143 1144 /* 1145 * Figure out what sort of conditional it is -- what its default 1146 * function is, etc. -- by looking in the table of valid "ifs" 1147 */ 1148 line += 2; 1149 for (ifp = ifs;; ifp++) { 1150 if (ifp->form == NULL) 1151 return COND_INVALID; 1152 if (is_token(ifp->form, line, ifp->formlen)) { 1153 line += ifp->formlen; 1154 break; 1155 } 1156 } 1157 1158 /* Now we know what sort of 'if' it is... */ 1159 1160 if (isElif) { 1161 if (cond_depth == cond_min_depth) { 1162 Parse_Error(PARSE_FATAL, "if-less elif"); 1163 return COND_PARSE; 1164 } 1165 state = cond_state[cond_depth]; 1166 if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) { 1167 Parse_Error(PARSE_WARNING, "extra elif"); 1168 cond_state[cond_depth] = SKIP_TO_ENDIF; 1169 return COND_SKIP; 1170 } 1171 if (state != SEARCH_FOR_ELIF) { 1172 /* Either just finished the 'true' block, or already SKIP_TO_ELSE */ 1173 cond_state[cond_depth] = SKIP_TO_ELSE; 1174 return COND_SKIP; 1175 } 1176 } else { 1177 /* Normal .if */ 1178 if (cond_depth + 1 >= max_if_depth) { 1179 /* 1180 * This is rare, but not impossible. 1181 * In meta mode, dirdeps.mk (only runs at level 0) 1182 * can need more than the default. 1183 */ 1184 max_if_depth += MAXIF_BUMP; 1185 cond_state = bmake_realloc(cond_state, 1186 max_if_depth * sizeof(*cond_state)); 1187 } 1188 state = cond_state[cond_depth]; 1189 cond_depth++; 1190 if (state > ELSE_ACTIVE) { 1191 /* If we aren't parsing the data, treat as always false */ 1192 cond_state[cond_depth] = SKIP_TO_ELSE; 1193 return COND_SKIP; 1194 } 1195 } 1196 1197 /* And evaluate the conditional expression */ 1198 if (CondEvalExpression(ifp, line, &value, TRUE, TRUE) == COND_INVALID) { 1199 /* Syntax error in conditional, error message already output. */ 1200 /* Skip everything to matching .endif */ 1201 cond_state[cond_depth] = SKIP_TO_ELSE; 1202 return COND_SKIP; 1203 } 1204 1205 if (!value) { 1206 cond_state[cond_depth] = SEARCH_FOR_ELIF; 1207 return COND_SKIP; 1208 } 1209 cond_state[cond_depth] = IF_ACTIVE; 1210 return COND_PARSE; 1211 } 1212 1213 void 1214 Cond_restore_depth(unsigned int saved_depth) 1215 { 1216 unsigned int open_conds = cond_depth - cond_min_depth; 1217 1218 if (open_conds != 0 || saved_depth > cond_depth) { 1219 Parse_Error(PARSE_FATAL, "%u open conditional%s", open_conds, 1220 open_conds == 1 ? "" : "s"); 1221 cond_depth = cond_min_depth; 1222 } 1223 1224 cond_min_depth = saved_depth; 1225 } 1226 1227 unsigned int 1228 Cond_save_depth(void) 1229 { 1230 unsigned int depth = cond_min_depth; 1231 1232 cond_min_depth = cond_depth; 1233 return depth; 1234 } 1235