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