1*3841c287SSimon J. Gerraty /* $NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $ */ 23955d011SMarcel Moolenaar 33955d011SMarcel Moolenaar /* 43955d011SMarcel Moolenaar * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 53955d011SMarcel Moolenaar * All rights reserved. 63955d011SMarcel Moolenaar * 73955d011SMarcel Moolenaar * This code is derived from software contributed to Berkeley by 83955d011SMarcel Moolenaar * Adam de Boor. 93955d011SMarcel Moolenaar * 103955d011SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 113955d011SMarcel Moolenaar * modification, are permitted provided that the following conditions 123955d011SMarcel Moolenaar * are met: 133955d011SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 143955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 153955d011SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 163955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 173955d011SMarcel Moolenaar * documentation and/or other materials provided with the distribution. 183955d011SMarcel Moolenaar * 3. Neither the name of the University nor the names of its contributors 193955d011SMarcel Moolenaar * may be used to endorse or promote products derived from this software 203955d011SMarcel Moolenaar * without specific prior written permission. 213955d011SMarcel Moolenaar * 223955d011SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 233955d011SMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 243955d011SMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 253955d011SMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 263955d011SMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 273955d011SMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 283955d011SMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 293955d011SMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 303955d011SMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 313955d011SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 323955d011SMarcel Moolenaar * SUCH DAMAGE. 333955d011SMarcel Moolenaar */ 343955d011SMarcel Moolenaar 353955d011SMarcel Moolenaar /* 363955d011SMarcel Moolenaar * Copyright (c) 1988, 1989 by Adam de Boor 373955d011SMarcel Moolenaar * Copyright (c) 1989 by Berkeley Softworks 383955d011SMarcel Moolenaar * All rights reserved. 393955d011SMarcel Moolenaar * 403955d011SMarcel Moolenaar * This code is derived from software contributed to Berkeley by 413955d011SMarcel Moolenaar * Adam de Boor. 423955d011SMarcel Moolenaar * 433955d011SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 443955d011SMarcel Moolenaar * modification, are permitted provided that the following conditions 453955d011SMarcel Moolenaar * are met: 463955d011SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 473955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 483955d011SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 493955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 503955d011SMarcel Moolenaar * documentation and/or other materials provided with the distribution. 513955d011SMarcel Moolenaar * 3. All advertising materials mentioning features or use of this software 523955d011SMarcel Moolenaar * must display the following acknowledgement: 533955d011SMarcel Moolenaar * This product includes software developed by the University of 543955d011SMarcel Moolenaar * California, Berkeley and its contributors. 553955d011SMarcel Moolenaar * 4. Neither the name of the University nor the names of its contributors 563955d011SMarcel Moolenaar * may be used to endorse or promote products derived from this software 573955d011SMarcel Moolenaar * without specific prior written permission. 583955d011SMarcel Moolenaar * 593955d011SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 603955d011SMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 613955d011SMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 623955d011SMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 633955d011SMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 643955d011SMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 653955d011SMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 663955d011SMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 673955d011SMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 683955d011SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 693955d011SMarcel Moolenaar * SUCH DAMAGE. 703955d011SMarcel Moolenaar */ 713955d011SMarcel Moolenaar 723955d011SMarcel Moolenaar #ifndef MAKE_NATIVE 73*3841c287SSimon J. Gerraty static char rcsid[] = "$NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $"; 743955d011SMarcel Moolenaar #else 753955d011SMarcel Moolenaar #include <sys/cdefs.h> 763955d011SMarcel Moolenaar #ifndef lint 773955d011SMarcel Moolenaar #if 0 783955d011SMarcel Moolenaar static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94"; 793955d011SMarcel Moolenaar #else 80*3841c287SSimon J. Gerraty __RCSID("$NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $"); 813955d011SMarcel Moolenaar #endif 823955d011SMarcel Moolenaar #endif /* not lint */ 833955d011SMarcel Moolenaar #endif 843955d011SMarcel Moolenaar 853955d011SMarcel Moolenaar /*- 863955d011SMarcel Moolenaar * cond.c -- 873955d011SMarcel Moolenaar * Functions to handle conditionals in a makefile. 883955d011SMarcel Moolenaar * 893955d011SMarcel Moolenaar * Interface: 903955d011SMarcel Moolenaar * Cond_Eval Evaluate the conditional in the passed line. 913955d011SMarcel Moolenaar * 923955d011SMarcel Moolenaar */ 933955d011SMarcel Moolenaar 94e1cee40dSSimon J. Gerraty #include <assert.h> 953955d011SMarcel Moolenaar #include <ctype.h> 963955d011SMarcel Moolenaar #include <errno.h> /* For strtoul() error checking */ 973955d011SMarcel Moolenaar 983955d011SMarcel Moolenaar #include "make.h" 993955d011SMarcel Moolenaar #include "hash.h" 1003955d011SMarcel Moolenaar #include "dir.h" 1013955d011SMarcel Moolenaar #include "buf.h" 1023955d011SMarcel Moolenaar 1033955d011SMarcel Moolenaar /* 1043955d011SMarcel Moolenaar * The parsing of conditional expressions is based on this grammar: 1053955d011SMarcel Moolenaar * E -> F || E 1063955d011SMarcel Moolenaar * E -> F 1073955d011SMarcel Moolenaar * F -> T && F 1083955d011SMarcel Moolenaar * F -> T 1093955d011SMarcel Moolenaar * T -> defined(variable) 1103955d011SMarcel Moolenaar * T -> make(target) 1113955d011SMarcel Moolenaar * T -> exists(file) 1123955d011SMarcel Moolenaar * T -> empty(varspec) 1133955d011SMarcel Moolenaar * T -> target(name) 1143955d011SMarcel Moolenaar * T -> commands(name) 1153955d011SMarcel Moolenaar * T -> symbol 1163955d011SMarcel Moolenaar * T -> $(varspec) op value 1173955d011SMarcel Moolenaar * T -> $(varspec) == "string" 1183955d011SMarcel Moolenaar * T -> $(varspec) != "string" 1193955d011SMarcel Moolenaar * T -> "string" 1203955d011SMarcel Moolenaar * T -> ( E ) 1213955d011SMarcel Moolenaar * T -> ! T 1223955d011SMarcel Moolenaar * op -> == | != | > | < | >= | <= 1233955d011SMarcel Moolenaar * 1243955d011SMarcel Moolenaar * 'symbol' is some other symbol to which the default function (condDefProc) 1253955d011SMarcel Moolenaar * is applied. 1263955d011SMarcel Moolenaar * 1273955d011SMarcel Moolenaar * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) 1283955d011SMarcel Moolenaar * will return TOK_AND for '&' and '&&', TOK_OR for '|' and '||', 1293955d011SMarcel Moolenaar * TOK_NOT for '!', TOK_LPAREN for '(', TOK_RPAREN for ')' and will evaluate 1303955d011SMarcel Moolenaar * the other terminal symbols, using either the default function or the 1313955d011SMarcel Moolenaar * function given in the terminal, and return the result as either TOK_TRUE 1323955d011SMarcel Moolenaar * or TOK_FALSE. 1333955d011SMarcel Moolenaar * 1343955d011SMarcel Moolenaar * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons. 1353955d011SMarcel Moolenaar * 1363955d011SMarcel Moolenaar * All Non-Terminal functions (CondE, CondF and CondT) return TOK_ERROR on 1373955d011SMarcel Moolenaar * error. 1383955d011SMarcel Moolenaar */ 1393955d011SMarcel Moolenaar typedef enum { 1403955d011SMarcel Moolenaar TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT, 1413955d011SMarcel Moolenaar TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR 1423955d011SMarcel Moolenaar } Token; 1433955d011SMarcel Moolenaar 1443955d011SMarcel Moolenaar /*- 1453955d011SMarcel Moolenaar * Structures to handle elegantly the different forms of #if's. The 1463955d011SMarcel Moolenaar * last two fields are stored in condInvert and condDefProc, respectively. 1473955d011SMarcel Moolenaar */ 1483955d011SMarcel Moolenaar static void CondPushBack(Token); 149*3841c287SSimon J. Gerraty static int CondGetArg(Boolean, char **, char **, const char *); 1503955d011SMarcel Moolenaar static Boolean CondDoDefined(int, const char *); 1513955d011SMarcel Moolenaar static int CondStrMatch(const void *, const void *); 1523955d011SMarcel Moolenaar static Boolean CondDoMake(int, const char *); 1533955d011SMarcel Moolenaar static Boolean CondDoExists(int, const char *); 1543955d011SMarcel Moolenaar static Boolean CondDoTarget(int, const char *); 1553955d011SMarcel Moolenaar static Boolean CondDoCommands(int, const char *); 1563955d011SMarcel Moolenaar static Boolean CondCvtArg(char *, double *); 1573955d011SMarcel Moolenaar static Token CondToken(Boolean); 1583955d011SMarcel Moolenaar static Token CondT(Boolean); 1593955d011SMarcel Moolenaar static Token CondF(Boolean); 1603955d011SMarcel Moolenaar static Token CondE(Boolean); 1613955d011SMarcel Moolenaar static int do_Cond_EvalExpression(Boolean *); 1623955d011SMarcel Moolenaar 1633955d011SMarcel Moolenaar static const struct If { 1643955d011SMarcel Moolenaar const char *form; /* Form of if */ 1653955d011SMarcel Moolenaar int formlen; /* Length of form */ 1663955d011SMarcel Moolenaar Boolean doNot; /* TRUE if default function should be negated */ 1673955d011SMarcel Moolenaar Boolean (*defProc)(int, const char *); /* Default function to apply */ 1683955d011SMarcel Moolenaar } ifs[] = { 1693955d011SMarcel Moolenaar { "def", 3, FALSE, CondDoDefined }, 1703955d011SMarcel Moolenaar { "ndef", 4, TRUE, CondDoDefined }, 1713955d011SMarcel Moolenaar { "make", 4, FALSE, CondDoMake }, 1723955d011SMarcel Moolenaar { "nmake", 5, TRUE, CondDoMake }, 1733955d011SMarcel Moolenaar { "", 0, FALSE, CondDoDefined }, 1743955d011SMarcel Moolenaar { NULL, 0, FALSE, NULL } 1753955d011SMarcel Moolenaar }; 1763955d011SMarcel Moolenaar 1773955d011SMarcel Moolenaar static const struct If *if_info; /* Info for current statement */ 1783955d011SMarcel Moolenaar static char *condExpr; /* The expression to parse */ 1793955d011SMarcel Moolenaar static Token condPushBack=TOK_NONE; /* Single push-back token used in 1803955d011SMarcel Moolenaar * parsing */ 1813955d011SMarcel Moolenaar 1823955d011SMarcel Moolenaar static unsigned int cond_depth = 0; /* current .if nesting level */ 1833955d011SMarcel Moolenaar static unsigned int cond_min_depth = 0; /* depth at makefile open */ 1843955d011SMarcel Moolenaar 18528a6bc81SSimon J. Gerraty /* 18628a6bc81SSimon J. Gerraty * Indicate when we should be strict about lhs of comparisons. 18728a6bc81SSimon J. Gerraty * TRUE when Cond_EvalExpression is called from Cond_Eval (.if etc) 18828a6bc81SSimon J. Gerraty * FALSE when Cond_EvalExpression is called from var.c:ApplyModifiers 18928a6bc81SSimon J. Gerraty * since lhs is already expanded and we cannot tell if 19028a6bc81SSimon J. Gerraty * it was a variable reference or not. 19128a6bc81SSimon J. Gerraty */ 19228a6bc81SSimon J. Gerraty static Boolean lhsStrict; 19328a6bc81SSimon J. Gerraty 1943955d011SMarcel Moolenaar static int 1953955d011SMarcel Moolenaar istoken(const char *str, const char *tok, size_t len) 1963955d011SMarcel Moolenaar { 1973955d011SMarcel Moolenaar return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]); 1983955d011SMarcel Moolenaar } 1993955d011SMarcel Moolenaar 2003955d011SMarcel Moolenaar /*- 2013955d011SMarcel Moolenaar *----------------------------------------------------------------------- 2023955d011SMarcel Moolenaar * CondPushBack -- 2033955d011SMarcel Moolenaar * Push back the most recent token read. We only need one level of 2043955d011SMarcel Moolenaar * this, so the thing is just stored in 'condPushback'. 2053955d011SMarcel Moolenaar * 2063955d011SMarcel Moolenaar * Input: 2073955d011SMarcel Moolenaar * t Token to push back into the "stream" 2083955d011SMarcel Moolenaar * 2093955d011SMarcel Moolenaar * Results: 2103955d011SMarcel Moolenaar * None. 2113955d011SMarcel Moolenaar * 2123955d011SMarcel Moolenaar * Side Effects: 2133955d011SMarcel Moolenaar * condPushback is overwritten. 2143955d011SMarcel Moolenaar * 2153955d011SMarcel Moolenaar *----------------------------------------------------------------------- 2163955d011SMarcel Moolenaar */ 2173955d011SMarcel Moolenaar static void 2183955d011SMarcel Moolenaar CondPushBack(Token t) 2193955d011SMarcel Moolenaar { 2203955d011SMarcel Moolenaar condPushBack = t; 2213955d011SMarcel Moolenaar } 2223955d011SMarcel Moolenaar 2233955d011SMarcel Moolenaar /*- 2243955d011SMarcel Moolenaar *----------------------------------------------------------------------- 2253955d011SMarcel Moolenaar * CondGetArg -- 2263955d011SMarcel Moolenaar * Find the argument of a built-in function. 2273955d011SMarcel Moolenaar * 2283955d011SMarcel Moolenaar * Results: 2293955d011SMarcel Moolenaar * The length of the argument and the address of the argument. 2303955d011SMarcel Moolenaar * 2313955d011SMarcel Moolenaar * Side Effects: 2323955d011SMarcel Moolenaar * The pointer is set to point to the closing parenthesis of the 2333955d011SMarcel Moolenaar * function call. 2343955d011SMarcel Moolenaar * 2353955d011SMarcel Moolenaar *----------------------------------------------------------------------- 2363955d011SMarcel Moolenaar */ 2373955d011SMarcel Moolenaar static int 238*3841c287SSimon J. Gerraty CondGetArg(Boolean doEval, char **linePtr, char **argPtr, const char *func) 2393955d011SMarcel Moolenaar { 2403955d011SMarcel Moolenaar char *cp; 2413955d011SMarcel Moolenaar int argLen; 2423955d011SMarcel Moolenaar Buffer buf; 2433955d011SMarcel Moolenaar int paren_depth; 2443955d011SMarcel Moolenaar char ch; 2453955d011SMarcel Moolenaar 2463955d011SMarcel Moolenaar cp = *linePtr; 2473955d011SMarcel Moolenaar if (func != NULL) 2483955d011SMarcel Moolenaar /* Skip opening '(' - verfied by caller */ 2493955d011SMarcel Moolenaar cp++; 2503955d011SMarcel Moolenaar 2513955d011SMarcel Moolenaar if (*cp == '\0') { 2523955d011SMarcel Moolenaar /* 2533955d011SMarcel Moolenaar * No arguments whatsoever. Because 'make' and 'defined' aren't really 2543955d011SMarcel Moolenaar * "reserved words", we don't print a message. I think this is better 2553955d011SMarcel Moolenaar * than hitting the user with a warning message every time s/he uses 2563955d011SMarcel Moolenaar * the word 'make' or 'defined' at the beginning of a symbol... 2573955d011SMarcel Moolenaar */ 2583955d011SMarcel Moolenaar *argPtr = NULL; 259*3841c287SSimon J. Gerraty return 0; 2603955d011SMarcel Moolenaar } 2613955d011SMarcel Moolenaar 2623955d011SMarcel Moolenaar while (*cp == ' ' || *cp == '\t') { 2633955d011SMarcel Moolenaar cp++; 2643955d011SMarcel Moolenaar } 2653955d011SMarcel Moolenaar 2663955d011SMarcel Moolenaar /* 2673955d011SMarcel Moolenaar * Create a buffer for the argument and start it out at 16 characters 2683955d011SMarcel Moolenaar * long. Why 16? Why not? 2693955d011SMarcel Moolenaar */ 2703955d011SMarcel Moolenaar Buf_Init(&buf, 16); 2713955d011SMarcel Moolenaar 2723955d011SMarcel Moolenaar paren_depth = 0; 2733955d011SMarcel Moolenaar for (;;) { 2743955d011SMarcel Moolenaar ch = *cp; 2753955d011SMarcel Moolenaar if (ch == 0 || ch == ' ' || ch == '\t') 2763955d011SMarcel Moolenaar break; 2773955d011SMarcel Moolenaar if ((ch == '&' || ch == '|') && paren_depth == 0) 2783955d011SMarcel Moolenaar break; 2793955d011SMarcel Moolenaar if (*cp == '$') { 2803955d011SMarcel Moolenaar /* 2813955d011SMarcel Moolenaar * Parse the variable spec and install it as part of the argument 2823955d011SMarcel Moolenaar * if it's valid. We tell Var_Parse to complain on an undefined 2833955d011SMarcel Moolenaar * variable, so we don't do it too. Nor do we return an error, 2843955d011SMarcel Moolenaar * though perhaps we should... 2853955d011SMarcel Moolenaar */ 2863955d011SMarcel Moolenaar char *cp2; 2873955d011SMarcel Moolenaar int len; 2883955d011SMarcel Moolenaar void *freeIt; 2893955d011SMarcel Moolenaar 290*3841c287SSimon J. Gerraty cp2 = Var_Parse(cp, VAR_CMD, VARF_UNDEFERR| 291*3841c287SSimon J. Gerraty (doEval ? VARF_WANTRES : 0), 292be19d90bSSimon J. Gerraty &len, &freeIt); 2933955d011SMarcel Moolenaar Buf_AddBytes(&buf, strlen(cp2), cp2); 2943955d011SMarcel Moolenaar free(freeIt); 2953955d011SMarcel Moolenaar cp += len; 2963955d011SMarcel Moolenaar continue; 2973955d011SMarcel Moolenaar } 2983955d011SMarcel Moolenaar if (ch == '(') 2993955d011SMarcel Moolenaar paren_depth++; 3003955d011SMarcel Moolenaar else 3013955d011SMarcel Moolenaar if (ch == ')' && --paren_depth < 0) 3023955d011SMarcel Moolenaar break; 3033955d011SMarcel Moolenaar Buf_AddByte(&buf, *cp); 3043955d011SMarcel Moolenaar cp++; 3053955d011SMarcel Moolenaar } 3063955d011SMarcel Moolenaar 3073955d011SMarcel Moolenaar *argPtr = Buf_GetAll(&buf, &argLen); 3083955d011SMarcel Moolenaar Buf_Destroy(&buf, FALSE); 3093955d011SMarcel Moolenaar 3103955d011SMarcel Moolenaar while (*cp == ' ' || *cp == '\t') { 3113955d011SMarcel Moolenaar cp++; 3123955d011SMarcel Moolenaar } 3133955d011SMarcel Moolenaar 3143955d011SMarcel Moolenaar if (func != NULL && *cp++ != ')') { 3153955d011SMarcel Moolenaar Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", 3163955d011SMarcel Moolenaar func); 317*3841c287SSimon J. Gerraty return 0; 3183955d011SMarcel Moolenaar } 3193955d011SMarcel Moolenaar 3203955d011SMarcel Moolenaar *linePtr = cp; 321*3841c287SSimon J. Gerraty return argLen; 3223955d011SMarcel Moolenaar } 3233955d011SMarcel Moolenaar 3243955d011SMarcel Moolenaar /*- 3253955d011SMarcel Moolenaar *----------------------------------------------------------------------- 3263955d011SMarcel Moolenaar * CondDoDefined -- 3273955d011SMarcel Moolenaar * Handle the 'defined' function for conditionals. 3283955d011SMarcel Moolenaar * 3293955d011SMarcel Moolenaar * Results: 3303955d011SMarcel Moolenaar * TRUE if the given variable is defined. 3313955d011SMarcel Moolenaar * 3323955d011SMarcel Moolenaar * Side Effects: 3333955d011SMarcel Moolenaar * None. 3343955d011SMarcel Moolenaar * 3353955d011SMarcel Moolenaar *----------------------------------------------------------------------- 3363955d011SMarcel Moolenaar */ 3373955d011SMarcel Moolenaar static Boolean 3383955d011SMarcel Moolenaar CondDoDefined(int argLen MAKE_ATTR_UNUSED, const char *arg) 3393955d011SMarcel Moolenaar { 3403955d011SMarcel Moolenaar char *p1; 3413955d011SMarcel Moolenaar Boolean result; 3423955d011SMarcel Moolenaar 3433955d011SMarcel Moolenaar if (Var_Value(arg, VAR_CMD, &p1) != NULL) { 3443955d011SMarcel Moolenaar result = TRUE; 3453955d011SMarcel Moolenaar } else { 3463955d011SMarcel Moolenaar result = FALSE; 3473955d011SMarcel Moolenaar } 348be19d90bSSimon J. Gerraty 3493955d011SMarcel Moolenaar free(p1); 350*3841c287SSimon J. Gerraty return result; 3513955d011SMarcel Moolenaar } 3523955d011SMarcel Moolenaar 3533955d011SMarcel Moolenaar /*- 3543955d011SMarcel Moolenaar *----------------------------------------------------------------------- 3553955d011SMarcel Moolenaar * CondStrMatch -- 3563955d011SMarcel Moolenaar * Front-end for Str_Match so it returns 0 on match and non-zero 3573955d011SMarcel Moolenaar * on mismatch. Callback function for CondDoMake via Lst_Find 3583955d011SMarcel Moolenaar * 3593955d011SMarcel Moolenaar * Results: 3603955d011SMarcel Moolenaar * 0 if string matches pattern 3613955d011SMarcel Moolenaar * 3623955d011SMarcel Moolenaar * Side Effects: 3633955d011SMarcel Moolenaar * None 3643955d011SMarcel Moolenaar * 3653955d011SMarcel Moolenaar *----------------------------------------------------------------------- 3663955d011SMarcel Moolenaar */ 3673955d011SMarcel Moolenaar static int 3683955d011SMarcel Moolenaar CondStrMatch(const void *string, const void *pattern) 3693955d011SMarcel Moolenaar { 370*3841c287SSimon J. Gerraty return !Str_Match(string, pattern); 3713955d011SMarcel Moolenaar } 3723955d011SMarcel Moolenaar 3733955d011SMarcel Moolenaar /*- 3743955d011SMarcel Moolenaar *----------------------------------------------------------------------- 3753955d011SMarcel Moolenaar * CondDoMake -- 3763955d011SMarcel Moolenaar * Handle the 'make' function for conditionals. 3773955d011SMarcel Moolenaar * 3783955d011SMarcel Moolenaar * Results: 3793955d011SMarcel Moolenaar * TRUE if the given target is being made. 3803955d011SMarcel Moolenaar * 3813955d011SMarcel Moolenaar * Side Effects: 3823955d011SMarcel Moolenaar * None. 3833955d011SMarcel Moolenaar * 3843955d011SMarcel Moolenaar *----------------------------------------------------------------------- 3853955d011SMarcel Moolenaar */ 3863955d011SMarcel Moolenaar static Boolean 3873955d011SMarcel Moolenaar CondDoMake(int argLen MAKE_ATTR_UNUSED, const char *arg) 3883955d011SMarcel Moolenaar { 3893955d011SMarcel Moolenaar return Lst_Find(create, arg, CondStrMatch) != NULL; 3903955d011SMarcel Moolenaar } 3913955d011SMarcel Moolenaar 3923955d011SMarcel Moolenaar /*- 3933955d011SMarcel Moolenaar *----------------------------------------------------------------------- 3943955d011SMarcel Moolenaar * CondDoExists -- 3953955d011SMarcel Moolenaar * See if the given file exists. 3963955d011SMarcel Moolenaar * 3973955d011SMarcel Moolenaar * Results: 3983955d011SMarcel Moolenaar * TRUE if the file exists and FALSE if it does not. 3993955d011SMarcel Moolenaar * 4003955d011SMarcel Moolenaar * Side Effects: 4013955d011SMarcel Moolenaar * None. 4023955d011SMarcel Moolenaar * 4033955d011SMarcel Moolenaar *----------------------------------------------------------------------- 4043955d011SMarcel Moolenaar */ 4053955d011SMarcel Moolenaar static Boolean 4063955d011SMarcel Moolenaar CondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg) 4073955d011SMarcel Moolenaar { 4083955d011SMarcel Moolenaar Boolean result; 4093955d011SMarcel Moolenaar char *path; 4103955d011SMarcel Moolenaar 4113955d011SMarcel Moolenaar path = Dir_FindFile(arg, dirSearchPath); 4123955d011SMarcel Moolenaar if (DEBUG(COND)) { 4133955d011SMarcel Moolenaar fprintf(debug_file, "exists(%s) result is \"%s\"\n", 4143955d011SMarcel Moolenaar arg, path ? path : ""); 4153955d011SMarcel Moolenaar } 4163955d011SMarcel Moolenaar if (path != NULL) { 4173955d011SMarcel Moolenaar result = TRUE; 4183955d011SMarcel Moolenaar free(path); 4193955d011SMarcel Moolenaar } else { 4203955d011SMarcel Moolenaar result = FALSE; 4213955d011SMarcel Moolenaar } 422*3841c287SSimon J. Gerraty return result; 4233955d011SMarcel Moolenaar } 4243955d011SMarcel Moolenaar 4253955d011SMarcel Moolenaar /*- 4263955d011SMarcel Moolenaar *----------------------------------------------------------------------- 4273955d011SMarcel Moolenaar * CondDoTarget -- 4283955d011SMarcel Moolenaar * See if the given node exists and is an actual target. 4293955d011SMarcel Moolenaar * 4303955d011SMarcel Moolenaar * Results: 4313955d011SMarcel Moolenaar * TRUE if the node exists as a target and FALSE if it does not. 4323955d011SMarcel Moolenaar * 4333955d011SMarcel Moolenaar * Side Effects: 4343955d011SMarcel Moolenaar * None. 4353955d011SMarcel Moolenaar * 4363955d011SMarcel Moolenaar *----------------------------------------------------------------------- 4373955d011SMarcel Moolenaar */ 4383955d011SMarcel Moolenaar static Boolean 4393955d011SMarcel Moolenaar CondDoTarget(int argLen MAKE_ATTR_UNUSED, const char *arg) 4403955d011SMarcel Moolenaar { 4413955d011SMarcel Moolenaar GNode *gn; 4423955d011SMarcel Moolenaar 4433955d011SMarcel Moolenaar gn = Targ_FindNode(arg, TARG_NOCREATE); 444*3841c287SSimon J. Gerraty return gn != NULL && !OP_NOP(gn->type); 4453955d011SMarcel Moolenaar } 4463955d011SMarcel Moolenaar 4473955d011SMarcel Moolenaar /*- 4483955d011SMarcel Moolenaar *----------------------------------------------------------------------- 4493955d011SMarcel Moolenaar * CondDoCommands -- 4503955d011SMarcel Moolenaar * See if the given node exists and is an actual target with commands 4513955d011SMarcel Moolenaar * associated with it. 4523955d011SMarcel Moolenaar * 4533955d011SMarcel Moolenaar * Results: 4543955d011SMarcel Moolenaar * TRUE if the node exists as a target and has commands associated with 4553955d011SMarcel Moolenaar * it and FALSE if it does not. 4563955d011SMarcel Moolenaar * 4573955d011SMarcel Moolenaar * Side Effects: 4583955d011SMarcel Moolenaar * None. 4593955d011SMarcel Moolenaar * 4603955d011SMarcel Moolenaar *----------------------------------------------------------------------- 4613955d011SMarcel Moolenaar */ 4623955d011SMarcel Moolenaar static Boolean 4633955d011SMarcel Moolenaar CondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg) 4643955d011SMarcel Moolenaar { 4653955d011SMarcel Moolenaar GNode *gn; 4663955d011SMarcel Moolenaar 4673955d011SMarcel Moolenaar gn = Targ_FindNode(arg, TARG_NOCREATE); 468*3841c287SSimon J. Gerraty return gn != NULL && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands); 4693955d011SMarcel Moolenaar } 4703955d011SMarcel Moolenaar 4713955d011SMarcel Moolenaar /*- 4723955d011SMarcel Moolenaar *----------------------------------------------------------------------- 4733955d011SMarcel Moolenaar * CondCvtArg -- 4743955d011SMarcel Moolenaar * Convert the given number into a double. 4753955d011SMarcel Moolenaar * We try a base 10 or 16 integer conversion first, if that fails 4763955d011SMarcel Moolenaar * then we try a floating point conversion instead. 4773955d011SMarcel Moolenaar * 4783955d011SMarcel Moolenaar * Results: 4793955d011SMarcel Moolenaar * Sets 'value' to double value of string. 4803955d011SMarcel Moolenaar * Returns 'true' if the convertion suceeded 4813955d011SMarcel Moolenaar * 4823955d011SMarcel Moolenaar *----------------------------------------------------------------------- 4833955d011SMarcel Moolenaar */ 4843955d011SMarcel Moolenaar static Boolean 4853955d011SMarcel Moolenaar CondCvtArg(char *str, double *value) 4863955d011SMarcel Moolenaar { 4873955d011SMarcel Moolenaar char *eptr, ech; 4883955d011SMarcel Moolenaar unsigned long l_val; 4893955d011SMarcel Moolenaar double d_val; 4903955d011SMarcel Moolenaar 4913955d011SMarcel Moolenaar errno = 0; 492ac3446e9SSimon J. Gerraty if (!*str) { 493ac3446e9SSimon J. Gerraty *value = (double)0; 494ac3446e9SSimon J. Gerraty return TRUE; 495ac3446e9SSimon J. Gerraty } 4963955d011SMarcel Moolenaar l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10); 4973955d011SMarcel Moolenaar ech = *eptr; 4983955d011SMarcel Moolenaar if (ech == 0 && errno != ERANGE) { 4993955d011SMarcel Moolenaar d_val = str[0] == '-' ? -(double)-l_val : (double)l_val; 5003955d011SMarcel Moolenaar } else { 5013955d011SMarcel Moolenaar if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E') 5023955d011SMarcel Moolenaar return FALSE; 5033955d011SMarcel Moolenaar d_val = strtod(str, &eptr); 5043955d011SMarcel Moolenaar if (*eptr) 5053955d011SMarcel Moolenaar return FALSE; 5063955d011SMarcel Moolenaar } 5073955d011SMarcel Moolenaar 5083955d011SMarcel Moolenaar *value = d_val; 5093955d011SMarcel Moolenaar return TRUE; 5103955d011SMarcel Moolenaar } 5113955d011SMarcel Moolenaar 5123955d011SMarcel Moolenaar /*- 5133955d011SMarcel Moolenaar *----------------------------------------------------------------------- 5143955d011SMarcel Moolenaar * CondGetString -- 5153955d011SMarcel Moolenaar * Get a string from a variable reference or an optionally quoted 5163955d011SMarcel Moolenaar * string. This is called for the lhs and rhs of string compares. 5173955d011SMarcel Moolenaar * 5183955d011SMarcel Moolenaar * Results: 5193955d011SMarcel Moolenaar * Sets freeIt if needed, 5203955d011SMarcel Moolenaar * Sets quoted if string was quoted, 5213955d011SMarcel Moolenaar * Returns NULL on error, 5223955d011SMarcel Moolenaar * else returns string - absent any quotes. 5233955d011SMarcel Moolenaar * 5243955d011SMarcel Moolenaar * Side Effects: 5253955d011SMarcel Moolenaar * Moves condExpr to end of this token. 5263955d011SMarcel Moolenaar * 5273955d011SMarcel Moolenaar * 5283955d011SMarcel Moolenaar *----------------------------------------------------------------------- 5293955d011SMarcel Moolenaar */ 5303955d011SMarcel Moolenaar /* coverity:[+alloc : arg-*2] */ 5313955d011SMarcel Moolenaar static char * 53228a6bc81SSimon J. Gerraty CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS) 5333955d011SMarcel Moolenaar { 5343955d011SMarcel Moolenaar Buffer buf; 5353955d011SMarcel Moolenaar char *cp; 5363955d011SMarcel Moolenaar char *str; 5373955d011SMarcel Moolenaar int len; 5383955d011SMarcel Moolenaar int qt; 5393955d011SMarcel Moolenaar char *start; 5403955d011SMarcel Moolenaar 5413955d011SMarcel Moolenaar Buf_Init(&buf, 0); 5423955d011SMarcel Moolenaar str = NULL; 5433955d011SMarcel Moolenaar *freeIt = NULL; 5443955d011SMarcel Moolenaar *quoted = qt = *condExpr == '"' ? 1 : 0; 5453955d011SMarcel Moolenaar if (qt) 5463955d011SMarcel Moolenaar condExpr++; 5473955d011SMarcel Moolenaar for (start = condExpr; *condExpr && str == NULL; condExpr++) { 5483955d011SMarcel Moolenaar switch (*condExpr) { 5493955d011SMarcel Moolenaar case '\\': 5503955d011SMarcel Moolenaar if (condExpr[1] != '\0') { 5513955d011SMarcel Moolenaar condExpr++; 5523955d011SMarcel Moolenaar Buf_AddByte(&buf, *condExpr); 5533955d011SMarcel Moolenaar } 5543955d011SMarcel Moolenaar break; 5553955d011SMarcel Moolenaar case '"': 5563955d011SMarcel Moolenaar if (qt) { 5573955d011SMarcel Moolenaar condExpr++; /* we don't want the quotes */ 5583955d011SMarcel Moolenaar goto got_str; 5593955d011SMarcel Moolenaar } else 5603955d011SMarcel Moolenaar Buf_AddByte(&buf, *condExpr); /* likely? */ 5613955d011SMarcel Moolenaar break; 5623955d011SMarcel Moolenaar case ')': 5633955d011SMarcel Moolenaar case '!': 5643955d011SMarcel Moolenaar case '=': 5653955d011SMarcel Moolenaar case '>': 5663955d011SMarcel Moolenaar case '<': 5673955d011SMarcel Moolenaar case ' ': 5683955d011SMarcel Moolenaar case '\t': 5693955d011SMarcel Moolenaar if (!qt) 5703955d011SMarcel Moolenaar goto got_str; 5713955d011SMarcel Moolenaar else 5723955d011SMarcel Moolenaar Buf_AddByte(&buf, *condExpr); 5733955d011SMarcel Moolenaar break; 5743955d011SMarcel Moolenaar case '$': 5753955d011SMarcel Moolenaar /* if we are in quotes, then an undefined variable is ok */ 576be19d90bSSimon J. Gerraty str = Var_Parse(condExpr, VAR_CMD, 577be19d90bSSimon J. Gerraty ((!qt && doEval) ? VARF_UNDEFERR : 0) | 578*3841c287SSimon J. Gerraty (doEval ? VARF_WANTRES : 0), &len, freeIt); 5793955d011SMarcel Moolenaar if (str == var_Error) { 5803955d011SMarcel Moolenaar if (*freeIt) { 5813955d011SMarcel Moolenaar free(*freeIt); 5823955d011SMarcel Moolenaar *freeIt = NULL; 5833955d011SMarcel Moolenaar } 5843955d011SMarcel Moolenaar /* 5853955d011SMarcel Moolenaar * Even if !doEval, we still report syntax errors, which 5863955d011SMarcel Moolenaar * is what getting var_Error back with !doEval means. 5873955d011SMarcel Moolenaar */ 5883955d011SMarcel Moolenaar str = NULL; 5893955d011SMarcel Moolenaar goto cleanup; 5903955d011SMarcel Moolenaar } 5913955d011SMarcel Moolenaar condExpr += len; 5923955d011SMarcel Moolenaar /* 5933955d011SMarcel Moolenaar * If the '$' was first char (no quotes), and we are 5943955d011SMarcel Moolenaar * followed by space, the operator or end of expression, 5953955d011SMarcel Moolenaar * we are done. 5963955d011SMarcel Moolenaar */ 5973955d011SMarcel Moolenaar if ((condExpr == start + len) && 5983955d011SMarcel Moolenaar (*condExpr == '\0' || 5993955d011SMarcel Moolenaar isspace((unsigned char) *condExpr) || 6003955d011SMarcel Moolenaar strchr("!=><)", *condExpr))) { 6013955d011SMarcel Moolenaar goto cleanup; 6023955d011SMarcel Moolenaar } 6033955d011SMarcel Moolenaar /* 6043955d011SMarcel Moolenaar * Nope, we better copy str to buf 6053955d011SMarcel Moolenaar */ 6063955d011SMarcel Moolenaar for (cp = str; *cp; cp++) { 6073955d011SMarcel Moolenaar Buf_AddByte(&buf, *cp); 6083955d011SMarcel Moolenaar } 6093955d011SMarcel Moolenaar if (*freeIt) { 6103955d011SMarcel Moolenaar free(*freeIt); 6113955d011SMarcel Moolenaar *freeIt = NULL; 6123955d011SMarcel Moolenaar } 6133955d011SMarcel Moolenaar str = NULL; /* not finished yet */ 6143955d011SMarcel Moolenaar condExpr--; /* don't skip over next char */ 6153955d011SMarcel Moolenaar break; 6163955d011SMarcel Moolenaar default: 61728a6bc81SSimon J. Gerraty if (strictLHS && !qt && *start != '$' && 61828a6bc81SSimon J. Gerraty !isdigit((unsigned char) *start)) { 61928a6bc81SSimon J. Gerraty /* lhs must be quoted, a variable reference or number */ 62028a6bc81SSimon J. Gerraty if (*freeIt) { 62128a6bc81SSimon J. Gerraty free(*freeIt); 62228a6bc81SSimon J. Gerraty *freeIt = NULL; 62328a6bc81SSimon J. Gerraty } 62428a6bc81SSimon J. Gerraty str = NULL; 62528a6bc81SSimon J. Gerraty goto cleanup; 62628a6bc81SSimon J. Gerraty } 6273955d011SMarcel Moolenaar Buf_AddByte(&buf, *condExpr); 6283955d011SMarcel Moolenaar break; 6293955d011SMarcel Moolenaar } 6303955d011SMarcel Moolenaar } 6313955d011SMarcel Moolenaar got_str: 6323955d011SMarcel Moolenaar str = Buf_GetAll(&buf, NULL); 6333955d011SMarcel Moolenaar *freeIt = str; 6343955d011SMarcel Moolenaar cleanup: 6353955d011SMarcel Moolenaar Buf_Destroy(&buf, FALSE); 6363955d011SMarcel Moolenaar return str; 6373955d011SMarcel Moolenaar } 6383955d011SMarcel Moolenaar 6393955d011SMarcel Moolenaar /*- 6403955d011SMarcel Moolenaar *----------------------------------------------------------------------- 6413955d011SMarcel Moolenaar * CondToken -- 6423955d011SMarcel Moolenaar * Return the next token from the input. 6433955d011SMarcel Moolenaar * 6443955d011SMarcel Moolenaar * Results: 6453955d011SMarcel Moolenaar * A Token for the next lexical token in the stream. 6463955d011SMarcel Moolenaar * 6473955d011SMarcel Moolenaar * Side Effects: 6483955d011SMarcel Moolenaar * condPushback will be set back to TOK_NONE if it is used. 6493955d011SMarcel Moolenaar * 6503955d011SMarcel Moolenaar *----------------------------------------------------------------------- 6513955d011SMarcel Moolenaar */ 6523955d011SMarcel Moolenaar static Token 6533955d011SMarcel Moolenaar compare_expression(Boolean doEval) 6543955d011SMarcel Moolenaar { 6553955d011SMarcel Moolenaar Token t; 6563955d011SMarcel Moolenaar char *lhs; 6573955d011SMarcel Moolenaar char *rhs; 6583955d011SMarcel Moolenaar char *op; 6593955d011SMarcel Moolenaar void *lhsFree; 6603955d011SMarcel Moolenaar void *rhsFree; 6613955d011SMarcel Moolenaar Boolean lhsQuoted; 6623955d011SMarcel Moolenaar Boolean rhsQuoted; 6633955d011SMarcel Moolenaar double left, right; 6643955d011SMarcel Moolenaar 6653955d011SMarcel Moolenaar t = TOK_ERROR; 6663955d011SMarcel Moolenaar rhs = NULL; 6673955d011SMarcel Moolenaar lhsFree = rhsFree = FALSE; 6683955d011SMarcel Moolenaar lhsQuoted = rhsQuoted = FALSE; 6693955d011SMarcel Moolenaar 6703955d011SMarcel Moolenaar /* 6713955d011SMarcel Moolenaar * Parse the variable spec and skip over it, saving its 6723955d011SMarcel Moolenaar * value in lhs. 6733955d011SMarcel Moolenaar */ 67428a6bc81SSimon J. Gerraty lhs = CondGetString(doEval, &lhsQuoted, &lhsFree, lhsStrict); 6753955d011SMarcel Moolenaar if (!lhs) 6763955d011SMarcel Moolenaar goto done; 6773955d011SMarcel Moolenaar 6783955d011SMarcel Moolenaar /* 6793955d011SMarcel Moolenaar * Skip whitespace to get to the operator 6803955d011SMarcel Moolenaar */ 6813955d011SMarcel Moolenaar while (isspace((unsigned char) *condExpr)) 6823955d011SMarcel Moolenaar condExpr++; 6833955d011SMarcel Moolenaar 6843955d011SMarcel Moolenaar /* 6853955d011SMarcel Moolenaar * Make sure the operator is a valid one. If it isn't a 6863955d011SMarcel Moolenaar * known relational operator, pretend we got a 6873955d011SMarcel Moolenaar * != 0 comparison. 6883955d011SMarcel Moolenaar */ 6893955d011SMarcel Moolenaar op = condExpr; 6903955d011SMarcel Moolenaar switch (*condExpr) { 6913955d011SMarcel Moolenaar case '!': 6923955d011SMarcel Moolenaar case '=': 6933955d011SMarcel Moolenaar case '<': 6943955d011SMarcel Moolenaar case '>': 6953955d011SMarcel Moolenaar if (condExpr[1] == '=') { 6963955d011SMarcel Moolenaar condExpr += 2; 6973955d011SMarcel Moolenaar } else { 6983955d011SMarcel Moolenaar condExpr += 1; 6993955d011SMarcel Moolenaar } 7003955d011SMarcel Moolenaar break; 7013955d011SMarcel Moolenaar default: 7023955d011SMarcel Moolenaar if (!doEval) { 7033955d011SMarcel Moolenaar t = TOK_FALSE; 7043955d011SMarcel Moolenaar goto done; 7053955d011SMarcel Moolenaar } 7063955d011SMarcel Moolenaar /* For .ifxxx "..." check for non-empty string. */ 7073955d011SMarcel Moolenaar if (lhsQuoted) { 7083955d011SMarcel Moolenaar t = lhs[0] != 0; 7093955d011SMarcel Moolenaar goto done; 7103955d011SMarcel Moolenaar } 7113955d011SMarcel Moolenaar /* For .ifxxx <number> compare against zero */ 7123955d011SMarcel Moolenaar if (CondCvtArg(lhs, &left)) { 7133955d011SMarcel Moolenaar t = left != 0.0; 7143955d011SMarcel Moolenaar goto done; 7153955d011SMarcel Moolenaar } 7163955d011SMarcel Moolenaar /* For .if ${...} check for non-empty string (defProc is ifdef). */ 7173955d011SMarcel Moolenaar if (if_info->form[0] == 0) { 7183955d011SMarcel Moolenaar t = lhs[0] != 0; 7193955d011SMarcel Moolenaar goto done; 7203955d011SMarcel Moolenaar } 7213955d011SMarcel Moolenaar /* Otherwise action default test ... */ 7223955d011SMarcel Moolenaar t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot; 7233955d011SMarcel Moolenaar goto done; 7243955d011SMarcel Moolenaar } 7253955d011SMarcel Moolenaar 7263955d011SMarcel Moolenaar while (isspace((unsigned char)*condExpr)) 7273955d011SMarcel Moolenaar condExpr++; 7283955d011SMarcel Moolenaar 7293955d011SMarcel Moolenaar if (*condExpr == '\0') { 7303955d011SMarcel Moolenaar Parse_Error(PARSE_WARNING, 7313955d011SMarcel Moolenaar "Missing right-hand-side of operator"); 7323955d011SMarcel Moolenaar goto done; 7333955d011SMarcel Moolenaar } 7343955d011SMarcel Moolenaar 73528a6bc81SSimon J. Gerraty rhs = CondGetString(doEval, &rhsQuoted, &rhsFree, FALSE); 7363955d011SMarcel Moolenaar if (!rhs) 7373955d011SMarcel Moolenaar goto done; 7383955d011SMarcel Moolenaar 739*3841c287SSimon J. Gerraty if (!doEval) { 740*3841c287SSimon J. Gerraty t = TOK_FALSE; 741*3841c287SSimon J. Gerraty goto done; 742*3841c287SSimon J. Gerraty } 743*3841c287SSimon J. Gerraty 7443955d011SMarcel Moolenaar if (rhsQuoted || lhsQuoted) { 7453955d011SMarcel Moolenaar do_string_compare: 7463955d011SMarcel Moolenaar if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { 7473955d011SMarcel Moolenaar Parse_Error(PARSE_WARNING, 7483955d011SMarcel Moolenaar "String comparison operator should be either == or !="); 7493955d011SMarcel Moolenaar goto done; 7503955d011SMarcel Moolenaar } 7513955d011SMarcel Moolenaar 7523955d011SMarcel Moolenaar if (DEBUG(COND)) { 7533955d011SMarcel Moolenaar fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", 7543955d011SMarcel Moolenaar lhs, rhs, op); 7553955d011SMarcel Moolenaar } 7563955d011SMarcel Moolenaar /* 7573955d011SMarcel Moolenaar * Null-terminate rhs and perform the comparison. 7583955d011SMarcel Moolenaar * t is set to the result. 7593955d011SMarcel Moolenaar */ 7603955d011SMarcel Moolenaar if (*op == '=') { 7613955d011SMarcel Moolenaar t = strcmp(lhs, rhs) == 0; 7623955d011SMarcel Moolenaar } else { 7633955d011SMarcel Moolenaar t = strcmp(lhs, rhs) != 0; 7643955d011SMarcel Moolenaar } 7653955d011SMarcel Moolenaar } else { 7663955d011SMarcel Moolenaar /* 7673955d011SMarcel Moolenaar * rhs is either a float or an integer. Convert both the 7683955d011SMarcel Moolenaar * lhs and the rhs to a double and compare the two. 7693955d011SMarcel Moolenaar */ 7703955d011SMarcel Moolenaar 7713955d011SMarcel Moolenaar if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right)) 7723955d011SMarcel Moolenaar goto do_string_compare; 7733955d011SMarcel Moolenaar 7743955d011SMarcel Moolenaar if (DEBUG(COND)) { 7753955d011SMarcel Moolenaar fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left, 7763955d011SMarcel Moolenaar right, op); 7773955d011SMarcel Moolenaar } 7783955d011SMarcel Moolenaar switch(op[0]) { 7793955d011SMarcel Moolenaar case '!': 7803955d011SMarcel Moolenaar if (op[1] != '=') { 7813955d011SMarcel Moolenaar Parse_Error(PARSE_WARNING, 7823955d011SMarcel Moolenaar "Unknown operator"); 7833955d011SMarcel Moolenaar goto done; 7843955d011SMarcel Moolenaar } 7853955d011SMarcel Moolenaar t = (left != right); 7863955d011SMarcel Moolenaar break; 7873955d011SMarcel Moolenaar case '=': 7883955d011SMarcel Moolenaar if (op[1] != '=') { 7893955d011SMarcel Moolenaar Parse_Error(PARSE_WARNING, 7903955d011SMarcel Moolenaar "Unknown operator"); 7913955d011SMarcel Moolenaar goto done; 7923955d011SMarcel Moolenaar } 7933955d011SMarcel Moolenaar t = (left == right); 7943955d011SMarcel Moolenaar break; 7953955d011SMarcel Moolenaar case '<': 7963955d011SMarcel Moolenaar if (op[1] == '=') { 7973955d011SMarcel Moolenaar t = (left <= right); 7983955d011SMarcel Moolenaar } else { 7993955d011SMarcel Moolenaar t = (left < right); 8003955d011SMarcel Moolenaar } 8013955d011SMarcel Moolenaar break; 8023955d011SMarcel Moolenaar case '>': 8033955d011SMarcel Moolenaar if (op[1] == '=') { 8043955d011SMarcel Moolenaar t = (left >= right); 8053955d011SMarcel Moolenaar } else { 8063955d011SMarcel Moolenaar t = (left > right); 8073955d011SMarcel Moolenaar } 8083955d011SMarcel Moolenaar break; 8093955d011SMarcel Moolenaar } 8103955d011SMarcel Moolenaar } 8113955d011SMarcel Moolenaar 8123955d011SMarcel Moolenaar done: 8133955d011SMarcel Moolenaar free(lhsFree); 8143955d011SMarcel Moolenaar free(rhsFree); 8153955d011SMarcel Moolenaar return t; 8163955d011SMarcel Moolenaar } 8173955d011SMarcel Moolenaar 8183955d011SMarcel Moolenaar static int 819*3841c287SSimon J. Gerraty get_mpt_arg(Boolean doEval, char **linePtr, char **argPtr, const char *func MAKE_ATTR_UNUSED) 8203955d011SMarcel Moolenaar { 8213955d011SMarcel Moolenaar /* 8223955d011SMarcel Moolenaar * Use Var_Parse to parse the spec in parens and return 8233955d011SMarcel Moolenaar * TOK_TRUE if the resulting string is empty. 8243955d011SMarcel Moolenaar */ 8253955d011SMarcel Moolenaar int length; 8263955d011SMarcel Moolenaar void *freeIt; 8273955d011SMarcel Moolenaar char *val; 8283955d011SMarcel Moolenaar char *cp = *linePtr; 8293955d011SMarcel Moolenaar 8303955d011SMarcel Moolenaar /* We do all the work here and return the result as the length */ 8313955d011SMarcel Moolenaar *argPtr = NULL; 8323955d011SMarcel Moolenaar 833*3841c287SSimon J. Gerraty val = Var_Parse(cp - 1, VAR_CMD, doEval ? VARF_WANTRES : 0, &length, &freeIt); 8343955d011SMarcel Moolenaar /* 8353955d011SMarcel Moolenaar * Advance *linePtr to beyond the closing ). Note that 8363955d011SMarcel Moolenaar * we subtract one because 'length' is calculated from 'cp - 1'. 8373955d011SMarcel Moolenaar */ 8383955d011SMarcel Moolenaar *linePtr = cp - 1 + length; 8393955d011SMarcel Moolenaar 8403955d011SMarcel Moolenaar if (val == var_Error) { 8413955d011SMarcel Moolenaar free(freeIt); 8423955d011SMarcel Moolenaar return -1; 8433955d011SMarcel Moolenaar } 8443955d011SMarcel Moolenaar 8453955d011SMarcel Moolenaar /* A variable is empty when it just contains spaces... 4/15/92, christos */ 8463955d011SMarcel Moolenaar while (isspace(*(unsigned char *)val)) 8473955d011SMarcel Moolenaar val++; 8483955d011SMarcel Moolenaar 8493955d011SMarcel Moolenaar /* 8503955d011SMarcel Moolenaar * For consistency with the other functions we can't generate the 8513955d011SMarcel Moolenaar * true/false here. 8523955d011SMarcel Moolenaar */ 8533955d011SMarcel Moolenaar length = *val ? 2 : 1; 8543955d011SMarcel Moolenaar free(freeIt); 8553955d011SMarcel Moolenaar return length; 8563955d011SMarcel Moolenaar } 8573955d011SMarcel Moolenaar 8583955d011SMarcel Moolenaar static Boolean 8593955d011SMarcel Moolenaar CondDoEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED) 8603955d011SMarcel Moolenaar { 8613955d011SMarcel Moolenaar return arglen == 1; 8623955d011SMarcel Moolenaar } 8633955d011SMarcel Moolenaar 8643955d011SMarcel Moolenaar static Token 8653955d011SMarcel Moolenaar compare_function(Boolean doEval) 8663955d011SMarcel Moolenaar { 8673955d011SMarcel Moolenaar static const struct fn_def { 8683955d011SMarcel Moolenaar const char *fn_name; 8693955d011SMarcel Moolenaar int fn_name_len; 870*3841c287SSimon J. Gerraty int (*fn_getarg)(Boolean, char **, char **, const char *); 8713955d011SMarcel Moolenaar Boolean (*fn_proc)(int, const char *); 8723955d011SMarcel Moolenaar } fn_defs[] = { 8733955d011SMarcel Moolenaar { "defined", 7, CondGetArg, CondDoDefined }, 8743955d011SMarcel Moolenaar { "make", 4, CondGetArg, CondDoMake }, 8753955d011SMarcel Moolenaar { "exists", 6, CondGetArg, CondDoExists }, 8763955d011SMarcel Moolenaar { "empty", 5, get_mpt_arg, CondDoEmpty }, 8773955d011SMarcel Moolenaar { "target", 6, CondGetArg, CondDoTarget }, 8783955d011SMarcel Moolenaar { "commands", 8, CondGetArg, CondDoCommands }, 8793955d011SMarcel Moolenaar { NULL, 0, NULL, NULL }, 8803955d011SMarcel Moolenaar }; 8813955d011SMarcel Moolenaar const struct fn_def *fn_def; 8823955d011SMarcel Moolenaar Token t; 8833955d011SMarcel Moolenaar char *arg = NULL; 8843955d011SMarcel Moolenaar int arglen; 8853955d011SMarcel Moolenaar char *cp = condExpr; 8863955d011SMarcel Moolenaar char *cp1; 8873955d011SMarcel Moolenaar 8883955d011SMarcel Moolenaar for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) { 8893955d011SMarcel Moolenaar if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len)) 8903955d011SMarcel Moolenaar continue; 8913955d011SMarcel Moolenaar cp += fn_def->fn_name_len; 8923955d011SMarcel Moolenaar /* There can only be whitespace before the '(' */ 8933955d011SMarcel Moolenaar while (isspace(*(unsigned char *)cp)) 8943955d011SMarcel Moolenaar cp++; 8953955d011SMarcel Moolenaar if (*cp != '(') 8963955d011SMarcel Moolenaar break; 8973955d011SMarcel Moolenaar 898*3841c287SSimon J. Gerraty arglen = fn_def->fn_getarg(doEval, &cp, &arg, fn_def->fn_name); 8993955d011SMarcel Moolenaar if (arglen <= 0) { 9003955d011SMarcel Moolenaar condExpr = cp; 9013955d011SMarcel Moolenaar return arglen < 0 ? TOK_ERROR : TOK_FALSE; 9023955d011SMarcel Moolenaar } 9033955d011SMarcel Moolenaar /* Evaluate the argument using the required function. */ 9043955d011SMarcel Moolenaar t = !doEval || fn_def->fn_proc(arglen, arg); 9053955d011SMarcel Moolenaar free(arg); 9063955d011SMarcel Moolenaar condExpr = cp; 9073955d011SMarcel Moolenaar return t; 9083955d011SMarcel Moolenaar } 9093955d011SMarcel Moolenaar 9103955d011SMarcel Moolenaar /* Push anything numeric through the compare expression */ 9113955d011SMarcel Moolenaar cp = condExpr; 9123955d011SMarcel Moolenaar if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0])) 9133955d011SMarcel Moolenaar return compare_expression(doEval); 9143955d011SMarcel Moolenaar 9153955d011SMarcel Moolenaar /* 9163955d011SMarcel Moolenaar * Most likely we have a naked token to apply the default function to. 9173955d011SMarcel Moolenaar * However ".if a == b" gets here when the "a" is unquoted and doesn't 9183955d011SMarcel Moolenaar * start with a '$'. This surprises people. 9193955d011SMarcel Moolenaar * If what follows the function argument is a '=' or '!' then the syntax 9203955d011SMarcel Moolenaar * would be invalid if we did "defined(a)" - so instead treat as an 9213955d011SMarcel Moolenaar * expression. 9223955d011SMarcel Moolenaar */ 923*3841c287SSimon J. Gerraty arglen = CondGetArg(doEval, &cp, &arg, NULL); 9243955d011SMarcel Moolenaar for (cp1 = cp; isspace(*(unsigned char *)cp1); cp1++) 9253955d011SMarcel Moolenaar continue; 9263955d011SMarcel Moolenaar if (*cp1 == '=' || *cp1 == '!') 9273955d011SMarcel Moolenaar return compare_expression(doEval); 9283955d011SMarcel Moolenaar condExpr = cp; 9293955d011SMarcel Moolenaar 9303955d011SMarcel Moolenaar /* 9313955d011SMarcel Moolenaar * Evaluate the argument using the default function. 9323955d011SMarcel Moolenaar * This path always treats .if as .ifdef. To get here the character 9333955d011SMarcel Moolenaar * after .if must have been taken literally, so the argument cannot 9343955d011SMarcel Moolenaar * be empty - even if it contained a variable expansion. 9353955d011SMarcel Moolenaar */ 9363955d011SMarcel Moolenaar t = !doEval || if_info->defProc(arglen, arg) != if_info->doNot; 9373955d011SMarcel Moolenaar free(arg); 9383955d011SMarcel Moolenaar return t; 9393955d011SMarcel Moolenaar } 9403955d011SMarcel Moolenaar 9413955d011SMarcel Moolenaar static Token 9423955d011SMarcel Moolenaar CondToken(Boolean doEval) 9433955d011SMarcel Moolenaar { 9443955d011SMarcel Moolenaar Token t; 9453955d011SMarcel Moolenaar 9463955d011SMarcel Moolenaar t = condPushBack; 9473955d011SMarcel Moolenaar if (t != TOK_NONE) { 9483955d011SMarcel Moolenaar condPushBack = TOK_NONE; 9493955d011SMarcel Moolenaar return t; 9503955d011SMarcel Moolenaar } 9513955d011SMarcel Moolenaar 9523955d011SMarcel Moolenaar while (*condExpr == ' ' || *condExpr == '\t') { 9533955d011SMarcel Moolenaar condExpr++; 9543955d011SMarcel Moolenaar } 9553955d011SMarcel Moolenaar 9563955d011SMarcel Moolenaar switch (*condExpr) { 9573955d011SMarcel Moolenaar 9583955d011SMarcel Moolenaar case '(': 9593955d011SMarcel Moolenaar condExpr++; 9603955d011SMarcel Moolenaar return TOK_LPAREN; 9613955d011SMarcel Moolenaar 9623955d011SMarcel Moolenaar case ')': 9633955d011SMarcel Moolenaar condExpr++; 9643955d011SMarcel Moolenaar return TOK_RPAREN; 9653955d011SMarcel Moolenaar 9663955d011SMarcel Moolenaar case '|': 9673955d011SMarcel Moolenaar if (condExpr[1] == '|') { 9683955d011SMarcel Moolenaar condExpr++; 9693955d011SMarcel Moolenaar } 9703955d011SMarcel Moolenaar condExpr++; 9713955d011SMarcel Moolenaar return TOK_OR; 9723955d011SMarcel Moolenaar 9733955d011SMarcel Moolenaar case '&': 9743955d011SMarcel Moolenaar if (condExpr[1] == '&') { 9753955d011SMarcel Moolenaar condExpr++; 9763955d011SMarcel Moolenaar } 9773955d011SMarcel Moolenaar condExpr++; 9783955d011SMarcel Moolenaar return TOK_AND; 9793955d011SMarcel Moolenaar 9803955d011SMarcel Moolenaar case '!': 9813955d011SMarcel Moolenaar condExpr++; 9823955d011SMarcel Moolenaar return TOK_NOT; 9833955d011SMarcel Moolenaar 9843955d011SMarcel Moolenaar case '#': 9853955d011SMarcel Moolenaar case '\n': 9863955d011SMarcel Moolenaar case '\0': 9873955d011SMarcel Moolenaar return TOK_EOF; 9883955d011SMarcel Moolenaar 9893955d011SMarcel Moolenaar case '"': 9903955d011SMarcel Moolenaar case '$': 9913955d011SMarcel Moolenaar return compare_expression(doEval); 9923955d011SMarcel Moolenaar 9933955d011SMarcel Moolenaar default: 9943955d011SMarcel Moolenaar return compare_function(doEval); 9953955d011SMarcel Moolenaar } 9963955d011SMarcel Moolenaar } 9973955d011SMarcel Moolenaar 9983955d011SMarcel Moolenaar /*- 9993955d011SMarcel Moolenaar *----------------------------------------------------------------------- 10003955d011SMarcel Moolenaar * CondT -- 10013955d011SMarcel Moolenaar * Parse a single term in the expression. This consists of a terminal 10023955d011SMarcel Moolenaar * symbol or TOK_NOT and a terminal symbol (not including the binary 10033955d011SMarcel Moolenaar * operators): 10043955d011SMarcel Moolenaar * T -> defined(variable) | make(target) | exists(file) | symbol 10053955d011SMarcel Moolenaar * T -> ! T | ( E ) 10063955d011SMarcel Moolenaar * 10073955d011SMarcel Moolenaar * Results: 10083955d011SMarcel Moolenaar * TOK_TRUE, TOK_FALSE or TOK_ERROR. 10093955d011SMarcel Moolenaar * 10103955d011SMarcel Moolenaar * Side Effects: 10113955d011SMarcel Moolenaar * Tokens are consumed. 10123955d011SMarcel Moolenaar * 10133955d011SMarcel Moolenaar *----------------------------------------------------------------------- 10143955d011SMarcel Moolenaar */ 10153955d011SMarcel Moolenaar static Token 10163955d011SMarcel Moolenaar CondT(Boolean doEval) 10173955d011SMarcel Moolenaar { 10183955d011SMarcel Moolenaar Token t; 10193955d011SMarcel Moolenaar 10203955d011SMarcel Moolenaar t = CondToken(doEval); 10213955d011SMarcel Moolenaar 10223955d011SMarcel Moolenaar if (t == TOK_EOF) { 10233955d011SMarcel Moolenaar /* 10243955d011SMarcel Moolenaar * If we reached the end of the expression, the expression 10253955d011SMarcel Moolenaar * is malformed... 10263955d011SMarcel Moolenaar */ 10273955d011SMarcel Moolenaar t = TOK_ERROR; 10283955d011SMarcel Moolenaar } else if (t == TOK_LPAREN) { 10293955d011SMarcel Moolenaar /* 10303955d011SMarcel Moolenaar * T -> ( E ) 10313955d011SMarcel Moolenaar */ 10323955d011SMarcel Moolenaar t = CondE(doEval); 10333955d011SMarcel Moolenaar if (t != TOK_ERROR) { 10343955d011SMarcel Moolenaar if (CondToken(doEval) != TOK_RPAREN) { 10353955d011SMarcel Moolenaar t = TOK_ERROR; 10363955d011SMarcel Moolenaar } 10373955d011SMarcel Moolenaar } 10383955d011SMarcel Moolenaar } else if (t == TOK_NOT) { 10393955d011SMarcel Moolenaar t = CondT(doEval); 10403955d011SMarcel Moolenaar if (t == TOK_TRUE) { 10413955d011SMarcel Moolenaar t = TOK_FALSE; 10423955d011SMarcel Moolenaar } else if (t == TOK_FALSE) { 10433955d011SMarcel Moolenaar t = TOK_TRUE; 10443955d011SMarcel Moolenaar } 10453955d011SMarcel Moolenaar } 1046*3841c287SSimon J. Gerraty return t; 10473955d011SMarcel Moolenaar } 10483955d011SMarcel Moolenaar 10493955d011SMarcel Moolenaar /*- 10503955d011SMarcel Moolenaar *----------------------------------------------------------------------- 10513955d011SMarcel Moolenaar * CondF -- 10523955d011SMarcel Moolenaar * Parse a conjunctive factor (nice name, wot?) 10533955d011SMarcel Moolenaar * F -> T && F | T 10543955d011SMarcel Moolenaar * 10553955d011SMarcel Moolenaar * Results: 10563955d011SMarcel Moolenaar * TOK_TRUE, TOK_FALSE or TOK_ERROR 10573955d011SMarcel Moolenaar * 10583955d011SMarcel Moolenaar * Side Effects: 10593955d011SMarcel Moolenaar * Tokens are consumed. 10603955d011SMarcel Moolenaar * 10613955d011SMarcel Moolenaar *----------------------------------------------------------------------- 10623955d011SMarcel Moolenaar */ 10633955d011SMarcel Moolenaar static Token 10643955d011SMarcel Moolenaar CondF(Boolean doEval) 10653955d011SMarcel Moolenaar { 10663955d011SMarcel Moolenaar Token l, o; 10673955d011SMarcel Moolenaar 10683955d011SMarcel Moolenaar l = CondT(doEval); 10693955d011SMarcel Moolenaar if (l != TOK_ERROR) { 10703955d011SMarcel Moolenaar o = CondToken(doEval); 10713955d011SMarcel Moolenaar 10723955d011SMarcel Moolenaar if (o == TOK_AND) { 10733955d011SMarcel Moolenaar /* 10743955d011SMarcel Moolenaar * F -> T && F 10753955d011SMarcel Moolenaar * 10763955d011SMarcel Moolenaar * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we have to 10773955d011SMarcel Moolenaar * parse the r.h.s. anyway (to throw it away). 10783955d011SMarcel Moolenaar * If T is TOK_TRUE, the result is the r.h.s., be it an TOK_ERROR or no. 10793955d011SMarcel Moolenaar */ 10803955d011SMarcel Moolenaar if (l == TOK_TRUE) { 10813955d011SMarcel Moolenaar l = CondF(doEval); 10823955d011SMarcel Moolenaar } else { 10833955d011SMarcel Moolenaar (void)CondF(FALSE); 10843955d011SMarcel Moolenaar } 10853955d011SMarcel Moolenaar } else { 10863955d011SMarcel Moolenaar /* 10873955d011SMarcel Moolenaar * F -> T 10883955d011SMarcel Moolenaar */ 10893955d011SMarcel Moolenaar CondPushBack(o); 10903955d011SMarcel Moolenaar } 10913955d011SMarcel Moolenaar } 1092*3841c287SSimon J. Gerraty return l; 10933955d011SMarcel Moolenaar } 10943955d011SMarcel Moolenaar 10953955d011SMarcel Moolenaar /*- 10963955d011SMarcel Moolenaar *----------------------------------------------------------------------- 10973955d011SMarcel Moolenaar * CondE -- 10983955d011SMarcel Moolenaar * Main expression production. 10993955d011SMarcel Moolenaar * E -> F || E | F 11003955d011SMarcel Moolenaar * 11013955d011SMarcel Moolenaar * Results: 11023955d011SMarcel Moolenaar * TOK_TRUE, TOK_FALSE or TOK_ERROR. 11033955d011SMarcel Moolenaar * 11043955d011SMarcel Moolenaar * Side Effects: 11053955d011SMarcel Moolenaar * Tokens are, of course, consumed. 11063955d011SMarcel Moolenaar * 11073955d011SMarcel Moolenaar *----------------------------------------------------------------------- 11083955d011SMarcel Moolenaar */ 11093955d011SMarcel Moolenaar static Token 11103955d011SMarcel Moolenaar CondE(Boolean doEval) 11113955d011SMarcel Moolenaar { 11123955d011SMarcel Moolenaar Token l, o; 11133955d011SMarcel Moolenaar 11143955d011SMarcel Moolenaar l = CondF(doEval); 11153955d011SMarcel Moolenaar if (l != TOK_ERROR) { 11163955d011SMarcel Moolenaar o = CondToken(doEval); 11173955d011SMarcel Moolenaar 11183955d011SMarcel Moolenaar if (o == TOK_OR) { 11193955d011SMarcel Moolenaar /* 11203955d011SMarcel Moolenaar * E -> F || E 11213955d011SMarcel Moolenaar * 11223955d011SMarcel Moolenaar * A similar thing occurs for ||, except that here we make sure 11233955d011SMarcel Moolenaar * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s. 11243955d011SMarcel Moolenaar * Once again, if l is TOK_FALSE, the result is the r.h.s. and once 11253955d011SMarcel Moolenaar * again if l is TOK_TRUE, we parse the r.h.s. to throw it away. 11263955d011SMarcel Moolenaar */ 11273955d011SMarcel Moolenaar if (l == TOK_FALSE) { 11283955d011SMarcel Moolenaar l = CondE(doEval); 11293955d011SMarcel Moolenaar } else { 11303955d011SMarcel Moolenaar (void)CondE(FALSE); 11313955d011SMarcel Moolenaar } 11323955d011SMarcel Moolenaar } else { 11333955d011SMarcel Moolenaar /* 11343955d011SMarcel Moolenaar * E -> F 11353955d011SMarcel Moolenaar */ 11363955d011SMarcel Moolenaar CondPushBack(o); 11373955d011SMarcel Moolenaar } 11383955d011SMarcel Moolenaar } 1139*3841c287SSimon J. Gerraty return l; 11403955d011SMarcel Moolenaar } 11413955d011SMarcel Moolenaar 11423955d011SMarcel Moolenaar /*- 11433955d011SMarcel Moolenaar *----------------------------------------------------------------------- 11443955d011SMarcel Moolenaar * Cond_EvalExpression -- 11453955d011SMarcel Moolenaar * Evaluate an expression in the passed line. The expression 11463955d011SMarcel Moolenaar * consists of &&, ||, !, make(target), defined(variable) 11473955d011SMarcel Moolenaar * and parenthetical groupings thereof. 11483955d011SMarcel Moolenaar * 11493955d011SMarcel Moolenaar * Results: 11503955d011SMarcel Moolenaar * COND_PARSE if the condition was valid grammatically 11513955d011SMarcel Moolenaar * COND_INVALID if not a valid conditional. 11523955d011SMarcel Moolenaar * 11533955d011SMarcel Moolenaar * (*value) is set to the boolean value of the condition 11543955d011SMarcel Moolenaar * 11553955d011SMarcel Moolenaar * Side Effects: 11563955d011SMarcel Moolenaar * None. 11573955d011SMarcel Moolenaar * 11583955d011SMarcel Moolenaar *----------------------------------------------------------------------- 11593955d011SMarcel Moolenaar */ 11603955d011SMarcel Moolenaar int 116128a6bc81SSimon J. Gerraty Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprint, Boolean strictLHS) 11623955d011SMarcel Moolenaar { 11633955d011SMarcel Moolenaar static const struct If *dflt_info; 11643955d011SMarcel Moolenaar const struct If *sv_if_info = if_info; 11653955d011SMarcel Moolenaar char *sv_condExpr = condExpr; 11663955d011SMarcel Moolenaar Token sv_condPushBack = condPushBack; 11673955d011SMarcel Moolenaar int rval; 11683955d011SMarcel Moolenaar 116928a6bc81SSimon J. Gerraty lhsStrict = strictLHS; 117028a6bc81SSimon J. Gerraty 11713955d011SMarcel Moolenaar while (*line == ' ' || *line == '\t') 11723955d011SMarcel Moolenaar line++; 11733955d011SMarcel Moolenaar 11743955d011SMarcel Moolenaar if (info == NULL && (info = dflt_info) == NULL) { 11753955d011SMarcel Moolenaar /* Scan for the entry for .if - it can't be first */ 11763955d011SMarcel Moolenaar for (info = ifs; ; info++) 11773955d011SMarcel Moolenaar if (info->form[0] == 0) 11783955d011SMarcel Moolenaar break; 11793955d011SMarcel Moolenaar dflt_info = info; 11803955d011SMarcel Moolenaar } 1181e1cee40dSSimon J. Gerraty assert(info != NULL); 11823955d011SMarcel Moolenaar 1183e1cee40dSSimon J. Gerraty if_info = info; 11843955d011SMarcel Moolenaar condExpr = line; 11853955d011SMarcel Moolenaar condPushBack = TOK_NONE; 11863955d011SMarcel Moolenaar 11873955d011SMarcel Moolenaar rval = do_Cond_EvalExpression(value); 11883955d011SMarcel Moolenaar 11893955d011SMarcel Moolenaar if (rval == COND_INVALID && eprint) 11903955d011SMarcel Moolenaar Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line); 11913955d011SMarcel Moolenaar 11923955d011SMarcel Moolenaar if_info = sv_if_info; 11933955d011SMarcel Moolenaar condExpr = sv_condExpr; 11943955d011SMarcel Moolenaar condPushBack = sv_condPushBack; 11953955d011SMarcel Moolenaar 11963955d011SMarcel Moolenaar return rval; 11973955d011SMarcel Moolenaar } 11983955d011SMarcel Moolenaar 11993955d011SMarcel Moolenaar static int 12003955d011SMarcel Moolenaar do_Cond_EvalExpression(Boolean *value) 12013955d011SMarcel Moolenaar { 12023955d011SMarcel Moolenaar 12033955d011SMarcel Moolenaar switch (CondE(TRUE)) { 12043955d011SMarcel Moolenaar case TOK_TRUE: 12053955d011SMarcel Moolenaar if (CondToken(TRUE) == TOK_EOF) { 12063955d011SMarcel Moolenaar *value = TRUE; 12073955d011SMarcel Moolenaar return COND_PARSE; 12083955d011SMarcel Moolenaar } 12093955d011SMarcel Moolenaar break; 12103955d011SMarcel Moolenaar case TOK_FALSE: 12113955d011SMarcel Moolenaar if (CondToken(TRUE) == TOK_EOF) { 12123955d011SMarcel Moolenaar *value = FALSE; 12133955d011SMarcel Moolenaar return COND_PARSE; 12143955d011SMarcel Moolenaar } 12153955d011SMarcel Moolenaar break; 12163955d011SMarcel Moolenaar default: 12173955d011SMarcel Moolenaar case TOK_ERROR: 12183955d011SMarcel Moolenaar break; 12193955d011SMarcel Moolenaar } 12203955d011SMarcel Moolenaar 12213955d011SMarcel Moolenaar return COND_INVALID; 12223955d011SMarcel Moolenaar } 12233955d011SMarcel Moolenaar 12243955d011SMarcel Moolenaar 12253955d011SMarcel Moolenaar /*- 12263955d011SMarcel Moolenaar *----------------------------------------------------------------------- 12273955d011SMarcel Moolenaar * Cond_Eval -- 12283955d011SMarcel Moolenaar * Evaluate the conditional in the passed line. The line 12293955d011SMarcel Moolenaar * looks like this: 12303955d011SMarcel Moolenaar * .<cond-type> <expr> 12313955d011SMarcel Moolenaar * where <cond-type> is any of if, ifmake, ifnmake, ifdef, 12323955d011SMarcel Moolenaar * ifndef, elif, elifmake, elifnmake, elifdef, elifndef 12333955d011SMarcel Moolenaar * and <expr> consists of &&, ||, !, make(target), defined(variable) 12343955d011SMarcel Moolenaar * and parenthetical groupings thereof. 12353955d011SMarcel Moolenaar * 12363955d011SMarcel Moolenaar * Input: 12373955d011SMarcel Moolenaar * line Line to parse 12383955d011SMarcel Moolenaar * 12393955d011SMarcel Moolenaar * Results: 12403955d011SMarcel Moolenaar * COND_PARSE if should parse lines after the conditional 12413955d011SMarcel Moolenaar * COND_SKIP if should skip lines after the conditional 12423955d011SMarcel Moolenaar * COND_INVALID if not a valid conditional. 12433955d011SMarcel Moolenaar * 12443955d011SMarcel Moolenaar * Side Effects: 12453955d011SMarcel Moolenaar * None. 12463955d011SMarcel Moolenaar * 12473955d011SMarcel Moolenaar * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order 12483955d011SMarcel Moolenaar * to detect splurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF) 12493955d011SMarcel Moolenaar * otherwise .else could be treated as '.elif 1'. 12503955d011SMarcel Moolenaar * 12513955d011SMarcel Moolenaar *----------------------------------------------------------------------- 12523955d011SMarcel Moolenaar */ 12533955d011SMarcel Moolenaar int 12543955d011SMarcel Moolenaar Cond_Eval(char *line) 12553955d011SMarcel Moolenaar { 12563955d011SMarcel Moolenaar #define MAXIF 128 /* maximum depth of .if'ing */ 125759a02420SSimon J. Gerraty #define MAXIF_BUMP 32 /* how much to grow by */ 12583955d011SMarcel Moolenaar enum if_states { 12593955d011SMarcel Moolenaar IF_ACTIVE, /* .if or .elif part active */ 12603955d011SMarcel Moolenaar ELSE_ACTIVE, /* .else part active */ 12613955d011SMarcel Moolenaar SEARCH_FOR_ELIF, /* searching for .elif/else to execute */ 12623955d011SMarcel Moolenaar SKIP_TO_ELSE, /* has been true, but not seen '.else' */ 12633955d011SMarcel Moolenaar SKIP_TO_ENDIF /* nothing else to execute */ 12643955d011SMarcel Moolenaar }; 126559a02420SSimon J. Gerraty static enum if_states *cond_state = NULL; 126659a02420SSimon J. Gerraty static unsigned int max_if_depth = MAXIF; 12673955d011SMarcel Moolenaar 12683955d011SMarcel Moolenaar const struct If *ifp; 12693955d011SMarcel Moolenaar Boolean isElif; 12703955d011SMarcel Moolenaar Boolean value; 12713955d011SMarcel Moolenaar int level; /* Level at which to report errors. */ 12723955d011SMarcel Moolenaar enum if_states state; 12733955d011SMarcel Moolenaar 12743955d011SMarcel Moolenaar level = PARSE_FATAL; 127559a02420SSimon J. Gerraty if (!cond_state) { 127659a02420SSimon J. Gerraty cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state)); 127759a02420SSimon J. Gerraty cond_state[0] = IF_ACTIVE; 127859a02420SSimon J. Gerraty } 12793955d011SMarcel Moolenaar /* skip leading character (the '.') and any whitespace */ 12803955d011SMarcel Moolenaar for (line++; *line == ' ' || *line == '\t'; line++) 12813955d011SMarcel Moolenaar continue; 12823955d011SMarcel Moolenaar 12833955d011SMarcel Moolenaar /* Find what type of if we're dealing with. */ 12843955d011SMarcel Moolenaar if (line[0] == 'e') { 12853955d011SMarcel Moolenaar if (line[1] != 'l') { 12863955d011SMarcel Moolenaar if (!istoken(line + 1, "ndif", 4)) 12873955d011SMarcel Moolenaar return COND_INVALID; 12883955d011SMarcel Moolenaar /* End of conditional section */ 12893955d011SMarcel Moolenaar if (cond_depth == cond_min_depth) { 12903955d011SMarcel Moolenaar Parse_Error(level, "if-less endif"); 12913955d011SMarcel Moolenaar return COND_PARSE; 12923955d011SMarcel Moolenaar } 12933955d011SMarcel Moolenaar /* Return state for previous conditional */ 12943955d011SMarcel Moolenaar cond_depth--; 12953955d011SMarcel Moolenaar return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; 12963955d011SMarcel Moolenaar } 12973955d011SMarcel Moolenaar 12983955d011SMarcel Moolenaar /* Quite likely this is 'else' or 'elif' */ 12993955d011SMarcel Moolenaar line += 2; 13003955d011SMarcel Moolenaar if (istoken(line, "se", 2)) { 13013955d011SMarcel Moolenaar /* It is else... */ 13023955d011SMarcel Moolenaar if (cond_depth == cond_min_depth) { 13033955d011SMarcel Moolenaar Parse_Error(level, "if-less else"); 13043955d011SMarcel Moolenaar return COND_PARSE; 13053955d011SMarcel Moolenaar } 13063955d011SMarcel Moolenaar 13073955d011SMarcel Moolenaar state = cond_state[cond_depth]; 13083955d011SMarcel Moolenaar switch (state) { 13093955d011SMarcel Moolenaar case SEARCH_FOR_ELIF: 13103955d011SMarcel Moolenaar state = ELSE_ACTIVE; 13113955d011SMarcel Moolenaar break; 13123955d011SMarcel Moolenaar case ELSE_ACTIVE: 13133955d011SMarcel Moolenaar case SKIP_TO_ENDIF: 13143955d011SMarcel Moolenaar Parse_Error(PARSE_WARNING, "extra else"); 13153955d011SMarcel Moolenaar /* FALLTHROUGH */ 13163955d011SMarcel Moolenaar default: 13173955d011SMarcel Moolenaar case IF_ACTIVE: 13183955d011SMarcel Moolenaar case SKIP_TO_ELSE: 13193955d011SMarcel Moolenaar state = SKIP_TO_ENDIF; 13203955d011SMarcel Moolenaar break; 13213955d011SMarcel Moolenaar } 13223955d011SMarcel Moolenaar cond_state[cond_depth] = state; 13233955d011SMarcel Moolenaar return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; 13243955d011SMarcel Moolenaar } 13253955d011SMarcel Moolenaar /* Assume for now it is an elif */ 13263955d011SMarcel Moolenaar isElif = TRUE; 13273955d011SMarcel Moolenaar } else 13283955d011SMarcel Moolenaar isElif = FALSE; 13293955d011SMarcel Moolenaar 13303955d011SMarcel Moolenaar if (line[0] != 'i' || line[1] != 'f') 13313955d011SMarcel Moolenaar /* Not an ifxxx or elifxxx line */ 13323955d011SMarcel Moolenaar return COND_INVALID; 13333955d011SMarcel Moolenaar 13343955d011SMarcel Moolenaar /* 13353955d011SMarcel Moolenaar * Figure out what sort of conditional it is -- what its default 13363955d011SMarcel Moolenaar * function is, etc. -- by looking in the table of valid "ifs" 13373955d011SMarcel Moolenaar */ 13383955d011SMarcel Moolenaar line += 2; 13393955d011SMarcel Moolenaar for (ifp = ifs; ; ifp++) { 13403955d011SMarcel Moolenaar if (ifp->form == NULL) 13413955d011SMarcel Moolenaar return COND_INVALID; 13423955d011SMarcel Moolenaar if (istoken(ifp->form, line, ifp->formlen)) { 13433955d011SMarcel Moolenaar line += ifp->formlen; 13443955d011SMarcel Moolenaar break; 13453955d011SMarcel Moolenaar } 13463955d011SMarcel Moolenaar } 13473955d011SMarcel Moolenaar 13483955d011SMarcel Moolenaar /* Now we know what sort of 'if' it is... */ 13493955d011SMarcel Moolenaar 13503955d011SMarcel Moolenaar if (isElif) { 13513955d011SMarcel Moolenaar if (cond_depth == cond_min_depth) { 13523955d011SMarcel Moolenaar Parse_Error(level, "if-less elif"); 13533955d011SMarcel Moolenaar return COND_PARSE; 13543955d011SMarcel Moolenaar } 13553955d011SMarcel Moolenaar state = cond_state[cond_depth]; 13563955d011SMarcel Moolenaar if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) { 13573955d011SMarcel Moolenaar Parse_Error(PARSE_WARNING, "extra elif"); 13583955d011SMarcel Moolenaar cond_state[cond_depth] = SKIP_TO_ENDIF; 13593955d011SMarcel Moolenaar return COND_SKIP; 13603955d011SMarcel Moolenaar } 13613955d011SMarcel Moolenaar if (state != SEARCH_FOR_ELIF) { 13623955d011SMarcel Moolenaar /* Either just finished the 'true' block, or already SKIP_TO_ELSE */ 13633955d011SMarcel Moolenaar cond_state[cond_depth] = SKIP_TO_ELSE; 13643955d011SMarcel Moolenaar return COND_SKIP; 13653955d011SMarcel Moolenaar } 13663955d011SMarcel Moolenaar } else { 13673955d011SMarcel Moolenaar /* Normal .if */ 136859a02420SSimon J. Gerraty if (cond_depth + 1 >= max_if_depth) { 136959a02420SSimon J. Gerraty /* 137059a02420SSimon J. Gerraty * This is rare, but not impossible. 137159a02420SSimon J. Gerraty * In meta mode, dirdeps.mk (only runs at level 0) 137259a02420SSimon J. Gerraty * can need more than the default. 137359a02420SSimon J. Gerraty */ 137459a02420SSimon J. Gerraty max_if_depth += MAXIF_BUMP; 137559a02420SSimon J. Gerraty cond_state = bmake_realloc(cond_state, max_if_depth * 137659a02420SSimon J. Gerraty sizeof(*cond_state)); 13773955d011SMarcel Moolenaar } 13783955d011SMarcel Moolenaar state = cond_state[cond_depth]; 13793955d011SMarcel Moolenaar cond_depth++; 13803955d011SMarcel Moolenaar if (state > ELSE_ACTIVE) { 13813955d011SMarcel Moolenaar /* If we aren't parsing the data, treat as always false */ 13823955d011SMarcel Moolenaar cond_state[cond_depth] = SKIP_TO_ELSE; 13833955d011SMarcel Moolenaar return COND_SKIP; 13843955d011SMarcel Moolenaar } 13853955d011SMarcel Moolenaar } 13863955d011SMarcel Moolenaar 13873955d011SMarcel Moolenaar /* And evaluate the conditional expresssion */ 138828a6bc81SSimon J. Gerraty if (Cond_EvalExpression(ifp, line, &value, 1, TRUE) == COND_INVALID) { 13893955d011SMarcel Moolenaar /* Syntax error in conditional, error message already output. */ 13903955d011SMarcel Moolenaar /* Skip everything to matching .endif */ 13913955d011SMarcel Moolenaar cond_state[cond_depth] = SKIP_TO_ELSE; 13923955d011SMarcel Moolenaar return COND_SKIP; 13933955d011SMarcel Moolenaar } 13943955d011SMarcel Moolenaar 13953955d011SMarcel Moolenaar if (!value) { 13963955d011SMarcel Moolenaar cond_state[cond_depth] = SEARCH_FOR_ELIF; 13973955d011SMarcel Moolenaar return COND_SKIP; 13983955d011SMarcel Moolenaar } 13993955d011SMarcel Moolenaar cond_state[cond_depth] = IF_ACTIVE; 14003955d011SMarcel Moolenaar return COND_PARSE; 14013955d011SMarcel Moolenaar } 14023955d011SMarcel Moolenaar 14033955d011SMarcel Moolenaar 14043955d011SMarcel Moolenaar 14053955d011SMarcel Moolenaar /*- 14063955d011SMarcel Moolenaar *----------------------------------------------------------------------- 14073955d011SMarcel Moolenaar * Cond_End -- 14083955d011SMarcel Moolenaar * Make sure everything's clean at the end of a makefile. 14093955d011SMarcel Moolenaar * 14103955d011SMarcel Moolenaar * Results: 14113955d011SMarcel Moolenaar * None. 14123955d011SMarcel Moolenaar * 14133955d011SMarcel Moolenaar * Side Effects: 14143955d011SMarcel Moolenaar * Parse_Error will be called if open conditionals are around. 14153955d011SMarcel Moolenaar * 14163955d011SMarcel Moolenaar *----------------------------------------------------------------------- 14173955d011SMarcel Moolenaar */ 14183955d011SMarcel Moolenaar void 14193955d011SMarcel Moolenaar Cond_restore_depth(unsigned int saved_depth) 14203955d011SMarcel Moolenaar { 14213955d011SMarcel Moolenaar int open_conds = cond_depth - cond_min_depth; 14223955d011SMarcel Moolenaar 14233955d011SMarcel Moolenaar if (open_conds != 0 || saved_depth > cond_depth) { 14243955d011SMarcel Moolenaar Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds, 14253955d011SMarcel Moolenaar open_conds == 1 ? "" : "s"); 14263955d011SMarcel Moolenaar cond_depth = cond_min_depth; 14273955d011SMarcel Moolenaar } 14283955d011SMarcel Moolenaar 14293955d011SMarcel Moolenaar cond_min_depth = saved_depth; 14303955d011SMarcel Moolenaar } 14313955d011SMarcel Moolenaar 14323955d011SMarcel Moolenaar unsigned int 14333955d011SMarcel Moolenaar Cond_save_depth(void) 14343955d011SMarcel Moolenaar { 14353955d011SMarcel Moolenaar int depth = cond_min_depth; 14363955d011SMarcel Moolenaar 14373955d011SMarcel Moolenaar cond_min_depth = cond_depth; 14383955d011SMarcel Moolenaar return depth; 14393955d011SMarcel Moolenaar } 1440