1319884Ssjg/* $NetBSD: cond.c,v 1.75 2017/04/16 20:59:04 riastradh Exp $ */ 2236769Sobrien 3236769Sobrien/* 4236769Sobrien * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 5236769Sobrien * All rights reserved. 6236769Sobrien * 7236769Sobrien * This code is derived from software contributed to Berkeley by 8236769Sobrien * Adam de Boor. 9236769Sobrien * 10236769Sobrien * Redistribution and use in source and binary forms, with or without 11236769Sobrien * modification, are permitted provided that the following conditions 12236769Sobrien * are met: 13236769Sobrien * 1. Redistributions of source code must retain the above copyright 14236769Sobrien * notice, this list of conditions and the following disclaimer. 15236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright 16236769Sobrien * notice, this list of conditions and the following disclaimer in the 17236769Sobrien * documentation and/or other materials provided with the distribution. 18236769Sobrien * 3. Neither the name of the University nor the names of its contributors 19236769Sobrien * may be used to endorse or promote products derived from this software 20236769Sobrien * without specific prior written permission. 21236769Sobrien * 22236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23236769Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24236769Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25236769Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26236769Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27236769Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28236769Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29236769Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30236769Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31236769Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32236769Sobrien * SUCH DAMAGE. 33236769Sobrien */ 34236769Sobrien 35236769Sobrien/* 36236769Sobrien * Copyright (c) 1988, 1989 by Adam de Boor 37236769Sobrien * Copyright (c) 1989 by Berkeley Softworks 38236769Sobrien * All rights reserved. 39236769Sobrien * 40236769Sobrien * This code is derived from software contributed to Berkeley by 41236769Sobrien * Adam de Boor. 42236769Sobrien * 43236769Sobrien * Redistribution and use in source and binary forms, with or without 44236769Sobrien * modification, are permitted provided that the following conditions 45236769Sobrien * are met: 46236769Sobrien * 1. Redistributions of source code must retain the above copyright 47236769Sobrien * notice, this list of conditions and the following disclaimer. 48236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright 49236769Sobrien * notice, this list of conditions and the following disclaimer in the 50236769Sobrien * documentation and/or other materials provided with the distribution. 51236769Sobrien * 3. All advertising materials mentioning features or use of this software 52236769Sobrien * must display the following acknowledgement: 53236769Sobrien * This product includes software developed by the University of 54236769Sobrien * California, Berkeley and its contributors. 55236769Sobrien * 4. Neither the name of the University nor the names of its contributors 56236769Sobrien * may be used to endorse or promote products derived from this software 57236769Sobrien * without specific prior written permission. 58236769Sobrien * 59236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60236769Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61236769Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62236769Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63236769Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64236769Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65236769Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66236769Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67236769Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68236769Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69236769Sobrien * SUCH DAMAGE. 70236769Sobrien */ 71236769Sobrien 72236769Sobrien#ifndef MAKE_NATIVE 73319884Ssjgstatic char rcsid[] = "$NetBSD: cond.c,v 1.75 2017/04/16 20:59:04 riastradh Exp $"; 74236769Sobrien#else 75236769Sobrien#include <sys/cdefs.h> 76236769Sobrien#ifndef lint 77236769Sobrien#if 0 78236769Sobrienstatic char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94"; 79236769Sobrien#else 80319884Ssjg__RCSID("$NetBSD: cond.c,v 1.75 2017/04/16 20:59:04 riastradh Exp $"); 81236769Sobrien#endif 82236769Sobrien#endif /* not lint */ 83236769Sobrien#endif 84236769Sobrien 85236769Sobrien/*- 86236769Sobrien * cond.c -- 87236769Sobrien * Functions to handle conditionals in a makefile. 88236769Sobrien * 89236769Sobrien * Interface: 90236769Sobrien * Cond_Eval Evaluate the conditional in the passed line. 91236769Sobrien * 92236769Sobrien */ 93236769Sobrien 94319884Ssjg#include <assert.h> 95236769Sobrien#include <ctype.h> 96236769Sobrien#include <errno.h> /* For strtoul() error checking */ 97236769Sobrien 98236769Sobrien#include "make.h" 99236769Sobrien#include "hash.h" 100236769Sobrien#include "dir.h" 101236769Sobrien#include "buf.h" 102236769Sobrien 103236769Sobrien/* 104236769Sobrien * The parsing of conditional expressions is based on this grammar: 105236769Sobrien * E -> F || E 106236769Sobrien * E -> F 107236769Sobrien * F -> T && F 108236769Sobrien * F -> T 109236769Sobrien * T -> defined(variable) 110236769Sobrien * T -> make(target) 111236769Sobrien * T -> exists(file) 112236769Sobrien * T -> empty(varspec) 113236769Sobrien * T -> target(name) 114236769Sobrien * T -> commands(name) 115236769Sobrien * T -> symbol 116236769Sobrien * T -> $(varspec) op value 117236769Sobrien * T -> $(varspec) == "string" 118236769Sobrien * T -> $(varspec) != "string" 119236769Sobrien * T -> "string" 120236769Sobrien * T -> ( E ) 121236769Sobrien * T -> ! T 122236769Sobrien * op -> == | != | > | < | >= | <= 123236769Sobrien * 124236769Sobrien * 'symbol' is some other symbol to which the default function (condDefProc) 125236769Sobrien * is applied. 126236769Sobrien * 127236769Sobrien * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) 128236769Sobrien * will return TOK_AND for '&' and '&&', TOK_OR for '|' and '||', 129236769Sobrien * TOK_NOT for '!', TOK_LPAREN for '(', TOK_RPAREN for ')' and will evaluate 130236769Sobrien * the other terminal symbols, using either the default function or the 131236769Sobrien * function given in the terminal, and return the result as either TOK_TRUE 132236769Sobrien * or TOK_FALSE. 133236769Sobrien * 134236769Sobrien * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons. 135236769Sobrien * 136236769Sobrien * All Non-Terminal functions (CondE, CondF and CondT) return TOK_ERROR on 137236769Sobrien * error. 138236769Sobrien */ 139236769Sobrientypedef enum { 140236769Sobrien TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT, 141236769Sobrien TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR 142236769Sobrien} Token; 143236769Sobrien 144236769Sobrien/*- 145236769Sobrien * Structures to handle elegantly the different forms of #if's. The 146236769Sobrien * last two fields are stored in condInvert and condDefProc, respectively. 147236769Sobrien */ 148236769Sobrienstatic void CondPushBack(Token); 149236769Sobrienstatic int CondGetArg(char **, char **, const char *); 150236769Sobrienstatic Boolean CondDoDefined(int, const char *); 151236769Sobrienstatic int CondStrMatch(const void *, const void *); 152236769Sobrienstatic Boolean CondDoMake(int, const char *); 153236769Sobrienstatic Boolean CondDoExists(int, const char *); 154236769Sobrienstatic Boolean CondDoTarget(int, const char *); 155236769Sobrienstatic Boolean CondDoCommands(int, const char *); 156236769Sobrienstatic Boolean CondCvtArg(char *, double *); 157236769Sobrienstatic Token CondToken(Boolean); 158236769Sobrienstatic Token CondT(Boolean); 159236769Sobrienstatic Token CondF(Boolean); 160236769Sobrienstatic Token CondE(Boolean); 161236769Sobrienstatic int do_Cond_EvalExpression(Boolean *); 162236769Sobrien 163236769Sobrienstatic const struct If { 164236769Sobrien const char *form; /* Form of if */ 165236769Sobrien int formlen; /* Length of form */ 166236769Sobrien Boolean doNot; /* TRUE if default function should be negated */ 167236769Sobrien Boolean (*defProc)(int, const char *); /* Default function to apply */ 168236769Sobrien} ifs[] = { 169236769Sobrien { "def", 3, FALSE, CondDoDefined }, 170236769Sobrien { "ndef", 4, TRUE, CondDoDefined }, 171236769Sobrien { "make", 4, FALSE, CondDoMake }, 172236769Sobrien { "nmake", 5, TRUE, CondDoMake }, 173236769Sobrien { "", 0, FALSE, CondDoDefined }, 174236769Sobrien { NULL, 0, FALSE, NULL } 175236769Sobrien}; 176236769Sobrien 177236769Sobrienstatic const struct If *if_info; /* Info for current statement */ 178236769Sobrienstatic char *condExpr; /* The expression to parse */ 179236769Sobrienstatic Token condPushBack=TOK_NONE; /* Single push-back token used in 180236769Sobrien * parsing */ 181236769Sobrien 182236769Sobrienstatic unsigned int cond_depth = 0; /* current .if nesting level */ 183236769Sobrienstatic unsigned int cond_min_depth = 0; /* depth at makefile open */ 184236769Sobrien 185282740Ssjg/* 186282740Ssjg * Indicate when we should be strict about lhs of comparisons. 187282740Ssjg * TRUE when Cond_EvalExpression is called from Cond_Eval (.if etc) 188282740Ssjg * FALSE when Cond_EvalExpression is called from var.c:ApplyModifiers 189282740Ssjg * since lhs is already expanded and we cannot tell if 190282740Ssjg * it was a variable reference or not. 191282740Ssjg */ 192282740Ssjgstatic Boolean lhsStrict; 193282740Ssjg 194236769Sobrienstatic int 195236769Sobrienistoken(const char *str, const char *tok, size_t len) 196236769Sobrien{ 197236769Sobrien return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]); 198236769Sobrien} 199236769Sobrien 200236769Sobrien/*- 201236769Sobrien *----------------------------------------------------------------------- 202236769Sobrien * CondPushBack -- 203236769Sobrien * Push back the most recent token read. We only need one level of 204236769Sobrien * this, so the thing is just stored in 'condPushback'. 205236769Sobrien * 206236769Sobrien * Input: 207236769Sobrien * t Token to push back into the "stream" 208236769Sobrien * 209236769Sobrien * Results: 210236769Sobrien * None. 211236769Sobrien * 212236769Sobrien * Side Effects: 213236769Sobrien * condPushback is overwritten. 214236769Sobrien * 215236769Sobrien *----------------------------------------------------------------------- 216236769Sobrien */ 217236769Sobrienstatic void 218236769SobrienCondPushBack(Token t) 219236769Sobrien{ 220236769Sobrien condPushBack = t; 221236769Sobrien} 222236769Sobrien 223236769Sobrien/*- 224236769Sobrien *----------------------------------------------------------------------- 225236769Sobrien * CondGetArg -- 226236769Sobrien * Find the argument of a built-in function. 227236769Sobrien * 228236769Sobrien * Input: 229236769Sobrien * parens TRUE if arg should be bounded by parens 230236769Sobrien * 231236769Sobrien * Results: 232236769Sobrien * The length of the argument and the address of the argument. 233236769Sobrien * 234236769Sobrien * Side Effects: 235236769Sobrien * The pointer is set to point to the closing parenthesis of the 236236769Sobrien * function call. 237236769Sobrien * 238236769Sobrien *----------------------------------------------------------------------- 239236769Sobrien */ 240236769Sobrienstatic int 241236769SobrienCondGetArg(char **linePtr, char **argPtr, const char *func) 242236769Sobrien{ 243236769Sobrien char *cp; 244236769Sobrien int argLen; 245236769Sobrien Buffer buf; 246236769Sobrien int paren_depth; 247236769Sobrien char ch; 248236769Sobrien 249236769Sobrien cp = *linePtr; 250236769Sobrien if (func != NULL) 251236769Sobrien /* Skip opening '(' - verfied by caller */ 252236769Sobrien cp++; 253236769Sobrien 254236769Sobrien if (*cp == '\0') { 255236769Sobrien /* 256236769Sobrien * No arguments whatsoever. Because 'make' and 'defined' aren't really 257236769Sobrien * "reserved words", we don't print a message. I think this is better 258236769Sobrien * than hitting the user with a warning message every time s/he uses 259236769Sobrien * the word 'make' or 'defined' at the beginning of a symbol... 260236769Sobrien */ 261236769Sobrien *argPtr = NULL; 262236769Sobrien return (0); 263236769Sobrien } 264236769Sobrien 265236769Sobrien while (*cp == ' ' || *cp == '\t') { 266236769Sobrien cp++; 267236769Sobrien } 268236769Sobrien 269236769Sobrien /* 270236769Sobrien * Create a buffer for the argument and start it out at 16 characters 271236769Sobrien * long. Why 16? Why not? 272236769Sobrien */ 273236769Sobrien Buf_Init(&buf, 16); 274236769Sobrien 275236769Sobrien paren_depth = 0; 276236769Sobrien for (;;) { 277236769Sobrien ch = *cp; 278236769Sobrien if (ch == 0 || ch == ' ' || ch == '\t') 279236769Sobrien break; 280236769Sobrien if ((ch == '&' || ch == '|') && paren_depth == 0) 281236769Sobrien break; 282236769Sobrien if (*cp == '$') { 283236769Sobrien /* 284236769Sobrien * Parse the variable spec and install it as part of the argument 285236769Sobrien * if it's valid. We tell Var_Parse to complain on an undefined 286236769Sobrien * variable, so we don't do it too. Nor do we return an error, 287236769Sobrien * though perhaps we should... 288236769Sobrien */ 289236769Sobrien char *cp2; 290236769Sobrien int len; 291236769Sobrien void *freeIt; 292236769Sobrien 293296637Ssjg cp2 = Var_Parse(cp, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES, 294296637Ssjg &len, &freeIt); 295236769Sobrien Buf_AddBytes(&buf, strlen(cp2), cp2); 296296637Ssjg free(freeIt); 297236769Sobrien cp += len; 298236769Sobrien continue; 299236769Sobrien } 300236769Sobrien if (ch == '(') 301236769Sobrien paren_depth++; 302236769Sobrien else 303236769Sobrien if (ch == ')' && --paren_depth < 0) 304236769Sobrien break; 305236769Sobrien Buf_AddByte(&buf, *cp); 306236769Sobrien cp++; 307236769Sobrien } 308236769Sobrien 309236769Sobrien *argPtr = Buf_GetAll(&buf, &argLen); 310236769Sobrien Buf_Destroy(&buf, FALSE); 311236769Sobrien 312236769Sobrien while (*cp == ' ' || *cp == '\t') { 313236769Sobrien cp++; 314236769Sobrien } 315236769Sobrien 316236769Sobrien if (func != NULL && *cp++ != ')') { 317236769Sobrien Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", 318236769Sobrien func); 319236769Sobrien return (0); 320236769Sobrien } 321236769Sobrien 322236769Sobrien *linePtr = cp; 323236769Sobrien return (argLen); 324236769Sobrien} 325236769Sobrien 326236769Sobrien/*- 327236769Sobrien *----------------------------------------------------------------------- 328236769Sobrien * CondDoDefined -- 329236769Sobrien * Handle the 'defined' function for conditionals. 330236769Sobrien * 331236769Sobrien * Results: 332236769Sobrien * TRUE if the given variable is defined. 333236769Sobrien * 334236769Sobrien * Side Effects: 335236769Sobrien * None. 336236769Sobrien * 337236769Sobrien *----------------------------------------------------------------------- 338236769Sobrien */ 339236769Sobrienstatic Boolean 340237578SobrienCondDoDefined(int argLen MAKE_ATTR_UNUSED, const char *arg) 341236769Sobrien{ 342236769Sobrien char *p1; 343236769Sobrien Boolean result; 344236769Sobrien 345236769Sobrien if (Var_Value(arg, VAR_CMD, &p1) != NULL) { 346236769Sobrien result = TRUE; 347236769Sobrien } else { 348236769Sobrien result = FALSE; 349236769Sobrien } 350296637Ssjg 351296637Ssjg free(p1); 352236769Sobrien return (result); 353236769Sobrien} 354236769Sobrien 355236769Sobrien/*- 356236769Sobrien *----------------------------------------------------------------------- 357236769Sobrien * CondStrMatch -- 358236769Sobrien * Front-end for Str_Match so it returns 0 on match and non-zero 359236769Sobrien * on mismatch. Callback function for CondDoMake via Lst_Find 360236769Sobrien * 361236769Sobrien * Results: 362236769Sobrien * 0 if string matches pattern 363236769Sobrien * 364236769Sobrien * Side Effects: 365236769Sobrien * None 366236769Sobrien * 367236769Sobrien *----------------------------------------------------------------------- 368236769Sobrien */ 369236769Sobrienstatic int 370236769SobrienCondStrMatch(const void *string, const void *pattern) 371236769Sobrien{ 372236769Sobrien return(!Str_Match(string, pattern)); 373236769Sobrien} 374236769Sobrien 375236769Sobrien/*- 376236769Sobrien *----------------------------------------------------------------------- 377236769Sobrien * CondDoMake -- 378236769Sobrien * Handle the 'make' function for conditionals. 379236769Sobrien * 380236769Sobrien * Results: 381236769Sobrien * TRUE if the given target is being made. 382236769Sobrien * 383236769Sobrien * Side Effects: 384236769Sobrien * None. 385236769Sobrien * 386236769Sobrien *----------------------------------------------------------------------- 387236769Sobrien */ 388236769Sobrienstatic Boolean 389237578SobrienCondDoMake(int argLen MAKE_ATTR_UNUSED, const char *arg) 390236769Sobrien{ 391236769Sobrien return Lst_Find(create, arg, CondStrMatch) != NULL; 392236769Sobrien} 393236769Sobrien 394236769Sobrien/*- 395236769Sobrien *----------------------------------------------------------------------- 396236769Sobrien * CondDoExists -- 397236769Sobrien * See if the given file exists. 398236769Sobrien * 399236769Sobrien * Results: 400236769Sobrien * TRUE if the file exists and FALSE if it does not. 401236769Sobrien * 402236769Sobrien * Side Effects: 403236769Sobrien * None. 404236769Sobrien * 405236769Sobrien *----------------------------------------------------------------------- 406236769Sobrien */ 407236769Sobrienstatic Boolean 408237578SobrienCondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg) 409236769Sobrien{ 410236769Sobrien Boolean result; 411236769Sobrien char *path; 412236769Sobrien 413236769Sobrien path = Dir_FindFile(arg, dirSearchPath); 414236769Sobrien if (DEBUG(COND)) { 415236769Sobrien fprintf(debug_file, "exists(%s) result is \"%s\"\n", 416236769Sobrien arg, path ? path : ""); 417236769Sobrien } 418236769Sobrien if (path != NULL) { 419236769Sobrien result = TRUE; 420236769Sobrien free(path); 421236769Sobrien } else { 422236769Sobrien result = FALSE; 423236769Sobrien } 424236769Sobrien return (result); 425236769Sobrien} 426236769Sobrien 427236769Sobrien/*- 428236769Sobrien *----------------------------------------------------------------------- 429236769Sobrien * CondDoTarget -- 430236769Sobrien * See if the given node exists and is an actual target. 431236769Sobrien * 432236769Sobrien * Results: 433236769Sobrien * TRUE if the node exists as a target and FALSE if it does not. 434236769Sobrien * 435236769Sobrien * Side Effects: 436236769Sobrien * None. 437236769Sobrien * 438236769Sobrien *----------------------------------------------------------------------- 439236769Sobrien */ 440236769Sobrienstatic Boolean 441237578SobrienCondDoTarget(int argLen MAKE_ATTR_UNUSED, const char *arg) 442236769Sobrien{ 443236769Sobrien GNode *gn; 444236769Sobrien 445236769Sobrien gn = Targ_FindNode(arg, TARG_NOCREATE); 446236769Sobrien return (gn != NULL) && !OP_NOP(gn->type); 447236769Sobrien} 448236769Sobrien 449236769Sobrien/*- 450236769Sobrien *----------------------------------------------------------------------- 451236769Sobrien * CondDoCommands -- 452236769Sobrien * See if the given node exists and is an actual target with commands 453236769Sobrien * associated with it. 454236769Sobrien * 455236769Sobrien * Results: 456236769Sobrien * TRUE if the node exists as a target and has commands associated with 457236769Sobrien * it and FALSE if it does not. 458236769Sobrien * 459236769Sobrien * Side Effects: 460236769Sobrien * None. 461236769Sobrien * 462236769Sobrien *----------------------------------------------------------------------- 463236769Sobrien */ 464236769Sobrienstatic Boolean 465237578SobrienCondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg) 466236769Sobrien{ 467236769Sobrien GNode *gn; 468236769Sobrien 469236769Sobrien gn = Targ_FindNode(arg, TARG_NOCREATE); 470236769Sobrien return (gn != NULL) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands); 471236769Sobrien} 472236769Sobrien 473236769Sobrien/*- 474236769Sobrien *----------------------------------------------------------------------- 475236769Sobrien * CondCvtArg -- 476236769Sobrien * Convert the given number into a double. 477236769Sobrien * We try a base 10 or 16 integer conversion first, if that fails 478236769Sobrien * then we try a floating point conversion instead. 479236769Sobrien * 480236769Sobrien * Results: 481236769Sobrien * Sets 'value' to double value of string. 482236769Sobrien * Returns 'true' if the convertion suceeded 483236769Sobrien * 484236769Sobrien *----------------------------------------------------------------------- 485236769Sobrien */ 486236769Sobrienstatic Boolean 487236769SobrienCondCvtArg(char *str, double *value) 488236769Sobrien{ 489236769Sobrien char *eptr, ech; 490236769Sobrien unsigned long l_val; 491236769Sobrien double d_val; 492236769Sobrien 493236769Sobrien errno = 0; 494291978Ssjg if (!*str) { 495291978Ssjg *value = (double)0; 496291978Ssjg return TRUE; 497291978Ssjg } 498236769Sobrien l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10); 499236769Sobrien ech = *eptr; 500236769Sobrien if (ech == 0 && errno != ERANGE) { 501236769Sobrien d_val = str[0] == '-' ? -(double)-l_val : (double)l_val; 502236769Sobrien } else { 503236769Sobrien if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E') 504236769Sobrien return FALSE; 505236769Sobrien d_val = strtod(str, &eptr); 506236769Sobrien if (*eptr) 507236769Sobrien return FALSE; 508236769Sobrien } 509236769Sobrien 510236769Sobrien *value = d_val; 511236769Sobrien return TRUE; 512236769Sobrien} 513236769Sobrien 514236769Sobrien/*- 515236769Sobrien *----------------------------------------------------------------------- 516236769Sobrien * CondGetString -- 517236769Sobrien * Get a string from a variable reference or an optionally quoted 518236769Sobrien * string. This is called for the lhs and rhs of string compares. 519236769Sobrien * 520236769Sobrien * Results: 521236769Sobrien * Sets freeIt if needed, 522236769Sobrien * Sets quoted if string was quoted, 523236769Sobrien * Returns NULL on error, 524236769Sobrien * else returns string - absent any quotes. 525236769Sobrien * 526236769Sobrien * Side Effects: 527236769Sobrien * Moves condExpr to end of this token. 528236769Sobrien * 529236769Sobrien * 530236769Sobrien *----------------------------------------------------------------------- 531236769Sobrien */ 532236769Sobrien/* coverity:[+alloc : arg-*2] */ 533236769Sobrienstatic char * 534282740SsjgCondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS) 535236769Sobrien{ 536236769Sobrien Buffer buf; 537236769Sobrien char *cp; 538236769Sobrien char *str; 539236769Sobrien int len; 540236769Sobrien int qt; 541236769Sobrien char *start; 542236769Sobrien 543236769Sobrien Buf_Init(&buf, 0); 544236769Sobrien str = NULL; 545236769Sobrien *freeIt = NULL; 546236769Sobrien *quoted = qt = *condExpr == '"' ? 1 : 0; 547236769Sobrien if (qt) 548236769Sobrien condExpr++; 549236769Sobrien for (start = condExpr; *condExpr && str == NULL; condExpr++) { 550236769Sobrien switch (*condExpr) { 551236769Sobrien case '\\': 552236769Sobrien if (condExpr[1] != '\0') { 553236769Sobrien condExpr++; 554236769Sobrien Buf_AddByte(&buf, *condExpr); 555236769Sobrien } 556236769Sobrien break; 557236769Sobrien case '"': 558236769Sobrien if (qt) { 559236769Sobrien condExpr++; /* we don't want the quotes */ 560236769Sobrien goto got_str; 561236769Sobrien } else 562236769Sobrien Buf_AddByte(&buf, *condExpr); /* likely? */ 563236769Sobrien break; 564236769Sobrien case ')': 565236769Sobrien case '!': 566236769Sobrien case '=': 567236769Sobrien case '>': 568236769Sobrien case '<': 569236769Sobrien case ' ': 570236769Sobrien case '\t': 571236769Sobrien if (!qt) 572236769Sobrien goto got_str; 573236769Sobrien else 574236769Sobrien Buf_AddByte(&buf, *condExpr); 575236769Sobrien break; 576236769Sobrien case '$': 577236769Sobrien /* if we are in quotes, then an undefined variable is ok */ 578296637Ssjg str = Var_Parse(condExpr, VAR_CMD, 579296637Ssjg ((!qt && doEval) ? VARF_UNDEFERR : 0) | 580296637Ssjg VARF_WANTRES, &len, freeIt); 581236769Sobrien if (str == var_Error) { 582236769Sobrien if (*freeIt) { 583236769Sobrien free(*freeIt); 584236769Sobrien *freeIt = NULL; 585236769Sobrien } 586236769Sobrien /* 587236769Sobrien * Even if !doEval, we still report syntax errors, which 588236769Sobrien * is what getting var_Error back with !doEval means. 589236769Sobrien */ 590236769Sobrien str = NULL; 591236769Sobrien goto cleanup; 592236769Sobrien } 593236769Sobrien condExpr += len; 594236769Sobrien /* 595236769Sobrien * If the '$' was first char (no quotes), and we are 596236769Sobrien * followed by space, the operator or end of expression, 597236769Sobrien * we are done. 598236769Sobrien */ 599236769Sobrien if ((condExpr == start + len) && 600236769Sobrien (*condExpr == '\0' || 601236769Sobrien isspace((unsigned char) *condExpr) || 602236769Sobrien strchr("!=><)", *condExpr))) { 603236769Sobrien goto cleanup; 604236769Sobrien } 605236769Sobrien /* 606236769Sobrien * Nope, we better copy str to buf 607236769Sobrien */ 608236769Sobrien for (cp = str; *cp; cp++) { 609236769Sobrien Buf_AddByte(&buf, *cp); 610236769Sobrien } 611236769Sobrien if (*freeIt) { 612236769Sobrien free(*freeIt); 613236769Sobrien *freeIt = NULL; 614236769Sobrien } 615236769Sobrien str = NULL; /* not finished yet */ 616236769Sobrien condExpr--; /* don't skip over next char */ 617236769Sobrien break; 618236769Sobrien default: 619282740Ssjg if (strictLHS && !qt && *start != '$' && 620282740Ssjg !isdigit((unsigned char) *start)) { 621282740Ssjg /* lhs must be quoted, a variable reference or number */ 622282740Ssjg if (*freeIt) { 623282740Ssjg free(*freeIt); 624282740Ssjg *freeIt = NULL; 625282740Ssjg } 626282740Ssjg str = NULL; 627282740Ssjg goto cleanup; 628282740Ssjg } 629236769Sobrien Buf_AddByte(&buf, *condExpr); 630236769Sobrien break; 631236769Sobrien } 632236769Sobrien } 633236769Sobrien got_str: 634236769Sobrien str = Buf_GetAll(&buf, NULL); 635236769Sobrien *freeIt = str; 636236769Sobrien cleanup: 637236769Sobrien Buf_Destroy(&buf, FALSE); 638236769Sobrien return str; 639236769Sobrien} 640236769Sobrien 641236769Sobrien/*- 642236769Sobrien *----------------------------------------------------------------------- 643236769Sobrien * CondToken -- 644236769Sobrien * Return the next token from the input. 645236769Sobrien * 646236769Sobrien * Results: 647236769Sobrien * A Token for the next lexical token in the stream. 648236769Sobrien * 649236769Sobrien * Side Effects: 650236769Sobrien * condPushback will be set back to TOK_NONE if it is used. 651236769Sobrien * 652236769Sobrien *----------------------------------------------------------------------- 653236769Sobrien */ 654236769Sobrienstatic Token 655236769Sobriencompare_expression(Boolean doEval) 656236769Sobrien{ 657236769Sobrien Token t; 658236769Sobrien char *lhs; 659236769Sobrien char *rhs; 660236769Sobrien char *op; 661236769Sobrien void *lhsFree; 662236769Sobrien void *rhsFree; 663236769Sobrien Boolean lhsQuoted; 664236769Sobrien Boolean rhsQuoted; 665236769Sobrien double left, right; 666236769Sobrien 667236769Sobrien t = TOK_ERROR; 668236769Sobrien rhs = NULL; 669236769Sobrien lhsFree = rhsFree = FALSE; 670236769Sobrien lhsQuoted = rhsQuoted = FALSE; 671236769Sobrien 672236769Sobrien /* 673236769Sobrien * Parse the variable spec and skip over it, saving its 674236769Sobrien * value in lhs. 675236769Sobrien */ 676282740Ssjg lhs = CondGetString(doEval, &lhsQuoted, &lhsFree, lhsStrict); 677236769Sobrien if (!lhs) 678236769Sobrien goto done; 679236769Sobrien 680236769Sobrien /* 681236769Sobrien * Skip whitespace to get to the operator 682236769Sobrien */ 683236769Sobrien while (isspace((unsigned char) *condExpr)) 684236769Sobrien condExpr++; 685236769Sobrien 686236769Sobrien /* 687236769Sobrien * Make sure the operator is a valid one. If it isn't a 688236769Sobrien * known relational operator, pretend we got a 689236769Sobrien * != 0 comparison. 690236769Sobrien */ 691236769Sobrien op = condExpr; 692236769Sobrien switch (*condExpr) { 693236769Sobrien case '!': 694236769Sobrien case '=': 695236769Sobrien case '<': 696236769Sobrien case '>': 697236769Sobrien if (condExpr[1] == '=') { 698236769Sobrien condExpr += 2; 699236769Sobrien } else { 700236769Sobrien condExpr += 1; 701236769Sobrien } 702236769Sobrien break; 703236769Sobrien default: 704236769Sobrien if (!doEval) { 705236769Sobrien t = TOK_FALSE; 706236769Sobrien goto done; 707236769Sobrien } 708236769Sobrien /* For .ifxxx "..." check for non-empty string. */ 709236769Sobrien if (lhsQuoted) { 710236769Sobrien t = lhs[0] != 0; 711236769Sobrien goto done; 712236769Sobrien } 713236769Sobrien /* For .ifxxx <number> compare against zero */ 714236769Sobrien if (CondCvtArg(lhs, &left)) { 715236769Sobrien t = left != 0.0; 716236769Sobrien goto done; 717236769Sobrien } 718236769Sobrien /* For .if ${...} check for non-empty string (defProc is ifdef). */ 719236769Sobrien if (if_info->form[0] == 0) { 720236769Sobrien t = lhs[0] != 0; 721236769Sobrien goto done; 722236769Sobrien } 723236769Sobrien /* Otherwise action default test ... */ 724236769Sobrien t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot; 725236769Sobrien goto done; 726236769Sobrien } 727236769Sobrien 728236769Sobrien while (isspace((unsigned char)*condExpr)) 729236769Sobrien condExpr++; 730236769Sobrien 731236769Sobrien if (*condExpr == '\0') { 732236769Sobrien Parse_Error(PARSE_WARNING, 733236769Sobrien "Missing right-hand-side of operator"); 734236769Sobrien goto done; 735236769Sobrien } 736236769Sobrien 737282740Ssjg rhs = CondGetString(doEval, &rhsQuoted, &rhsFree, FALSE); 738236769Sobrien if (!rhs) 739236769Sobrien goto done; 740236769Sobrien 741236769Sobrien if (rhsQuoted || lhsQuoted) { 742236769Sobriendo_string_compare: 743236769Sobrien if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { 744236769Sobrien Parse_Error(PARSE_WARNING, 745236769Sobrien "String comparison operator should be either == or !="); 746236769Sobrien goto done; 747236769Sobrien } 748236769Sobrien 749236769Sobrien if (DEBUG(COND)) { 750236769Sobrien fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", 751236769Sobrien lhs, rhs, op); 752236769Sobrien } 753236769Sobrien /* 754236769Sobrien * Null-terminate rhs and perform the comparison. 755236769Sobrien * t is set to the result. 756236769Sobrien */ 757236769Sobrien if (*op == '=') { 758236769Sobrien t = strcmp(lhs, rhs) == 0; 759236769Sobrien } else { 760236769Sobrien t = strcmp(lhs, rhs) != 0; 761236769Sobrien } 762236769Sobrien } else { 763236769Sobrien /* 764236769Sobrien * rhs is either a float or an integer. Convert both the 765236769Sobrien * lhs and the rhs to a double and compare the two. 766236769Sobrien */ 767236769Sobrien 768236769Sobrien if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right)) 769236769Sobrien goto do_string_compare; 770236769Sobrien 771236769Sobrien if (DEBUG(COND)) { 772236769Sobrien fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left, 773236769Sobrien right, op); 774236769Sobrien } 775236769Sobrien switch(op[0]) { 776236769Sobrien case '!': 777236769Sobrien if (op[1] != '=') { 778236769Sobrien Parse_Error(PARSE_WARNING, 779236769Sobrien "Unknown operator"); 780236769Sobrien goto done; 781236769Sobrien } 782236769Sobrien t = (left != right); 783236769Sobrien break; 784236769Sobrien case '=': 785236769Sobrien if (op[1] != '=') { 786236769Sobrien Parse_Error(PARSE_WARNING, 787236769Sobrien "Unknown operator"); 788236769Sobrien goto done; 789236769Sobrien } 790236769Sobrien t = (left == right); 791236769Sobrien break; 792236769Sobrien case '<': 793236769Sobrien if (op[1] == '=') { 794236769Sobrien t = (left <= right); 795236769Sobrien } else { 796236769Sobrien t = (left < right); 797236769Sobrien } 798236769Sobrien break; 799236769Sobrien case '>': 800236769Sobrien if (op[1] == '=') { 801236769Sobrien t = (left >= right); 802236769Sobrien } else { 803236769Sobrien t = (left > right); 804236769Sobrien } 805236769Sobrien break; 806236769Sobrien } 807236769Sobrien } 808236769Sobrien 809236769Sobriendone: 810296637Ssjg free(lhsFree); 811296637Ssjg free(rhsFree); 812236769Sobrien return t; 813236769Sobrien} 814236769Sobrien 815236769Sobrienstatic int 816237578Sobrienget_mpt_arg(char **linePtr, char **argPtr, const char *func MAKE_ATTR_UNUSED) 817236769Sobrien{ 818236769Sobrien /* 819236769Sobrien * Use Var_Parse to parse the spec in parens and return 820236769Sobrien * TOK_TRUE if the resulting string is empty. 821236769Sobrien */ 822236769Sobrien int length; 823236769Sobrien void *freeIt; 824236769Sobrien char *val; 825236769Sobrien char *cp = *linePtr; 826236769Sobrien 827236769Sobrien /* We do all the work here and return the result as the length */ 828236769Sobrien *argPtr = NULL; 829236769Sobrien 830296637Ssjg val = Var_Parse(cp - 1, VAR_CMD, VARF_WANTRES, &length, &freeIt); 831236769Sobrien /* 832236769Sobrien * Advance *linePtr to beyond the closing ). Note that 833236769Sobrien * we subtract one because 'length' is calculated from 'cp - 1'. 834236769Sobrien */ 835236769Sobrien *linePtr = cp - 1 + length; 836236769Sobrien 837236769Sobrien if (val == var_Error) { 838236769Sobrien free(freeIt); 839236769Sobrien return -1; 840236769Sobrien } 841236769Sobrien 842236769Sobrien /* A variable is empty when it just contains spaces... 4/15/92, christos */ 843236769Sobrien while (isspace(*(unsigned char *)val)) 844236769Sobrien val++; 845236769Sobrien 846236769Sobrien /* 847236769Sobrien * For consistency with the other functions we can't generate the 848236769Sobrien * true/false here. 849236769Sobrien */ 850236769Sobrien length = *val ? 2 : 1; 851296637Ssjg free(freeIt); 852236769Sobrien return length; 853236769Sobrien} 854236769Sobrien 855236769Sobrienstatic Boolean 856237578SobrienCondDoEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED) 857236769Sobrien{ 858236769Sobrien return arglen == 1; 859236769Sobrien} 860236769Sobrien 861236769Sobrienstatic Token 862236769Sobriencompare_function(Boolean doEval) 863236769Sobrien{ 864236769Sobrien static const struct fn_def { 865236769Sobrien const char *fn_name; 866236769Sobrien int fn_name_len; 867236769Sobrien int (*fn_getarg)(char **, char **, const char *); 868236769Sobrien Boolean (*fn_proc)(int, const char *); 869236769Sobrien } fn_defs[] = { 870236769Sobrien { "defined", 7, CondGetArg, CondDoDefined }, 871236769Sobrien { "make", 4, CondGetArg, CondDoMake }, 872236769Sobrien { "exists", 6, CondGetArg, CondDoExists }, 873236769Sobrien { "empty", 5, get_mpt_arg, CondDoEmpty }, 874236769Sobrien { "target", 6, CondGetArg, CondDoTarget }, 875236769Sobrien { "commands", 8, CondGetArg, CondDoCommands }, 876236769Sobrien { NULL, 0, NULL, NULL }, 877236769Sobrien }; 878236769Sobrien const struct fn_def *fn_def; 879236769Sobrien Token t; 880236769Sobrien char *arg = NULL; 881236769Sobrien int arglen; 882236769Sobrien char *cp = condExpr; 883236769Sobrien char *cp1; 884236769Sobrien 885236769Sobrien for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) { 886236769Sobrien if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len)) 887236769Sobrien continue; 888236769Sobrien cp += fn_def->fn_name_len; 889236769Sobrien /* There can only be whitespace before the '(' */ 890236769Sobrien while (isspace(*(unsigned char *)cp)) 891236769Sobrien cp++; 892236769Sobrien if (*cp != '(') 893236769Sobrien break; 894236769Sobrien 895236769Sobrien arglen = fn_def->fn_getarg(&cp, &arg, fn_def->fn_name); 896236769Sobrien if (arglen <= 0) { 897236769Sobrien condExpr = cp; 898236769Sobrien return arglen < 0 ? TOK_ERROR : TOK_FALSE; 899236769Sobrien } 900236769Sobrien /* Evaluate the argument using the required function. */ 901236769Sobrien t = !doEval || fn_def->fn_proc(arglen, arg); 902296637Ssjg free(arg); 903236769Sobrien condExpr = cp; 904236769Sobrien return t; 905236769Sobrien } 906236769Sobrien 907236769Sobrien /* Push anything numeric through the compare expression */ 908236769Sobrien cp = condExpr; 909236769Sobrien if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0])) 910236769Sobrien return compare_expression(doEval); 911236769Sobrien 912236769Sobrien /* 913236769Sobrien * Most likely we have a naked token to apply the default function to. 914236769Sobrien * However ".if a == b" gets here when the "a" is unquoted and doesn't 915236769Sobrien * start with a '$'. This surprises people. 916236769Sobrien * If what follows the function argument is a '=' or '!' then the syntax 917236769Sobrien * would be invalid if we did "defined(a)" - so instead treat as an 918236769Sobrien * expression. 919236769Sobrien */ 920236769Sobrien arglen = CondGetArg(&cp, &arg, NULL); 921236769Sobrien for (cp1 = cp; isspace(*(unsigned char *)cp1); cp1++) 922236769Sobrien continue; 923236769Sobrien if (*cp1 == '=' || *cp1 == '!') 924236769Sobrien return compare_expression(doEval); 925236769Sobrien condExpr = cp; 926236769Sobrien 927236769Sobrien /* 928236769Sobrien * Evaluate the argument using the default function. 929236769Sobrien * This path always treats .if as .ifdef. To get here the character 930236769Sobrien * after .if must have been taken literally, so the argument cannot 931236769Sobrien * be empty - even if it contained a variable expansion. 932236769Sobrien */ 933236769Sobrien t = !doEval || if_info->defProc(arglen, arg) != if_info->doNot; 934296637Ssjg free(arg); 935236769Sobrien return t; 936236769Sobrien} 937236769Sobrien 938236769Sobrienstatic Token 939236769SobrienCondToken(Boolean doEval) 940236769Sobrien{ 941236769Sobrien Token t; 942236769Sobrien 943236769Sobrien t = condPushBack; 944236769Sobrien if (t != TOK_NONE) { 945236769Sobrien condPushBack = TOK_NONE; 946236769Sobrien return t; 947236769Sobrien } 948236769Sobrien 949236769Sobrien while (*condExpr == ' ' || *condExpr == '\t') { 950236769Sobrien condExpr++; 951236769Sobrien } 952236769Sobrien 953236769Sobrien switch (*condExpr) { 954236769Sobrien 955236769Sobrien case '(': 956236769Sobrien condExpr++; 957236769Sobrien return TOK_LPAREN; 958236769Sobrien 959236769Sobrien case ')': 960236769Sobrien condExpr++; 961236769Sobrien return TOK_RPAREN; 962236769Sobrien 963236769Sobrien case '|': 964236769Sobrien if (condExpr[1] == '|') { 965236769Sobrien condExpr++; 966236769Sobrien } 967236769Sobrien condExpr++; 968236769Sobrien return TOK_OR; 969236769Sobrien 970236769Sobrien case '&': 971236769Sobrien if (condExpr[1] == '&') { 972236769Sobrien condExpr++; 973236769Sobrien } 974236769Sobrien condExpr++; 975236769Sobrien return TOK_AND; 976236769Sobrien 977236769Sobrien case '!': 978236769Sobrien condExpr++; 979236769Sobrien return TOK_NOT; 980236769Sobrien 981236769Sobrien case '#': 982236769Sobrien case '\n': 983236769Sobrien case '\0': 984236769Sobrien return TOK_EOF; 985236769Sobrien 986236769Sobrien case '"': 987236769Sobrien case '$': 988236769Sobrien return compare_expression(doEval); 989236769Sobrien 990236769Sobrien default: 991236769Sobrien return compare_function(doEval); 992236769Sobrien } 993236769Sobrien} 994236769Sobrien 995236769Sobrien/*- 996236769Sobrien *----------------------------------------------------------------------- 997236769Sobrien * CondT -- 998236769Sobrien * Parse a single term in the expression. This consists of a terminal 999236769Sobrien * symbol or TOK_NOT and a terminal symbol (not including the binary 1000236769Sobrien * operators): 1001236769Sobrien * T -> defined(variable) | make(target) | exists(file) | symbol 1002236769Sobrien * T -> ! T | ( E ) 1003236769Sobrien * 1004236769Sobrien * Results: 1005236769Sobrien * TOK_TRUE, TOK_FALSE or TOK_ERROR. 1006236769Sobrien * 1007236769Sobrien * Side Effects: 1008236769Sobrien * Tokens are consumed. 1009236769Sobrien * 1010236769Sobrien *----------------------------------------------------------------------- 1011236769Sobrien */ 1012236769Sobrienstatic Token 1013236769SobrienCondT(Boolean doEval) 1014236769Sobrien{ 1015236769Sobrien Token t; 1016236769Sobrien 1017236769Sobrien t = CondToken(doEval); 1018236769Sobrien 1019236769Sobrien if (t == TOK_EOF) { 1020236769Sobrien /* 1021236769Sobrien * If we reached the end of the expression, the expression 1022236769Sobrien * is malformed... 1023236769Sobrien */ 1024236769Sobrien t = TOK_ERROR; 1025236769Sobrien } else if (t == TOK_LPAREN) { 1026236769Sobrien /* 1027236769Sobrien * T -> ( E ) 1028236769Sobrien */ 1029236769Sobrien t = CondE(doEval); 1030236769Sobrien if (t != TOK_ERROR) { 1031236769Sobrien if (CondToken(doEval) != TOK_RPAREN) { 1032236769Sobrien t = TOK_ERROR; 1033236769Sobrien } 1034236769Sobrien } 1035236769Sobrien } else if (t == TOK_NOT) { 1036236769Sobrien t = CondT(doEval); 1037236769Sobrien if (t == TOK_TRUE) { 1038236769Sobrien t = TOK_FALSE; 1039236769Sobrien } else if (t == TOK_FALSE) { 1040236769Sobrien t = TOK_TRUE; 1041236769Sobrien } 1042236769Sobrien } 1043236769Sobrien return (t); 1044236769Sobrien} 1045236769Sobrien 1046236769Sobrien/*- 1047236769Sobrien *----------------------------------------------------------------------- 1048236769Sobrien * CondF -- 1049236769Sobrien * Parse a conjunctive factor (nice name, wot?) 1050236769Sobrien * F -> T && F | T 1051236769Sobrien * 1052236769Sobrien * Results: 1053236769Sobrien * TOK_TRUE, TOK_FALSE or TOK_ERROR 1054236769Sobrien * 1055236769Sobrien * Side Effects: 1056236769Sobrien * Tokens are consumed. 1057236769Sobrien * 1058236769Sobrien *----------------------------------------------------------------------- 1059236769Sobrien */ 1060236769Sobrienstatic Token 1061236769SobrienCondF(Boolean doEval) 1062236769Sobrien{ 1063236769Sobrien Token l, o; 1064236769Sobrien 1065236769Sobrien l = CondT(doEval); 1066236769Sobrien if (l != TOK_ERROR) { 1067236769Sobrien o = CondToken(doEval); 1068236769Sobrien 1069236769Sobrien if (o == TOK_AND) { 1070236769Sobrien /* 1071236769Sobrien * F -> T && F 1072236769Sobrien * 1073236769Sobrien * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we have to 1074236769Sobrien * parse the r.h.s. anyway (to throw it away). 1075236769Sobrien * If T is TOK_TRUE, the result is the r.h.s., be it an TOK_ERROR or no. 1076236769Sobrien */ 1077236769Sobrien if (l == TOK_TRUE) { 1078236769Sobrien l = CondF(doEval); 1079236769Sobrien } else { 1080236769Sobrien (void)CondF(FALSE); 1081236769Sobrien } 1082236769Sobrien } else { 1083236769Sobrien /* 1084236769Sobrien * F -> T 1085236769Sobrien */ 1086236769Sobrien CondPushBack(o); 1087236769Sobrien } 1088236769Sobrien } 1089236769Sobrien return (l); 1090236769Sobrien} 1091236769Sobrien 1092236769Sobrien/*- 1093236769Sobrien *----------------------------------------------------------------------- 1094236769Sobrien * CondE -- 1095236769Sobrien * Main expression production. 1096236769Sobrien * E -> F || E | F 1097236769Sobrien * 1098236769Sobrien * Results: 1099236769Sobrien * TOK_TRUE, TOK_FALSE or TOK_ERROR. 1100236769Sobrien * 1101236769Sobrien * Side Effects: 1102236769Sobrien * Tokens are, of course, consumed. 1103236769Sobrien * 1104236769Sobrien *----------------------------------------------------------------------- 1105236769Sobrien */ 1106236769Sobrienstatic Token 1107236769SobrienCondE(Boolean doEval) 1108236769Sobrien{ 1109236769Sobrien Token l, o; 1110236769Sobrien 1111236769Sobrien l = CondF(doEval); 1112236769Sobrien if (l != TOK_ERROR) { 1113236769Sobrien o = CondToken(doEval); 1114236769Sobrien 1115236769Sobrien if (o == TOK_OR) { 1116236769Sobrien /* 1117236769Sobrien * E -> F || E 1118236769Sobrien * 1119236769Sobrien * A similar thing occurs for ||, except that here we make sure 1120236769Sobrien * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s. 1121236769Sobrien * Once again, if l is TOK_FALSE, the result is the r.h.s. and once 1122236769Sobrien * again if l is TOK_TRUE, we parse the r.h.s. to throw it away. 1123236769Sobrien */ 1124236769Sobrien if (l == TOK_FALSE) { 1125236769Sobrien l = CondE(doEval); 1126236769Sobrien } else { 1127236769Sobrien (void)CondE(FALSE); 1128236769Sobrien } 1129236769Sobrien } else { 1130236769Sobrien /* 1131236769Sobrien * E -> F 1132236769Sobrien */ 1133236769Sobrien CondPushBack(o); 1134236769Sobrien } 1135236769Sobrien } 1136236769Sobrien return (l); 1137236769Sobrien} 1138236769Sobrien 1139236769Sobrien/*- 1140236769Sobrien *----------------------------------------------------------------------- 1141236769Sobrien * Cond_EvalExpression -- 1142236769Sobrien * Evaluate an expression in the passed line. The expression 1143236769Sobrien * consists of &&, ||, !, make(target), defined(variable) 1144236769Sobrien * and parenthetical groupings thereof. 1145236769Sobrien * 1146236769Sobrien * Results: 1147236769Sobrien * COND_PARSE if the condition was valid grammatically 1148236769Sobrien * COND_INVALID if not a valid conditional. 1149236769Sobrien * 1150236769Sobrien * (*value) is set to the boolean value of the condition 1151236769Sobrien * 1152236769Sobrien * Side Effects: 1153236769Sobrien * None. 1154236769Sobrien * 1155236769Sobrien *----------------------------------------------------------------------- 1156236769Sobrien */ 1157236769Sobrienint 1158282740SsjgCond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprint, Boolean strictLHS) 1159236769Sobrien{ 1160236769Sobrien static const struct If *dflt_info; 1161236769Sobrien const struct If *sv_if_info = if_info; 1162236769Sobrien char *sv_condExpr = condExpr; 1163236769Sobrien Token sv_condPushBack = condPushBack; 1164236769Sobrien int rval; 1165236769Sobrien 1166282740Ssjg lhsStrict = strictLHS; 1167282740Ssjg 1168236769Sobrien while (*line == ' ' || *line == '\t') 1169236769Sobrien line++; 1170236769Sobrien 1171236769Sobrien if (info == NULL && (info = dflt_info) == NULL) { 1172236769Sobrien /* Scan for the entry for .if - it can't be first */ 1173236769Sobrien for (info = ifs; ; info++) 1174236769Sobrien if (info->form[0] == 0) 1175236769Sobrien break; 1176236769Sobrien dflt_info = info; 1177236769Sobrien } 1178319884Ssjg assert(info != NULL); 1179236769Sobrien 1180319884Ssjg if_info = info; 1181236769Sobrien condExpr = line; 1182236769Sobrien condPushBack = TOK_NONE; 1183236769Sobrien 1184236769Sobrien rval = do_Cond_EvalExpression(value); 1185236769Sobrien 1186236769Sobrien if (rval == COND_INVALID && eprint) 1187236769Sobrien Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line); 1188236769Sobrien 1189236769Sobrien if_info = sv_if_info; 1190236769Sobrien condExpr = sv_condExpr; 1191236769Sobrien condPushBack = sv_condPushBack; 1192236769Sobrien 1193236769Sobrien return rval; 1194236769Sobrien} 1195236769Sobrien 1196236769Sobrienstatic int 1197236769Sobriendo_Cond_EvalExpression(Boolean *value) 1198236769Sobrien{ 1199236769Sobrien 1200236769Sobrien switch (CondE(TRUE)) { 1201236769Sobrien case TOK_TRUE: 1202236769Sobrien if (CondToken(TRUE) == TOK_EOF) { 1203236769Sobrien *value = TRUE; 1204236769Sobrien return COND_PARSE; 1205236769Sobrien } 1206236769Sobrien break; 1207236769Sobrien case TOK_FALSE: 1208236769Sobrien if (CondToken(TRUE) == TOK_EOF) { 1209236769Sobrien *value = FALSE; 1210236769Sobrien return COND_PARSE; 1211236769Sobrien } 1212236769Sobrien break; 1213236769Sobrien default: 1214236769Sobrien case TOK_ERROR: 1215236769Sobrien break; 1216236769Sobrien } 1217236769Sobrien 1218236769Sobrien return COND_INVALID; 1219236769Sobrien} 1220236769Sobrien 1221236769Sobrien 1222236769Sobrien/*- 1223236769Sobrien *----------------------------------------------------------------------- 1224236769Sobrien * Cond_Eval -- 1225236769Sobrien * Evaluate the conditional in the passed line. The line 1226236769Sobrien * looks like this: 1227236769Sobrien * .<cond-type> <expr> 1228236769Sobrien * where <cond-type> is any of if, ifmake, ifnmake, ifdef, 1229236769Sobrien * ifndef, elif, elifmake, elifnmake, elifdef, elifndef 1230236769Sobrien * and <expr> consists of &&, ||, !, make(target), defined(variable) 1231236769Sobrien * and parenthetical groupings thereof. 1232236769Sobrien * 1233236769Sobrien * Input: 1234236769Sobrien * line Line to parse 1235236769Sobrien * 1236236769Sobrien * Results: 1237236769Sobrien * COND_PARSE if should parse lines after the conditional 1238236769Sobrien * COND_SKIP if should skip lines after the conditional 1239236769Sobrien * COND_INVALID if not a valid conditional. 1240236769Sobrien * 1241236769Sobrien * Side Effects: 1242236769Sobrien * None. 1243236769Sobrien * 1244236769Sobrien * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order 1245236769Sobrien * to detect splurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF) 1246236769Sobrien * otherwise .else could be treated as '.elif 1'. 1247236769Sobrien * 1248236769Sobrien *----------------------------------------------------------------------- 1249236769Sobrien */ 1250236769Sobrienint 1251236769SobrienCond_Eval(char *line) 1252236769Sobrien{ 1253243115Ssjg#define MAXIF 128 /* maximum depth of .if'ing */ 1254243115Ssjg#define MAXIF_BUMP 32 /* how much to grow by */ 1255236769Sobrien enum if_states { 1256236769Sobrien IF_ACTIVE, /* .if or .elif part active */ 1257236769Sobrien ELSE_ACTIVE, /* .else part active */ 1258236769Sobrien SEARCH_FOR_ELIF, /* searching for .elif/else to execute */ 1259236769Sobrien SKIP_TO_ELSE, /* has been true, but not seen '.else' */ 1260236769Sobrien SKIP_TO_ENDIF /* nothing else to execute */ 1261236769Sobrien }; 1262243115Ssjg static enum if_states *cond_state = NULL; 1263243115Ssjg static unsigned int max_if_depth = MAXIF; 1264236769Sobrien 1265236769Sobrien const struct If *ifp; 1266236769Sobrien Boolean isElif; 1267236769Sobrien Boolean value; 1268236769Sobrien int level; /* Level at which to report errors. */ 1269236769Sobrien enum if_states state; 1270236769Sobrien 1271236769Sobrien level = PARSE_FATAL; 1272243115Ssjg if (!cond_state) { 1273243115Ssjg cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state)); 1274243115Ssjg cond_state[0] = IF_ACTIVE; 1275243115Ssjg } 1276236769Sobrien /* skip leading character (the '.') and any whitespace */ 1277236769Sobrien for (line++; *line == ' ' || *line == '\t'; line++) 1278236769Sobrien continue; 1279236769Sobrien 1280236769Sobrien /* Find what type of if we're dealing with. */ 1281236769Sobrien if (line[0] == 'e') { 1282236769Sobrien if (line[1] != 'l') { 1283236769Sobrien if (!istoken(line + 1, "ndif", 4)) 1284236769Sobrien return COND_INVALID; 1285236769Sobrien /* End of conditional section */ 1286236769Sobrien if (cond_depth == cond_min_depth) { 1287236769Sobrien Parse_Error(level, "if-less endif"); 1288236769Sobrien return COND_PARSE; 1289236769Sobrien } 1290236769Sobrien /* Return state for previous conditional */ 1291236769Sobrien cond_depth--; 1292236769Sobrien return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; 1293236769Sobrien } 1294236769Sobrien 1295236769Sobrien /* Quite likely this is 'else' or 'elif' */ 1296236769Sobrien line += 2; 1297236769Sobrien if (istoken(line, "se", 2)) { 1298236769Sobrien /* It is else... */ 1299236769Sobrien if (cond_depth == cond_min_depth) { 1300236769Sobrien Parse_Error(level, "if-less else"); 1301236769Sobrien return COND_PARSE; 1302236769Sobrien } 1303236769Sobrien 1304236769Sobrien state = cond_state[cond_depth]; 1305236769Sobrien switch (state) { 1306236769Sobrien case SEARCH_FOR_ELIF: 1307236769Sobrien state = ELSE_ACTIVE; 1308236769Sobrien break; 1309236769Sobrien case ELSE_ACTIVE: 1310236769Sobrien case SKIP_TO_ENDIF: 1311236769Sobrien Parse_Error(PARSE_WARNING, "extra else"); 1312236769Sobrien /* FALLTHROUGH */ 1313236769Sobrien default: 1314236769Sobrien case IF_ACTIVE: 1315236769Sobrien case SKIP_TO_ELSE: 1316236769Sobrien state = SKIP_TO_ENDIF; 1317236769Sobrien break; 1318236769Sobrien } 1319236769Sobrien cond_state[cond_depth] = state; 1320236769Sobrien return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; 1321236769Sobrien } 1322236769Sobrien /* Assume for now it is an elif */ 1323236769Sobrien isElif = TRUE; 1324236769Sobrien } else 1325236769Sobrien isElif = FALSE; 1326236769Sobrien 1327236769Sobrien if (line[0] != 'i' || line[1] != 'f') 1328236769Sobrien /* Not an ifxxx or elifxxx line */ 1329236769Sobrien return COND_INVALID; 1330236769Sobrien 1331236769Sobrien /* 1332236769Sobrien * Figure out what sort of conditional it is -- what its default 1333236769Sobrien * function is, etc. -- by looking in the table of valid "ifs" 1334236769Sobrien */ 1335236769Sobrien line += 2; 1336236769Sobrien for (ifp = ifs; ; ifp++) { 1337236769Sobrien if (ifp->form == NULL) 1338236769Sobrien return COND_INVALID; 1339236769Sobrien if (istoken(ifp->form, line, ifp->formlen)) { 1340236769Sobrien line += ifp->formlen; 1341236769Sobrien break; 1342236769Sobrien } 1343236769Sobrien } 1344236769Sobrien 1345236769Sobrien /* Now we know what sort of 'if' it is... */ 1346236769Sobrien 1347236769Sobrien if (isElif) { 1348236769Sobrien if (cond_depth == cond_min_depth) { 1349236769Sobrien Parse_Error(level, "if-less elif"); 1350236769Sobrien return COND_PARSE; 1351236769Sobrien } 1352236769Sobrien state = cond_state[cond_depth]; 1353236769Sobrien if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) { 1354236769Sobrien Parse_Error(PARSE_WARNING, "extra elif"); 1355236769Sobrien cond_state[cond_depth] = SKIP_TO_ENDIF; 1356236769Sobrien return COND_SKIP; 1357236769Sobrien } 1358236769Sobrien if (state != SEARCH_FOR_ELIF) { 1359236769Sobrien /* Either just finished the 'true' block, or already SKIP_TO_ELSE */ 1360236769Sobrien cond_state[cond_depth] = SKIP_TO_ELSE; 1361236769Sobrien return COND_SKIP; 1362236769Sobrien } 1363236769Sobrien } else { 1364236769Sobrien /* Normal .if */ 1365243115Ssjg if (cond_depth + 1 >= max_if_depth) { 1366243115Ssjg /* 1367243115Ssjg * This is rare, but not impossible. 1368243115Ssjg * In meta mode, dirdeps.mk (only runs at level 0) 1369243115Ssjg * can need more than the default. 1370243115Ssjg */ 1371243115Ssjg max_if_depth += MAXIF_BUMP; 1372243115Ssjg cond_state = bmake_realloc(cond_state, max_if_depth * 1373243115Ssjg sizeof(*cond_state)); 1374236769Sobrien } 1375236769Sobrien state = cond_state[cond_depth]; 1376236769Sobrien cond_depth++; 1377236769Sobrien if (state > ELSE_ACTIVE) { 1378236769Sobrien /* If we aren't parsing the data, treat as always false */ 1379236769Sobrien cond_state[cond_depth] = SKIP_TO_ELSE; 1380236769Sobrien return COND_SKIP; 1381236769Sobrien } 1382236769Sobrien } 1383236769Sobrien 1384236769Sobrien /* And evaluate the conditional expresssion */ 1385282740Ssjg if (Cond_EvalExpression(ifp, line, &value, 1, TRUE) == COND_INVALID) { 1386236769Sobrien /* Syntax error in conditional, error message already output. */ 1387236769Sobrien /* Skip everything to matching .endif */ 1388236769Sobrien cond_state[cond_depth] = SKIP_TO_ELSE; 1389236769Sobrien return COND_SKIP; 1390236769Sobrien } 1391236769Sobrien 1392236769Sobrien if (!value) { 1393236769Sobrien cond_state[cond_depth] = SEARCH_FOR_ELIF; 1394236769Sobrien return COND_SKIP; 1395236769Sobrien } 1396236769Sobrien cond_state[cond_depth] = IF_ACTIVE; 1397236769Sobrien return COND_PARSE; 1398236769Sobrien} 1399236769Sobrien 1400236769Sobrien 1401236769Sobrien 1402236769Sobrien/*- 1403236769Sobrien *----------------------------------------------------------------------- 1404236769Sobrien * Cond_End -- 1405236769Sobrien * Make sure everything's clean at the end of a makefile. 1406236769Sobrien * 1407236769Sobrien * Results: 1408236769Sobrien * None. 1409236769Sobrien * 1410236769Sobrien * Side Effects: 1411236769Sobrien * Parse_Error will be called if open conditionals are around. 1412236769Sobrien * 1413236769Sobrien *----------------------------------------------------------------------- 1414236769Sobrien */ 1415236769Sobrienvoid 1416236769SobrienCond_restore_depth(unsigned int saved_depth) 1417236769Sobrien{ 1418236769Sobrien int open_conds = cond_depth - cond_min_depth; 1419236769Sobrien 1420236769Sobrien if (open_conds != 0 || saved_depth > cond_depth) { 1421236769Sobrien Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds, 1422236769Sobrien open_conds == 1 ? "" : "s"); 1423236769Sobrien cond_depth = cond_min_depth; 1424236769Sobrien } 1425236769Sobrien 1426236769Sobrien cond_min_depth = saved_depth; 1427236769Sobrien} 1428236769Sobrien 1429236769Sobrienunsigned int 1430236769SobrienCond_save_depth(void) 1431236769Sobrien{ 1432236769Sobrien int depth = cond_min_depth; 1433236769Sobrien 1434236769Sobrien cond_min_depth = cond_depth; 1435236769Sobrien return depth; 1436236769Sobrien} 1437