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