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