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