1 /* $OpenBSD: cond.c,v 1.50 2013/11/22 15:47:35 espie Exp $ */ 2 /* $NetBSD: cond.c,v 1.7 1996/11/06 17:59:02 christos Exp $ */ 3 4 /* 5 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 6 * Copyright (c) 1988, 1989 by Adam de Boor 7 * Copyright (c) 1989 by Berkeley Softworks 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Adam de Boor. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include <ctype.h> 39 #include <stddef.h> 40 #include <stdint.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <ohash.h> 45 #include "config.h" 46 #include "defines.h" 47 #include "dir.h" 48 #include "buf.h" 49 #include "cond.h" 50 #include "cond_int.h" 51 #include "condhashconsts.h" 52 #include "error.h" 53 #include "var.h" 54 #include "varname.h" 55 #include "targ.h" 56 #include "lowparse.h" 57 #include "str.h" 58 #include "main.h" 59 #include "gnode.h" 60 #include "lst.h" 61 62 63 /* The parsing of conditional expressions is based on this grammar: 64 * E -> F || E 65 * E -> F 66 * F -> T && F 67 * F -> T 68 * T -> defined(variable) 69 * T -> make(target) 70 * T -> exists(file) 71 * T -> empty(varspec) 72 * T -> target(name) 73 * T -> commands(name) 74 * T -> symbol 75 * T -> $(varspec) op value 76 * T -> $(varspec) == "string" 77 * T -> $(varspec) != "string" 78 * T -> "string" == "string" 79 * T -> "string" != "string" 80 * T -> number op number 81 * T -> ( E ) 82 * T -> ! T 83 * op -> == | != | > | < | >= | <= 84 * 85 * 'symbol' is some other symbol to which the default function (condDefProc) 86 * is applied. 87 * 88 * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) 89 * will return And for '&' and '&&', Or for '|' and '||', Not for '!', 90 * LParen for '(', RParen for ')' and will evaluate the other terminal 91 * symbols, using either the default function or the function given in the 92 * terminal, and return the result as either true or False. 93 * 94 * All Non-Terminal functions (CondE, CondF and CondT) return Err on error. */ 95 typedef enum { 96 False = 0, True = 1, And, Or, Not, LParen, RParen, EndOfFile, None, Err 97 } Token; 98 99 /*- 100 * Structures to handle elegantly the different forms of #if's. The 101 * last two fields are stored in condInvert and condDefProc, respectively. 102 */ 103 static bool CondGetArg(const char **, struct Name *, 104 const char *, bool); 105 static bool CondDoDefined(struct Name *); 106 static bool CondDoMake(struct Name *); 107 static bool CondDoExists(struct Name *); 108 static bool CondDoTarget(struct Name *); 109 static bool CondDoTargetWithCommands(struct Name *); 110 static bool CondCvtArg(const char *, double *); 111 static Token CondToken(bool); 112 static Token CondT(bool); 113 static Token CondF(bool); 114 static Token CondE(bool); 115 static Token CondHandleVarSpec(bool); 116 static Token CondHandleDefault(bool); 117 static Token CondHandleComparison(char *, bool, bool); 118 static Token CondHandleString(bool); 119 static Token CondHandleNumber(bool); 120 static const char *find_cond(const char *); 121 122 123 struct If { 124 bool isElse; /* true for else forms */ 125 bool doNot; /* true for embedded negation */ 126 bool (*defProc)(struct Name *); /* function to apply */ 127 }; 128 129 static struct If ifs[] = { 130 { false,false, CondDoDefined }, /* if, ifdef */ 131 { false,true, CondDoDefined }, /* ifndef */ 132 { false,false, CondDoMake }, /* ifmake */ 133 { false,true, CondDoMake }, /* ifnmake */ 134 { true, false, CondDoDefined }, /* elif, elifdef */ 135 { true, true, CondDoDefined }, /* elifndef */ 136 { true, false, CondDoMake }, /* elifmake */ 137 { true, true, CondDoMake }, /* elifnmake */ 138 { true, false, NULL } 139 }; 140 141 #define COND_IF_INDEX 0 142 #define COND_IFDEF_INDEX 0 143 #define COND_IFNDEF_INDEX 1 144 #define COND_IFMAKE_INDEX 2 145 #define COND_IFNMAKE_INDEX 3 146 #define COND_ELIF_INDEX 4 147 #define COND_ELIFDEF_INDEX 4 148 #define COND_ELIFNDEF_INDEX 5 149 #define COND_ELIFMAKE_INDEX 6 150 #define COND_ELIFNMAKE_INDEX 7 151 #define COND_ELSE_INDEX 8 152 153 static bool condInvert; /* Invert the default function */ 154 static bool (*condDefProc)(struct Name *); 155 /* Default function to apply */ 156 static const char *condExpr; /* The expression to parse */ 157 static Token condPushBack=None; /* Single push-back token used in parsing */ 158 159 #define MAXIF 30 /* greatest depth of #if'ing */ 160 161 static struct { 162 bool value; 163 Location origin; 164 } condStack[MAXIF]; /* Stack of conditionals */ 165 166 static int condTop = MAXIF; /* Top-most conditional */ 167 static int skipIfLevel=0; /* Depth of skipped conditionals */ 168 static bool skipLine = false; /* Whether the parse module is skipping lines */ 169 170 static const char * 171 find_cond(const char *p) 172 { 173 for (;;p++) { 174 /* XXX: when *p == '\0', strchr() returns !NULL */ 175 if (strchr(" \t)&|$", *p) != NULL) 176 return p; 177 } 178 } 179 180 181 /*- 182 *----------------------------------------------------------------------- 183 * CondGetArg -- 184 * Find the argument of a built-in function. 185 * 186 * Results: 187 * true if evaluation went okay 188 * 189 * Side Effects: 190 * The line pointer is set to point to the closing parenthesis of the 191 * function call. The argument is filled. 192 *----------------------------------------------------------------------- 193 */ 194 static bool 195 CondGetArg(const char **linePtr, struct Name *arg, const char *func, 196 bool parens) /* true if arg should be bounded by parens */ 197 { 198 const char *cp; 199 200 cp = *linePtr; 201 /* Set things up to return faster in case of problem */ 202 arg->s = cp; 203 arg->e = cp; 204 arg->tofree = false; 205 206 /* make and defined are not really keywords, so if CondGetArg doesn't 207 * work... 208 */ 209 if (parens) { 210 while (ISSPACE(*cp)) 211 cp++; 212 if (*cp == '(') 213 cp++; 214 else 215 return false; 216 } 217 218 if (*cp == '\0') 219 return false; 220 221 while (ISSPACE(*cp)) 222 cp++; 223 224 cp = VarName_Get(cp, arg, NULL, true, find_cond); 225 226 while (ISSPACE(*cp)) 227 cp++; 228 if (parens) { 229 if (*cp == ')') 230 cp++; 231 else { 232 Parse_Error(PARSE_WARNING, 233 "Missing closing parenthesis for %s()", func); 234 return false; 235 } 236 } 237 238 *linePtr = cp; 239 return true; 240 } 241 242 /*- 243 *----------------------------------------------------------------------- 244 * CondDoDefined -- 245 * Handle the 'defined' function for conditionals. 246 * 247 * Results: 248 * true if the given variable is defined. 249 *----------------------------------------------------------------------- 250 */ 251 static bool 252 CondDoDefined(struct Name *arg) 253 { 254 return Var_Definedi(arg->s, arg->e); 255 } 256 257 /*- 258 *----------------------------------------------------------------------- 259 * CondDoMake -- 260 * Handle the 'make' function for conditionals. 261 * 262 * Results: 263 * true if the given target is being made. 264 *----------------------------------------------------------------------- 265 */ 266 static bool 267 CondDoMake(struct Name *arg) 268 { 269 LstNode ln; 270 271 for (ln = Lst_First(create); ln != NULL; ln = Lst_Adv(ln)) { 272 char *s = (char *)Lst_Datum(ln); 273 if (Str_Matchi(s, strchr(s, '\0'), arg->s, arg->e)) 274 return true; 275 } 276 277 return false; 278 } 279 280 /*- 281 *----------------------------------------------------------------------- 282 * CondDoExists -- 283 * See if the given file exists. 284 * 285 * Results: 286 * true if the file exists and false if it does not. 287 *----------------------------------------------------------------------- 288 */ 289 static bool 290 CondDoExists(struct Name *arg) 291 { 292 bool result; 293 char *path; 294 295 path = Dir_FindFilei(arg->s, arg->e, defaultPath); 296 if (path != NULL) { 297 result = true; 298 free(path); 299 } else { 300 result = false; 301 } 302 return result; 303 } 304 305 /*- 306 *----------------------------------------------------------------------- 307 * CondDoTarget -- 308 * See if the given node exists and is an actual target. 309 * 310 * Results: 311 * true if the node exists as a target and false if it does not. 312 *----------------------------------------------------------------------- 313 */ 314 static bool 315 CondDoTarget(struct Name *arg) 316 { 317 GNode *gn; 318 319 gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE); 320 if (gn != NULL && !OP_NOP(gn->type)) 321 return true; 322 else 323 return false; 324 } 325 326 /*- 327 *----------------------------------------------------------------------- 328 * CondDoTargetWithCommands -- 329 * See if the given node exists and has commands. 330 * 331 * Results: 332 * true if the node is complete and false if it does not. 333 *----------------------------------------------------------------------- 334 */ 335 static bool 336 CondDoTargetWithCommands(struct Name *arg) 337 { 338 GNode *gn; 339 340 gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE); 341 if (gn != NULL && !OP_NOP(gn->type) && (gn->type & OP_HAS_COMMANDS)) 342 return true; 343 else 344 return false; 345 } 346 347 348 /*- 349 *----------------------------------------------------------------------- 350 * CondCvtArg -- 351 * Convert the given number into a double. If the number begins 352 * with 0x, it is interpreted as a hexadecimal integer 353 * and converted to a double from there. All other strings just have 354 * strtod called on them. 355 * 356 * Results: 357 * Sets 'value' to double value of string. 358 * Returns true if the string was a valid number, false o.w. 359 * 360 * Side Effects: 361 * Can change 'value' even if string is not a valid number. 362 *----------------------------------------------------------------------- 363 */ 364 static bool 365 CondCvtArg(const char *str, double *value) 366 { 367 if (*str == '0' && str[1] == 'x') { 368 long i; 369 370 for (str += 2, i = 0; *str; str++) { 371 int x; 372 if (ISDIGIT(*str)) 373 x = *str - '0'; 374 else if (ISXDIGIT(*str)) 375 x = 10 + *str - (ISUPPER(*str) ? 'A' : 'a'); 376 else 377 return false; 378 i = (i << 4) + x; 379 } 380 *value = (double) i; 381 return true; 382 } 383 else { 384 char *eptr; 385 *value = strtod(str, &eptr); 386 return *eptr == '\0'; 387 } 388 } 389 390 391 static Token 392 CondHandleNumber(bool doEval) 393 { 394 const char *end; 395 char *lhs; 396 397 end = condExpr; 398 while (!ISSPACE(*end) && strchr("!=><", *end) == NULL) 399 end++; 400 lhs = Str_dupi(condExpr, end); 401 condExpr = end; 402 return CondHandleComparison(lhs, true, doEval); 403 } 404 405 static Token 406 CondHandleVarSpec(bool doEval) 407 { 408 char *lhs; 409 size_t varSpecLen; 410 bool doFree; 411 412 /* Parse the variable spec and skip over it, saving its 413 * value in lhs. */ 414 lhs = Var_Parse(condExpr, NULL, doEval,&varSpecLen,&doFree); 415 if (lhs == var_Error) 416 /* Even if !doEval, we still report syntax errors, which 417 * is what getting var_Error back with !doEval means. */ 418 return Err; 419 condExpr += varSpecLen; 420 421 if (!ISSPACE(*condExpr) && 422 strchr("!=><", *condExpr) == NULL) { 423 BUFFER buf; 424 425 Buf_Init(&buf, 0); 426 427 Buf_AddString(&buf, lhs); 428 429 if (doFree) 430 free(lhs); 431 432 for (;*condExpr && !ISSPACE(*condExpr); condExpr++) 433 Buf_AddChar(&buf, *condExpr); 434 435 lhs = Var_Subst(Buf_Retrieve(&buf), NULL, doEval); 436 Buf_Destroy(&buf); 437 doFree = true; 438 } 439 440 return CondHandleComparison(lhs, doFree, doEval); 441 } 442 443 static Token 444 CondHandleString(bool doEval) 445 { 446 char *lhs; 447 const char *begin; 448 BUFFER buf; 449 450 /* find the extent of the string */ 451 begin = ++condExpr; 452 while (*condExpr && *condExpr != '"') { 453 condExpr++; 454 } 455 456 Buf_Init(&buf, 0); 457 Buf_Addi(&buf, begin, condExpr); 458 if (*condExpr == '"') 459 condExpr++; 460 lhs = Var_Subst(Buf_Retrieve(&buf), NULL, doEval); 461 Buf_Destroy(&buf); 462 return CondHandleComparison(lhs, true, doEval); 463 } 464 465 static Token 466 CondHandleComparison(char *lhs, bool doFree, bool doEval) 467 { 468 Token t; 469 const char *rhs; 470 const char *op; 471 472 t = Err; 473 /* Skip whitespace to get to the operator. */ 474 while (ISSPACE(*condExpr)) 475 condExpr++; 476 477 /* Make sure the operator is a valid one. If it isn't a 478 * known relational operator, pretend we got a 479 * != 0 comparison. */ 480 op = condExpr; 481 switch (*condExpr) { 482 case '!': 483 case '=': 484 case '<': 485 case '>': 486 if (condExpr[1] == '=') 487 condExpr += 2; 488 else 489 condExpr += 1; 490 break; 491 default: 492 op = "!="; 493 rhs = "0"; 494 495 goto do_compare; 496 } 497 while (ISSPACE(*condExpr)) 498 condExpr++; 499 if (*condExpr == '\0') { 500 Parse_Error(PARSE_WARNING, 501 "Missing right-hand-side of operator"); 502 goto error; 503 } 504 rhs = condExpr; 505 do_compare: 506 if (*rhs == '"') { 507 /* Doing a string comparison. Only allow == and != for 508 * operators. */ 509 char *string; 510 const char *cp; 511 int qt; 512 BUFFER buf; 513 514 do_string_compare: 515 if ((*op != '!' && *op != '=') || op[1] != '=') { 516 Parse_Error(PARSE_WARNING, 517 "String comparison operator should be either == or !="); 518 goto error; 519 } 520 521 Buf_Init(&buf, 0); 522 qt = *rhs == '"' ? 1 : 0; 523 524 for (cp = &rhs[qt]; ((qt && *cp != '"') || 525 (!qt && strchr(" \t)", *cp) == NULL)) && *cp != '\0';) { 526 if (*cp == '$') { 527 size_t len; 528 529 if (Var_ParseBuffer(&buf, cp, NULL, doEval, 530 &len)) { 531 cp += len; 532 continue; 533 } 534 } else if (*cp == '\\' && cp[1] != '\0') 535 /* Backslash escapes things -- skip over next 536 * character, if it exists. */ 537 cp++; 538 Buf_AddChar(&buf, *cp++); 539 } 540 541 string = Buf_Retrieve(&buf); 542 543 if (DEBUG(COND)) 544 printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n", 545 lhs, string, op); 546 /* Null-terminate rhs and perform the comparison. 547 * t is set to the result. */ 548 if (*op == '=') 549 t = strcmp(lhs, string) ? False : True; 550 else 551 t = strcmp(lhs, string) ? True : False; 552 free(string); 553 if (rhs == condExpr) { 554 if (!qt && *cp == ')') 555 condExpr = cp; 556 else if (*cp == '\0') 557 condExpr = cp; 558 else 559 condExpr = cp + 1; 560 } 561 } else { 562 /* rhs is either a float or an integer. Convert both the 563 * lhs and the rhs to a double and compare the two. */ 564 double left, right; 565 char *string; 566 567 if (!CondCvtArg(lhs, &left)) 568 goto do_string_compare; 569 if (*rhs == '$') { 570 size_t len; 571 bool freeIt; 572 573 string = Var_Parse(rhs, NULL, doEval,&len,&freeIt); 574 if (string == var_Error) 575 right = 0.0; 576 else { 577 if (!CondCvtArg(string, &right)) { 578 if (freeIt) 579 free(string); 580 goto do_string_compare; 581 } 582 if (freeIt) 583 free(string); 584 if (rhs == condExpr) 585 condExpr += len; 586 } 587 } else { 588 if (!CondCvtArg(rhs, &right)) 589 goto do_string_compare; 590 if (rhs == condExpr) { 591 /* Skip over the right-hand side. */ 592 while (!ISSPACE(*condExpr) && *condExpr != '\0') 593 condExpr++; 594 } 595 } 596 597 if (DEBUG(COND)) 598 printf("left = %f, right = %f, op = %.2s\n", left, 599 right, op); 600 switch (op[0]) { 601 case '!': 602 if (op[1] != '=') { 603 Parse_Error(PARSE_WARNING, "Unknown operator"); 604 goto error; 605 } 606 t = left != right ? True : False; 607 break; 608 case '=': 609 if (op[1] != '=') { 610 Parse_Error(PARSE_WARNING, "Unknown operator"); 611 goto error; 612 } 613 t = left == right ? True : False; 614 break; 615 case '<': 616 if (op[1] == '=') 617 t = left <= right ? True : False; 618 else 619 t = left < right ? True : False; 620 break; 621 case '>': 622 if (op[1] == '=') 623 t = left >= right ? True : False; 624 else 625 t = left > right ? True : False; 626 break; 627 } 628 } 629 error: 630 if (doFree) 631 free(lhs); 632 return t; 633 } 634 635 #define S(s) s, sizeof(s)-1 636 static struct operator { 637 const char *s; 638 size_t len; 639 bool (*proc)(struct Name *); 640 } ops[] = { 641 {S("defined"), CondDoDefined}, 642 {S("make"), CondDoMake}, 643 {S("exists"), CondDoExists}, 644 {S("target"), CondDoTarget}, 645 {S("commands"), CondDoTargetWithCommands}, 646 {NULL, 0, NULL} 647 }; 648 649 static Token 650 CondHandleDefault(bool doEval) 651 { 652 bool t; 653 bool (*evalProc)(struct Name *); 654 bool invert = false; 655 struct Name arg; 656 size_t arglen; 657 658 evalProc = NULL; 659 if (strncmp(condExpr, "empty", 5) == 0) { 660 /* Use Var_Parse to parse the spec in parens and return 661 * True if the resulting string is empty. */ 662 size_t length; 663 bool doFree; 664 char *val; 665 666 condExpr += 5; 667 668 for (arglen = 0; condExpr[arglen] != '(' && 669 condExpr[arglen] != '\0';) 670 arglen++; 671 672 if (condExpr[arglen] != '\0') { 673 val = Var_Parse(&condExpr[arglen - 1], NULL, 674 doEval, &length, &doFree); 675 if (val == var_Error) 676 t = Err; 677 else { 678 /* A variable is empty when it just contains 679 * spaces... 4/15/92, christos */ 680 char *p; 681 for (p = val; ISSPACE(*p); p++) 682 continue; 683 t = *p == '\0' ? True : False; 684 } 685 if (doFree) 686 free(val); 687 /* Advance condExpr to beyond the closing ). Note that 688 * we subtract one from arglen + length b/c length 689 * is calculated from condExpr[arglen - 1]. */ 690 condExpr += arglen + length - 1; 691 return t; 692 } else 693 condExpr -= 5; 694 } else { 695 struct operator *op; 696 697 for (op = ops; op != NULL; op++) 698 if (strncmp(condExpr, op->s, op->len) == 0) { 699 condExpr += op->len; 700 if (CondGetArg(&condExpr, &arg, op->s, true)) 701 evalProc = op->proc; 702 else 703 condExpr -= op->len; 704 break; 705 } 706 } 707 if (evalProc == NULL) { 708 /* The symbol is itself the argument to the default 709 * function. We advance condExpr to the end of the symbol 710 * by hand (the next whitespace, closing paren or 711 * binary operator) and set to invert the evaluation 712 * function if condInvert is true. */ 713 invert = condInvert; 714 evalProc = condDefProc; 715 /* XXX should we ignore problems now ? */ 716 CondGetArg(&condExpr, &arg, "", false); 717 } 718 719 /* Evaluate the argument using the set function. If invert 720 * is true, we invert the sense of the function. */ 721 t = (!doEval || (*evalProc)(&arg) ? 722 (invert ? False : True) : 723 (invert ? True : False)); 724 VarName_Free(&arg); 725 return t; 726 } 727 728 /*- 729 *----------------------------------------------------------------------- 730 * CondToken -- 731 * Return the next token from the input. 732 * 733 * Results: 734 * A Token for the next lexical token in the stream. 735 * 736 * Side Effects: 737 * condPushback will be set back to None if it is used. 738 *----------------------------------------------------------------------- 739 */ 740 static Token 741 CondToken(bool doEval) 742 { 743 744 if (condPushBack != None) { 745 Token t; 746 747 t = condPushBack; 748 condPushBack = None; 749 return t; 750 } 751 752 while (ISSPACE(*condExpr)) 753 condExpr++; 754 switch (*condExpr) { 755 case '(': 756 condExpr++; 757 return LParen; 758 case ')': 759 condExpr++; 760 return RParen; 761 case '|': 762 if (condExpr[1] == '|') 763 condExpr++; 764 condExpr++; 765 return Or; 766 case '&': 767 if (condExpr[1] == '&') 768 condExpr++; 769 condExpr++; 770 return And; 771 case '!': 772 condExpr++; 773 return Not; 774 case '\n': 775 case '\0': 776 return EndOfFile; 777 case '"': 778 return CondHandleString(doEval); 779 case '$': 780 return CondHandleVarSpec(doEval); 781 case '0': case '1': case '2': case '3': case '4': 782 case '5': case '6': case '7': case '8': case '9': 783 return CondHandleNumber(doEval); 784 default: 785 return CondHandleDefault(doEval); 786 } 787 } 788 789 /*- 790 *----------------------------------------------------------------------- 791 * CondT -- 792 * Parse a single term in the expression. This consists of a terminal 793 * symbol or Not and a terminal symbol (not including the binary 794 * operators): 795 * T -> defined(variable) | make(target) | exists(file) | symbol 796 * T -> ! T | ( E ) 797 * 798 * Results: 799 * True, False or Err. 800 * 801 * Side Effects: 802 * Tokens are consumed. 803 *----------------------------------------------------------------------- 804 */ 805 static Token 806 CondT(bool doEval) 807 { 808 Token t; 809 810 t = CondToken(doEval); 811 812 if (t == EndOfFile) 813 /* If we reached the end of the expression, the expression 814 * is malformed... */ 815 t = Err; 816 else if (t == LParen) { 817 /* T -> ( E ). */ 818 t = CondE(doEval); 819 if (t != Err) 820 if (CondToken(doEval) != RParen) 821 t = Err; 822 } else if (t == Not) { 823 t = CondT(doEval); 824 if (t == True) 825 t = False; 826 else if (t == False) 827 t = True; 828 } 829 return t; 830 } 831 832 /*- 833 *----------------------------------------------------------------------- 834 * CondF -- 835 * Parse a conjunctive factor (nice name, wot?) 836 * F -> T && F | T 837 * 838 * Results: 839 * True, False or Err 840 * 841 * Side Effects: 842 * Tokens are consumed. 843 *----------------------------------------------------------------------- 844 */ 845 static Token 846 CondF(bool doEval) 847 { 848 Token l, o; 849 850 l = CondT(doEval); 851 if (l != Err) { 852 o = CondToken(doEval); 853 854 if (o == And) { 855 /* F -> T && F 856 * 857 * If T is False, the whole thing will be False, but we 858 * have to parse the r.h.s. anyway (to throw it away). If 859 * T is True, the result is the r.h.s., be it an Err or no. 860 * */ 861 if (l == True) 862 l = CondF(doEval); 863 else 864 (void)CondF(false); 865 } else 866 /* F -> T. */ 867 condPushBack = o; 868 } 869 return l; 870 } 871 872 /*- 873 *----------------------------------------------------------------------- 874 * CondE -- 875 * Main expression production. 876 * E -> F || E | F 877 * 878 * Results: 879 * True, False or Err. 880 * 881 * Side Effects: 882 * Tokens are, of course, consumed. 883 *----------------------------------------------------------------------- 884 */ 885 static Token 886 CondE(bool doEval) 887 { 888 Token l, o; 889 890 l = CondF(doEval); 891 if (l != Err) { 892 o = CondToken(doEval); 893 894 if (o == Or) { 895 /* E -> F || E 896 * 897 * A similar thing occurs for ||, except that here we 898 * make sure the l.h.s. is False before we bother to 899 * evaluate the r.h.s. Once again, if l is False, the 900 * result is the r.h.s. and once again if l is True, we 901 * parse the r.h.s. to throw it away. */ 902 if (l == False) 903 l = CondE(doEval); 904 else 905 (void)CondE(false); 906 } else 907 /* E -> F. */ 908 condPushBack = o; 909 } 910 return l; 911 } 912 913 /* Evaluate conditional in line. 914 * returns COND_SKIP, COND_PARSE, COND_INVALID, COND_ISFOR, COND_ISINCLUDE, 915 * COND_ISUNDEF. 916 * A conditional line looks like this: 917 * <cond-type> <expr> 918 * where <cond-type> is any of if, ifmake, ifnmake, ifdef, 919 * ifndef, elif, elifmake, elifnmake, elifdef, elifndef 920 * and <expr> consists of &&, ||, !, make(target), defined(variable) 921 * and parenthetical groupings thereof. 922 */ 923 int 924 Cond_Eval(const char *line) 925 { 926 /* find end of keyword */ 927 const char *end; 928 uint32_t k; 929 size_t len; 930 struct If *ifp; 931 bool value = false; 932 int level; /* Level at which to report errors. */ 933 934 level = PARSE_FATAL; 935 936 for (end = line; ISLOWER(*end); end++) 937 ; 938 /* quick path: recognize special targets early on */ 939 if (*end == '.' || *end == ':') 940 return COND_INVALID; 941 len = end - line; 942 k = ohash_interval(line, &end); 943 switch(k % MAGICSLOTS2) { 944 case K_COND_IF % MAGICSLOTS2: 945 if (k == K_COND_IF && len == strlen(COND_IF) && 946 strncmp(line, COND_IF, len) == 0) { 947 ifp = ifs + COND_IF_INDEX; 948 } else 949 return COND_INVALID; 950 break; 951 case K_COND_IFDEF % MAGICSLOTS2: 952 if (k == K_COND_IFDEF && len == strlen(COND_IFDEF) && 953 strncmp(line, COND_IFDEF, len) == 0) { 954 ifp = ifs + COND_IFDEF_INDEX; 955 } else 956 return COND_INVALID; 957 break; 958 case K_COND_IFNDEF % MAGICSLOTS2: 959 if (k == K_COND_IFNDEF && len == strlen(COND_IFNDEF) && 960 strncmp(line, COND_IFNDEF, len) == 0) { 961 ifp = ifs + COND_IFNDEF_INDEX; 962 } else 963 return COND_INVALID; 964 break; 965 case K_COND_IFMAKE % MAGICSLOTS2: 966 if (k == K_COND_IFMAKE && len == strlen(COND_IFMAKE) && 967 strncmp(line, COND_IFMAKE, len) == 0) { 968 ifp = ifs + COND_IFMAKE_INDEX; 969 } else 970 return COND_INVALID; 971 break; 972 case K_COND_IFNMAKE % MAGICSLOTS2: 973 if (k == K_COND_IFNMAKE && len == strlen(COND_IFNMAKE) && 974 strncmp(line, COND_IFNMAKE, len) == 0) { 975 ifp = ifs + COND_IFNMAKE_INDEX; 976 } else 977 return COND_INVALID; 978 break; 979 case K_COND_ELIF % MAGICSLOTS2: 980 if (k == K_COND_ELIF && len == strlen(COND_ELIF) && 981 strncmp(line, COND_ELIF, len) == 0) { 982 ifp = ifs + COND_ELIF_INDEX; 983 } else 984 return COND_INVALID; 985 break; 986 case K_COND_ELIFDEF % MAGICSLOTS2: 987 if (k == K_COND_ELIFDEF && len == strlen(COND_ELIFDEF) && 988 strncmp(line, COND_ELIFDEF, len) == 0) { 989 ifp = ifs + COND_ELIFDEF_INDEX; 990 } else 991 return COND_INVALID; 992 break; 993 case K_COND_ELIFNDEF % MAGICSLOTS2: 994 if (k == K_COND_ELIFNDEF && len == strlen(COND_ELIFNDEF) && 995 strncmp(line, COND_ELIFNDEF, len) == 0) { 996 ifp = ifs + COND_ELIFNDEF_INDEX; 997 } else 998 return COND_INVALID; 999 break; 1000 case K_COND_ELIFMAKE % MAGICSLOTS2: 1001 if (k == K_COND_ELIFMAKE && len == strlen(COND_ELIFMAKE) && 1002 strncmp(line, COND_ELIFMAKE, len) == 0) { 1003 ifp = ifs + COND_ELIFMAKE_INDEX; 1004 } else 1005 return COND_INVALID; 1006 break; 1007 case K_COND_ELIFNMAKE % MAGICSLOTS2: 1008 if (k == K_COND_ELIFNMAKE && len == strlen(COND_ELIFNMAKE) && 1009 strncmp(line, COND_ELIFNMAKE, len) == 0) { 1010 ifp = ifs + COND_ELIFNMAKE_INDEX; 1011 } else 1012 return COND_INVALID; 1013 break; 1014 case K_COND_ELSE % MAGICSLOTS2: 1015 /* valid conditional whose value is the inverse 1016 * of the previous if we parsed. */ 1017 if (k == K_COND_ELSE && len == strlen(COND_ELSE) && 1018 strncmp(line, COND_ELSE, len) == 0) { 1019 if (condTop == MAXIF) { 1020 Parse_Error(level, "if-less else"); 1021 return COND_INVALID; 1022 } else if (skipIfLevel == 0) { 1023 value = !condStack[condTop].value; 1024 ifp = ifs + COND_ELSE_INDEX; 1025 } else 1026 return COND_SKIP; 1027 } else 1028 return COND_INVALID; 1029 break; 1030 case K_COND_ENDIF % MAGICSLOTS2: 1031 if (k == K_COND_ENDIF && len == strlen(COND_ENDIF) && 1032 strncmp(line, COND_ENDIF, len) == 0) { 1033 /* End of a conditional section. If skipIfLevel is 1034 * non-zero, that conditional was skipped, so lines 1035 * following it should also be skipped. Hence, we 1036 * return COND_SKIP. Otherwise, the conditional was 1037 * read so succeeding lines should be parsed (think 1038 * about it...) so we return COND_PARSE, unless this 1039 * endif isn't paired with a decent if. */ 1040 if (skipIfLevel != 0) { 1041 skipIfLevel--; 1042 return COND_SKIP; 1043 } else { 1044 if (condTop == MAXIF) { 1045 Parse_Error(level, "if-less endif"); 1046 return COND_INVALID; 1047 } else { 1048 skipLine = false; 1049 condTop++; 1050 return COND_PARSE; 1051 } 1052 } 1053 } else 1054 return COND_INVALID; 1055 break; 1056 1057 /* Recognize other keywords there, to simplify parser's task */ 1058 case K_COND_FOR % MAGICSLOTS2: 1059 if (k == K_COND_FOR && len == strlen(COND_FOR) && 1060 strncmp(line, COND_FOR, len) == 0) 1061 return COND_ISFOR; 1062 else 1063 return COND_INVALID; 1064 case K_COND_UNDEF % MAGICSLOTS2: 1065 if (k == K_COND_UNDEF && len == strlen(COND_UNDEF) && 1066 strncmp(line, COND_UNDEF, len) == 0) 1067 return COND_ISUNDEF; 1068 else 1069 return COND_INVALID; 1070 case K_COND_POISON % MAGICSLOTS2: 1071 if (k == K_COND_POISON && len == strlen(COND_POISON) && 1072 strncmp(line, COND_POISON, len) == 0) 1073 return COND_ISPOISON; 1074 else 1075 return COND_INVALID; 1076 case K_COND_INCLUDE % MAGICSLOTS2: 1077 if (k == K_COND_INCLUDE && len == strlen(COND_INCLUDE) && 1078 strncmp(line, COND_INCLUDE, len) == 0) 1079 return COND_ISINCLUDE; 1080 else 1081 return COND_INVALID; 1082 default: 1083 /* Not a valid conditional type. No error... */ 1084 return COND_INVALID; 1085 } 1086 1087 if (ifp->isElse) { 1088 if (condTop == MAXIF) { 1089 Parse_Error(level, "if-less elif"); 1090 return COND_INVALID; 1091 } else if (skipIfLevel != 0 || condStack[condTop].value) { 1092 /* 1093 * Skip if we're meant to or is an else-type 1094 * conditional and previous corresponding one was 1095 * evaluated to true. 1096 */ 1097 skipLine = true; 1098 return COND_SKIP; 1099 } 1100 } else if (skipLine) { 1101 /* Don't even try to evaluate a conditional that's not an else 1102 * if we're skipping things... */ 1103 skipIfLevel++; 1104 return COND_SKIP; 1105 } else 1106 condTop--; 1107 1108 if (condTop < 0) { 1109 /* This is the one case where we can definitely proclaim a fatal 1110 * error. If we don't, we're hosed. */ 1111 Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", 1112 MAXIF); 1113 condTop = 0; 1114 return COND_INVALID; 1115 } 1116 1117 if (ifp->defProc) { 1118 /* Initialize file-global variables for parsing. */ 1119 condDefProc = ifp->defProc; 1120 condInvert = ifp->doNot; 1121 1122 line += len; 1123 1124 while (*line == ' ' || *line == '\t') 1125 line++; 1126 1127 condExpr = line; 1128 condPushBack = None; 1129 1130 switch (CondE(true)) { 1131 case True: 1132 if (CondToken(true) == EndOfFile) { 1133 value = true; 1134 break; 1135 } 1136 goto err; 1137 /* FALLTHROUGH */ 1138 case False: 1139 if (CondToken(true) == EndOfFile) { 1140 value = false; 1141 break; 1142 } 1143 /* FALLTHROUGH */ 1144 case Err: 1145 err: 1146 Parse_Error(level, "Malformed conditional (%s)", line); 1147 return COND_INVALID; 1148 default: 1149 break; 1150 } 1151 } 1152 1153 condStack[condTop].value = value; 1154 Parse_FillLocation(&condStack[condTop].origin); 1155 skipLine = !value; 1156 return value ? COND_PARSE : COND_SKIP; 1157 } 1158 1159 void 1160 Cond_End(void) 1161 { 1162 int i; 1163 1164 if (condTop != MAXIF) { 1165 Parse_Error(PARSE_FATAL, "%s%d open conditional%s", 1166 condTop == 0 ? "at least ": "", MAXIF-condTop, 1167 MAXIF-condTop == 1 ? "" : "s"); 1168 for (i = MAXIF-1; i >= condTop; i--) { 1169 fprintf(stderr, "\t(%s:%lu)\n", 1170 condStack[i].origin.fname, 1171 condStack[i].origin.lineno); 1172 } 1173 } 1174 condTop = MAXIF; 1175 } 1176