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