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