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