1 /* $OpenBSD: cond.c,v 1.54 2019/12/21 15:29:25 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 currently being built, 264 * either explicitly on the command line, or implicitly as the 265 * default target. 266 *----------------------------------------------------------------------- 267 */ 268 static bool 269 CondDoMake(struct Name *arg) 270 { 271 LstNode ln; 272 273 for (ln = Lst_First(create); ln != NULL; ln = Lst_Adv(ln)) { 274 char *s = Lst_Datum(ln); 275 if (Str_Matchi(s, strchr(s, '\0'), arg->s, arg->e)) 276 return true; 277 } 278 279 return false; 280 } 281 282 /*- 283 *----------------------------------------------------------------------- 284 * CondDoExists -- 285 * See if the given file exists. 286 * 287 * Results: 288 * true if the file exists and false if it does not. 289 *----------------------------------------------------------------------- 290 */ 291 static bool 292 CondDoExists(struct Name *arg) 293 { 294 bool result; 295 char *path; 296 297 if (arg->s == arg->e) 298 Parse_Error(PARSE_FATAL, "Empty file name in .if exists()"); 299 300 path = Dir_FindFilei(arg->s, arg->e, defaultPath); 301 if (path != NULL) { 302 result = true; 303 free(path); 304 } else { 305 result = false; 306 } 307 return result; 308 } 309 310 /*- 311 *----------------------------------------------------------------------- 312 * CondDoTarget -- 313 * See if the given node exists and is an actual target. 314 * 315 * Results: 316 * true if the node exists as a target and false if it does not. 317 *----------------------------------------------------------------------- 318 */ 319 static bool 320 CondDoTarget(struct Name *arg) 321 { 322 GNode *gn; 323 324 gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE); 325 if (gn != NULL && !OP_NOP(gn->type)) 326 return true; 327 else 328 return false; 329 } 330 331 /*- 332 *----------------------------------------------------------------------- 333 * CondDoTargetWithCommands -- 334 * See if the given node exists and has commands. 335 * 336 * Results: 337 * true if the node is complete and false if it does not. 338 *----------------------------------------------------------------------- 339 */ 340 static bool 341 CondDoTargetWithCommands(struct Name *arg) 342 { 343 GNode *gn; 344 345 gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE); 346 if (gn != NULL && !OP_NOP(gn->type) && (gn->type & OP_HAS_COMMANDS)) 347 return true; 348 else 349 return false; 350 } 351 352 353 /*- 354 *----------------------------------------------------------------------- 355 * CondCvtArg -- 356 * Convert the given number into a double. If the number begins 357 * with 0x, it is interpreted as a hexadecimal integer 358 * and converted to a double from there. All other strings just have 359 * strtod called on them. 360 * 361 * Results: 362 * Sets 'value' to double value of string. 363 * Returns true if the string was a valid number, false o.w. 364 * 365 * Side Effects: 366 * Can change 'value' even if string is not a valid number. 367 *----------------------------------------------------------------------- 368 */ 369 static bool 370 CondCvtArg(const char *str, double *value) 371 { 372 if (*str == '0' && str[1] == 'x') { 373 long i; 374 375 for (str += 2, i = 0; *str; str++) { 376 int x; 377 if (ISDIGIT(*str)) 378 x = *str - '0'; 379 else if (ISXDIGIT(*str)) 380 x = 10 + *str - (ISUPPER(*str) ? 'A' : 'a'); 381 else 382 return false; 383 i = (i << 4) + x; 384 } 385 *value = (double) i; 386 return true; 387 } 388 else { 389 char *eptr; 390 *value = strtod(str, &eptr); 391 return *eptr == '\0'; 392 } 393 } 394 395 396 static Token 397 CondHandleNumber(bool doEval) 398 { 399 const char *end; 400 char *lhs; 401 402 end = condExpr; 403 while (*end != '\0' && !ISSPACE(*end) && strchr("!=><", *end) == NULL) 404 end++; 405 lhs = Str_dupi(condExpr, end); 406 condExpr = end; 407 return CondHandleComparison(lhs, true, doEval); 408 } 409 410 static Token 411 CondHandleVarSpec(bool doEval) 412 { 413 char *lhs; 414 size_t varSpecLen; 415 bool doFree; 416 417 /* Parse the variable spec and skip over it, saving its 418 * value in lhs. */ 419 lhs = Var_Parse(condExpr, NULL, doEval,&varSpecLen,&doFree); 420 if (lhs == var_Error) 421 /* Even if !doEval, we still report syntax errors, which 422 * is what getting var_Error back with !doEval means. */ 423 return Err; 424 condExpr += varSpecLen; 425 426 if (*condExpr && !ISSPACE(*condExpr) && 427 strchr("!=><", *condExpr) == NULL) { 428 BUFFER buf; 429 430 Buf_Init(&buf, 0); 431 432 Buf_AddString(&buf, lhs); 433 434 if (doFree) 435 free(lhs); 436 437 for (;*condExpr && !ISSPACE(*condExpr); condExpr++) 438 Buf_AddChar(&buf, *condExpr); 439 440 lhs = Var_Subst(Buf_Retrieve(&buf), NULL, doEval); 441 Buf_Destroy(&buf); 442 doFree = true; 443 } 444 445 return CondHandleComparison(lhs, doFree, doEval); 446 } 447 448 static Token 449 CondHandleString(bool doEval) 450 { 451 char *lhs; 452 const char *begin; 453 BUFFER buf; 454 455 /* find the extent of the string */ 456 begin = ++condExpr; 457 while (*condExpr && *condExpr != '"') { 458 condExpr++; 459 } 460 461 Buf_Init(&buf, 0); 462 Buf_Addi(&buf, begin, condExpr); 463 if (*condExpr == '"') 464 condExpr++; 465 lhs = Var_Subst(Buf_Retrieve(&buf), NULL, doEval); 466 Buf_Destroy(&buf); 467 return CondHandleComparison(lhs, true, doEval); 468 } 469 470 static Token 471 CondHandleComparison(char *lhs, bool doFree, bool doEval) 472 { 473 Token t; 474 const char *rhs; 475 const char *op; 476 477 t = Err; 478 /* Skip whitespace to get to the operator. */ 479 while (ISSPACE(*condExpr)) 480 condExpr++; 481 482 /* Make sure the operator is a valid one. If it isn't a 483 * known relational operator, pretend we got a 484 * != 0 comparison. */ 485 op = condExpr; 486 switch (*condExpr) { 487 case '!': 488 case '=': 489 case '<': 490 case '>': 491 if (condExpr[1] == '=') 492 condExpr += 2; 493 else 494 condExpr += 1; 495 break; 496 default: 497 op = "!="; 498 rhs = "0"; 499 500 goto do_compare; 501 } 502 while (ISSPACE(*condExpr)) 503 condExpr++; 504 if (*condExpr == '\0') { 505 Parse_Error(PARSE_WARNING, 506 "Missing right-hand-side of operator"); 507 goto error; 508 } 509 rhs = condExpr; 510 do_compare: 511 if (*rhs == '"') { 512 /* Doing a string comparison. Only allow == and != for 513 * operators. */ 514 char *string; 515 const char *cp; 516 int qt; 517 BUFFER buf; 518 519 do_string_compare: 520 if ((*op != '!' && *op != '=') || op[1] != '=') { 521 Parse_Error(PARSE_WARNING, 522 "String comparison operator should be either == or !="); 523 goto error; 524 } 525 526 Buf_Init(&buf, 0); 527 qt = *rhs == '"' ? 1 : 0; 528 529 for (cp = &rhs[qt]; ((qt && *cp != '"') || 530 (!qt && strchr(" \t)", *cp) == NULL)) && *cp != '\0';) { 531 if (*cp == '$') { 532 size_t len; 533 534 if (Var_ParseBuffer(&buf, cp, NULL, doEval, 535 &len)) { 536 cp += len; 537 continue; 538 } 539 } else if (*cp == '\\' && cp[1] != '\0') 540 /* Backslash escapes things -- skip over next 541 * character, if it exists. */ 542 cp++; 543 Buf_AddChar(&buf, *cp++); 544 } 545 546 string = Buf_Retrieve(&buf); 547 548 if (DEBUG(COND)) 549 printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n", 550 lhs, string, op); 551 /* Null-terminate rhs and perform the comparison. 552 * t is set to the result. */ 553 if (*op == '=') 554 t = strcmp(lhs, string) ? False : True; 555 else 556 t = strcmp(lhs, string) ? True : False; 557 free(string); 558 if (rhs == condExpr) { 559 if (!qt && *cp == ')') 560 condExpr = cp; 561 else if (*cp == '\0') 562 condExpr = cp; 563 else 564 condExpr = cp + 1; 565 } 566 } else { 567 /* rhs is either a float or an integer. Convert both the 568 * lhs and the rhs to a double and compare the two. */ 569 double left, right; 570 char *string; 571 572 if (!CondCvtArg(lhs, &left)) 573 goto do_string_compare; 574 if (*rhs == '$') { 575 size_t len; 576 bool freeIt; 577 578 string = Var_Parse(rhs, NULL, doEval,&len,&freeIt); 579 if (string == var_Error) 580 right = 0.0; 581 else { 582 if (!CondCvtArg(string, &right)) { 583 if (freeIt) 584 free(string); 585 goto do_string_compare; 586 } 587 if (freeIt) 588 free(string); 589 if (rhs == condExpr) 590 condExpr += len; 591 } 592 } else { 593 if (!CondCvtArg(rhs, &right)) 594 goto do_string_compare; 595 if (rhs == condExpr) { 596 /* Skip over the right-hand side. */ 597 while (!ISSPACE(*condExpr) && *condExpr != '\0') 598 condExpr++; 599 } 600 } 601 602 if (DEBUG(COND)) 603 printf("left = %f, right = %f, op = %.2s\n", left, 604 right, op); 605 switch (op[0]) { 606 case '!': 607 if (op[1] != '=') { 608 Parse_Error(PARSE_WARNING, "Unknown operator"); 609 goto error; 610 } 611 t = left != right ? True : False; 612 break; 613 case '=': 614 if (op[1] != '=') { 615 Parse_Error(PARSE_WARNING, "Unknown operator"); 616 goto error; 617 } 618 t = left == right ? True : False; 619 break; 620 case '<': 621 if (op[1] == '=') 622 t = left <= right ? True : False; 623 else 624 t = left < right ? True : False; 625 break; 626 case '>': 627 if (op[1] == '=') 628 t = left >= right ? True : False; 629 else 630 t = left > right ? True : False; 631 break; 632 } 633 } 634 error: 635 if (doFree) 636 free(lhs); 637 return t; 638 } 639 640 #define S(s) s, sizeof(s)-1 641 static struct operator { 642 const char *s; 643 size_t len; 644 bool (*proc)(struct Name *); 645 } ops[] = { 646 {S("defined"), CondDoDefined}, 647 {S("make"), CondDoMake}, 648 {S("exists"), CondDoExists}, 649 {S("target"), CondDoTarget}, 650 {S("commands"), CondDoTargetWithCommands}, 651 {NULL, 0, NULL} 652 }; 653 654 static Token 655 CondHandleDefault(bool doEval) 656 { 657 bool t; 658 bool (*evalProc)(struct Name *); 659 bool invert = false; 660 struct Name arg; 661 size_t arglen; 662 663 evalProc = NULL; 664 if (strncmp(condExpr, "empty", 5) == 0) { 665 /* Use Var_Parse to parse the spec in parens and return 666 * True if the resulting string is empty. */ 667 size_t length; 668 bool doFree; 669 char *val; 670 671 condExpr += 5; 672 673 for (arglen = 0; condExpr[arglen] != '(' && 674 condExpr[arglen] != '\0';) 675 arglen++; 676 677 if (condExpr[arglen] != '\0') { 678 val = Var_Parse(&condExpr[arglen - 1], NULL, 679 doEval, &length, &doFree); 680 if (val == var_Error) 681 t = Err; 682 else { 683 /* A variable is empty when it just contains 684 * spaces... 4/15/92, christos */ 685 char *p; 686 for (p = val; ISSPACE(*p); p++) 687 continue; 688 t = *p == '\0' ? True : False; 689 } 690 if (doFree) 691 free(val); 692 /* Advance condExpr to beyond the closing ). Note that 693 * we subtract one from arglen + length b/c length 694 * is calculated from condExpr[arglen - 1]. */ 695 condExpr += arglen + length - 1; 696 return t; 697 } else 698 condExpr -= 5; 699 } else { 700 struct operator *op; 701 702 for (op = ops; op != NULL; op++) 703 if (strncmp(condExpr, op->s, op->len) == 0) { 704 condExpr += op->len; 705 if (CondGetArg(&condExpr, &arg, op->s, true)) 706 evalProc = op->proc; 707 else 708 condExpr -= op->len; 709 break; 710 } 711 } 712 if (evalProc == NULL) { 713 /* The symbol is itself the argument to the default 714 * function. We advance condExpr to the end of the symbol 715 * by hand (the next whitespace, closing paren or 716 * binary operator) and set to invert the evaluation 717 * function if condInvert is true. */ 718 invert = condInvert; 719 evalProc = condDefProc; 720 /* XXX should we ignore problems now ? */ 721 CondGetArg(&condExpr, &arg, "", false); 722 } 723 724 /* Evaluate the argument using the set function. If invert 725 * is true, we invert the sense of the function. */ 726 t = (!doEval || (*evalProc)(&arg) ? 727 (invert ? False : True) : 728 (invert ? True : False)); 729 VarName_Free(&arg); 730 return t; 731 } 732 733 /*- 734 *----------------------------------------------------------------------- 735 * CondToken -- 736 * Return the next token from the input. 737 * 738 * Results: 739 * A Token for the next lexical token in the stream. 740 * 741 * Side Effects: 742 * condPushback will be set back to None if it is used. 743 *----------------------------------------------------------------------- 744 */ 745 static Token 746 CondToken(bool doEval) 747 { 748 749 if (condPushBack != None) { 750 Token t; 751 752 t = condPushBack; 753 condPushBack = None; 754 return t; 755 } 756 757 while (ISSPACE(*condExpr)) 758 condExpr++; 759 switch (*condExpr) { 760 case '(': 761 condExpr++; 762 return LParen; 763 case ')': 764 condExpr++; 765 return RParen; 766 case '|': 767 if (condExpr[1] == '|') 768 condExpr++; 769 condExpr++; 770 return Or; 771 case '&': 772 if (condExpr[1] == '&') 773 condExpr++; 774 condExpr++; 775 return And; 776 case '!': 777 condExpr++; 778 return Not; 779 case '\n': 780 case '\0': 781 return EndOfFile; 782 case '"': 783 return CondHandleString(doEval); 784 case '$': 785 return CondHandleVarSpec(doEval); 786 case '0': case '1': case '2': case '3': case '4': 787 case '5': case '6': case '7': case '8': case '9': 788 return CondHandleNumber(doEval); 789 default: 790 return CondHandleDefault(doEval); 791 } 792 } 793 794 /*- 795 *----------------------------------------------------------------------- 796 * CondT -- 797 * Parse a single term in the expression. This consists of a terminal 798 * symbol or Not and a terminal symbol (not including the binary 799 * operators): 800 * T -> defined(variable) | make(target) | exists(file) | symbol 801 * T -> ! T | ( E ) 802 * 803 * Results: 804 * True, False or Err. 805 * 806 * Side Effects: 807 * Tokens are consumed. 808 *----------------------------------------------------------------------- 809 */ 810 static Token 811 CondT(bool doEval) 812 { 813 Token t; 814 815 t = CondToken(doEval); 816 817 if (t == EndOfFile) 818 /* If we reached the end of the expression, the expression 819 * is malformed... */ 820 t = Err; 821 else if (t == LParen) { 822 /* T -> ( E ). */ 823 t = CondE(doEval); 824 if (t != Err) 825 if (CondToken(doEval) != RParen) 826 t = Err; 827 } else if (t == Not) { 828 t = CondT(doEval); 829 if (t == True) 830 t = False; 831 else if (t == False) 832 t = True; 833 } 834 return t; 835 } 836 837 /*- 838 *----------------------------------------------------------------------- 839 * CondF -- 840 * Parse a conjunctive factor (nice name, wot?) 841 * F -> T && F | T 842 * 843 * Results: 844 * True, False or Err 845 * 846 * Side Effects: 847 * Tokens are consumed. 848 *----------------------------------------------------------------------- 849 */ 850 static Token 851 CondF(bool doEval) 852 { 853 Token l, o; 854 855 l = CondT(doEval); 856 if (l != Err) { 857 o = CondToken(doEval); 858 859 if (o == And) { 860 /* F -> T && F 861 * 862 * If T is False, the whole thing will be False, but we 863 * have to parse the r.h.s. anyway (to throw it away). If 864 * T is True, the result is the r.h.s., be it an Err or no. 865 * */ 866 if (l == True) 867 l = CondF(doEval); 868 else 869 (void)CondF(false); 870 } else 871 /* F -> T. */ 872 condPushBack = o; 873 } 874 return l; 875 } 876 877 /*- 878 *----------------------------------------------------------------------- 879 * CondE -- 880 * Main expression production. 881 * E -> F || E | F 882 * 883 * Results: 884 * True, False or Err. 885 * 886 * Side Effects: 887 * Tokens are, of course, consumed. 888 *----------------------------------------------------------------------- 889 */ 890 static Token 891 CondE(bool doEval) 892 { 893 Token l, o; 894 895 l = CondF(doEval); 896 if (l != Err) { 897 o = CondToken(doEval); 898 899 if (o == Or) { 900 /* E -> F || E 901 * 902 * A similar thing occurs for ||, except that here we 903 * make sure the l.h.s. is False before we bother to 904 * evaluate the r.h.s. Once again, if l is False, the 905 * result is the r.h.s. and once again if l is True, we 906 * parse the r.h.s. to throw it away. */ 907 if (l == False) 908 l = CondE(doEval); 909 else 910 (void)CondE(false); 911 } else 912 /* E -> F. */ 913 condPushBack = o; 914 } 915 return l; 916 } 917 918 /* Evaluate conditional in line. 919 * returns COND_SKIP, COND_PARSE, COND_INVALID, COND_ISFOR, COND_ISINCLUDE, 920 * COND_ISUNDEF. 921 * A conditional line looks like this: 922 * <cond-type> <expr> 923 * where <cond-type> is any of if, ifmake, ifnmake, ifdef, 924 * ifndef, elif, elifmake, elifnmake, elifdef, elifndef 925 * and <expr> consists of &&, ||, !, make(target), defined(variable) 926 * and parenthetical groupings thereof. 927 */ 928 int 929 Cond_Eval(const char *line) 930 { 931 /* find end of keyword */ 932 const char *end; 933 uint32_t k; 934 size_t len; 935 struct If *ifp; 936 bool value = false; 937 int level; /* Level at which to report errors. */ 938 939 level = PARSE_FATAL; 940 941 for (end = line; ISLOWER(*end); end++) 942 ; 943 /* quick path: recognize special targets early on */ 944 if (*end == '.' || *end == ':') 945 return COND_INVALID; 946 len = end - line; 947 k = ohash_interval(line, &end); 948 switch(k % MAGICSLOTS2) { 949 case K_COND_IF % MAGICSLOTS2: 950 if (k == K_COND_IF && len == strlen(COND_IF) && 951 strncmp(line, COND_IF, len) == 0) { 952 ifp = ifs + COND_IF_INDEX; 953 } else 954 return COND_INVALID; 955 break; 956 case K_COND_IFDEF % MAGICSLOTS2: 957 if (k == K_COND_IFDEF && len == strlen(COND_IFDEF) && 958 strncmp(line, COND_IFDEF, len) == 0) { 959 ifp = ifs + COND_IFDEF_INDEX; 960 } else 961 return COND_INVALID; 962 break; 963 case K_COND_IFNDEF % MAGICSLOTS2: 964 if (k == K_COND_IFNDEF && len == strlen(COND_IFNDEF) && 965 strncmp(line, COND_IFNDEF, len) == 0) { 966 ifp = ifs + COND_IFNDEF_INDEX; 967 } else 968 return COND_INVALID; 969 break; 970 case K_COND_IFMAKE % MAGICSLOTS2: 971 if (k == K_COND_IFMAKE && len == strlen(COND_IFMAKE) && 972 strncmp(line, COND_IFMAKE, len) == 0) { 973 ifp = ifs + COND_IFMAKE_INDEX; 974 } else 975 return COND_INVALID; 976 break; 977 case K_COND_IFNMAKE % MAGICSLOTS2: 978 if (k == K_COND_IFNMAKE && len == strlen(COND_IFNMAKE) && 979 strncmp(line, COND_IFNMAKE, len) == 0) { 980 ifp = ifs + COND_IFNMAKE_INDEX; 981 } else 982 return COND_INVALID; 983 break; 984 case K_COND_ELIF % MAGICSLOTS2: 985 if (k == K_COND_ELIF && len == strlen(COND_ELIF) && 986 strncmp(line, COND_ELIF, len) == 0) { 987 ifp = ifs + COND_ELIF_INDEX; 988 } else 989 return COND_INVALID; 990 break; 991 case K_COND_ELIFDEF % MAGICSLOTS2: 992 if (k == K_COND_ELIFDEF && len == strlen(COND_ELIFDEF) && 993 strncmp(line, COND_ELIFDEF, len) == 0) { 994 ifp = ifs + COND_ELIFDEF_INDEX; 995 } else 996 return COND_INVALID; 997 break; 998 case K_COND_ELIFNDEF % MAGICSLOTS2: 999 if (k == K_COND_ELIFNDEF && len == strlen(COND_ELIFNDEF) && 1000 strncmp(line, COND_ELIFNDEF, len) == 0) { 1001 ifp = ifs + COND_ELIFNDEF_INDEX; 1002 } else 1003 return COND_INVALID; 1004 break; 1005 case K_COND_ELIFMAKE % MAGICSLOTS2: 1006 if (k == K_COND_ELIFMAKE && len == strlen(COND_ELIFMAKE) && 1007 strncmp(line, COND_ELIFMAKE, len) == 0) { 1008 ifp = ifs + COND_ELIFMAKE_INDEX; 1009 } else 1010 return COND_INVALID; 1011 break; 1012 case K_COND_ELIFNMAKE % MAGICSLOTS2: 1013 if (k == K_COND_ELIFNMAKE && len == strlen(COND_ELIFNMAKE) && 1014 strncmp(line, COND_ELIFNMAKE, len) == 0) { 1015 ifp = ifs + COND_ELIFNMAKE_INDEX; 1016 } else 1017 return COND_INVALID; 1018 break; 1019 case K_COND_ELSE % MAGICSLOTS2: 1020 /* valid conditional whose value is the inverse 1021 * of the previous if we parsed. */ 1022 if (k == K_COND_ELSE && len == strlen(COND_ELSE) && 1023 strncmp(line, COND_ELSE, len) == 0) { 1024 if (condTop == MAXIF) { 1025 Parse_Error(level, "if-less else"); 1026 return COND_INVALID; 1027 } else if (skipIfLevel == 0) { 1028 value = !condStack[condTop].value; 1029 ifp = ifs + COND_ELSE_INDEX; 1030 } else 1031 return COND_SKIP; 1032 } else 1033 return COND_INVALID; 1034 break; 1035 case K_COND_ENDIF % MAGICSLOTS2: 1036 if (k == K_COND_ENDIF && len == strlen(COND_ENDIF) && 1037 strncmp(line, COND_ENDIF, len) == 0) { 1038 /* End of a conditional section. If skipIfLevel is 1039 * non-zero, that conditional was skipped, so lines 1040 * following it should also be skipped. Hence, we 1041 * return COND_SKIP. Otherwise, the conditional was 1042 * read so succeeding lines should be parsed (think 1043 * about it...) so we return COND_PARSE, unless this 1044 * endif isn't paired with a decent if. */ 1045 if (skipIfLevel != 0) { 1046 skipIfLevel--; 1047 return COND_SKIP; 1048 } else { 1049 if (condTop == MAXIF) { 1050 Parse_Error(level, "if-less endif"); 1051 return COND_INVALID; 1052 } else { 1053 skipLine = false; 1054 condTop++; 1055 return COND_PARSE; 1056 } 1057 } 1058 } else 1059 return COND_INVALID; 1060 break; 1061 1062 /* Recognize other keywords there, to simplify parser's task */ 1063 case K_COND_FOR % MAGICSLOTS2: 1064 if (k == K_COND_FOR && len == strlen(COND_FOR) && 1065 strncmp(line, COND_FOR, len) == 0) 1066 return COND_ISFOR; 1067 else 1068 return COND_INVALID; 1069 case K_COND_UNDEF % MAGICSLOTS2: 1070 if (k == K_COND_UNDEF && len == strlen(COND_UNDEF) && 1071 strncmp(line, COND_UNDEF, len) == 0) 1072 return COND_ISUNDEF; 1073 else 1074 return COND_INVALID; 1075 case K_COND_POISON % MAGICSLOTS2: 1076 if (k == K_COND_POISON && len == strlen(COND_POISON) && 1077 strncmp(line, COND_POISON, len) == 0) 1078 return COND_ISPOISON; 1079 else 1080 return COND_INVALID; 1081 case K_COND_INCLUDE % MAGICSLOTS2: 1082 if (k == K_COND_INCLUDE && len == strlen(COND_INCLUDE) && 1083 strncmp(line, COND_INCLUDE, len) == 0) 1084 return COND_ISINCLUDE; 1085 else 1086 return COND_INVALID; 1087 default: 1088 /* Not a valid conditional type. No error... */ 1089 return COND_INVALID; 1090 } 1091 1092 if (ifp->isElse) { 1093 if (condTop == MAXIF) { 1094 Parse_Error(level, "if-less elif"); 1095 return COND_INVALID; 1096 } else if (skipIfLevel != 0 || condStack[condTop].value) { 1097 /* 1098 * Skip if we're meant to or is an else-type 1099 * conditional and previous corresponding one was 1100 * evaluated to true. 1101 */ 1102 skipLine = true; 1103 return COND_SKIP; 1104 } 1105 } else if (skipLine) { 1106 /* Don't even try to evaluate a conditional that's not an else 1107 * if we're skipping things... */ 1108 skipIfLevel++; 1109 return COND_SKIP; 1110 } else 1111 condTop--; 1112 1113 if (condTop < 0) { 1114 /* This is the one case where we can definitely proclaim a fatal 1115 * error. If we don't, we're hosed. */ 1116 Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", 1117 MAXIF); 1118 condTop = 0; 1119 return COND_INVALID; 1120 } 1121 1122 if (ifp->defProc) { 1123 /* Initialize file-global variables for parsing. */ 1124 condDefProc = ifp->defProc; 1125 condInvert = ifp->doNot; 1126 1127 line += len; 1128 1129 while (*line == ' ' || *line == '\t') 1130 line++; 1131 1132 condExpr = line; 1133 condPushBack = None; 1134 1135 switch (CondE(true)) { 1136 case True: 1137 if (CondToken(true) == EndOfFile) { 1138 value = true; 1139 break; 1140 } 1141 goto err; 1142 /* FALLTHROUGH */ 1143 case False: 1144 if (CondToken(true) == EndOfFile) { 1145 value = false; 1146 break; 1147 } 1148 /* FALLTHROUGH */ 1149 case Err: 1150 err: 1151 Parse_Error(level, "Malformed conditional (%s)", line); 1152 return COND_INVALID; 1153 default: 1154 break; 1155 } 1156 } 1157 1158 condStack[condTop].value = value; 1159 Parse_FillLocation(&condStack[condTop].origin); 1160 skipLine = !value; 1161 return value ? COND_PARSE : COND_SKIP; 1162 } 1163 1164 void 1165 Cond_End(void) 1166 { 1167 int i; 1168 1169 if (condTop != MAXIF) { 1170 Parse_Error(PARSE_FATAL, "%s%d open conditional%s", 1171 condTop == 0 ? "at least ": "", MAXIF-condTop, 1172 MAXIF-condTop == 1 ? "" : "s"); 1173 for (i = MAXIF-1; i >= condTop; i--) { 1174 fprintf(stderr, "\t(%s:%lu)\n", 1175 condStack[i].origin.fname, 1176 condStack[i].origin.lineno); 1177 } 1178 } 1179 condTop = MAXIF; 1180 } 1181