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