[BACK]Return to cond.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / usr.bin / make

Annotation of src/usr.bin/make/cond.c, Revision 1.296

1.296   ! rillig      1: /*     $NetBSD: cond.c,v 1.295 2021/12/11 10:41:31 rillig Exp $        */
1.6       christos    2:
1.1       cgd         3: /*
                      4:  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
1.17      agc         5:  * All rights reserved.
                      6:  *
                      7:  * This code is derived from software contributed to Berkeley by
                      8:  * Adam de Boor.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
                     18:  * 3. Neither the name of the University nor the names of its contributors
                     19:  *    may be used to endorse or promote products derived from this software
                     20:  *    without specific prior written permission.
                     21:  *
                     22:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     23:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     24:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     25:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     26:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     27:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     28:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     29:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     30:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     31:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     32:  * SUCH DAMAGE.
                     33:  */
                     34:
                     35: /*
1.1       cgd        36:  * Copyright (c) 1988, 1989 by Adam de Boor
                     37:  * Copyright (c) 1989 by Berkeley Softworks
                     38:  * All rights reserved.
                     39:  *
                     40:  * This code is derived from software contributed to Berkeley by
                     41:  * Adam de Boor.
                     42:  *
                     43:  * Redistribution and use in source and binary forms, with or without
                     44:  * modification, are permitted provided that the following conditions
                     45:  * are met:
                     46:  * 1. Redistributions of source code must retain the above copyright
                     47:  *    notice, this list of conditions and the following disclaimer.
                     48:  * 2. Redistributions in binary form must reproduce the above copyright
                     49:  *    notice, this list of conditions and the following disclaimer in the
                     50:  *    documentation and/or other materials provided with the distribution.
                     51:  * 3. All advertising materials mentioning features or use of this software
                     52:  *    must display the following acknowledgement:
                     53:  *     This product includes software developed by the University of
                     54:  *     California, Berkeley and its contributors.
                     55:  * 4. Neither the name of the University nor the names of its contributors
                     56:  *    may be used to endorse or promote products derived from this software
                     57:  *    without specific prior written permission.
                     58:  *
                     59:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     60:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     61:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     62:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     63:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     64:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     65:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     66:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     67:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     68:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     69:  * SUCH DAMAGE.
                     70:  */
                     71:
1.233     rillig     72: /*
                     73:  * Handling of conditionals in a makefile.
1.1       cgd        74:  *
                     75:  * Interface:
1.209     rillig     76:  *     Cond_EvalLine   Evaluate the conditional directive, such as
                     77:  *                     '.if <cond>', '.elifnmake <cond>', '.else', '.endif'.
1.1       cgd        78:  *
1.142     rillig     79:  *     Cond_EvalCondition
1.168     rillig     80:  *                     Evaluate the conditional, which is either the argument
                     81:  *                     of one of the .if directives or the condition in a
                     82:  *                     ':?then:else' variable modifier.
1.117     rillig     83:  *
                     84:  *     Cond_save_depth
                     85:  *     Cond_restore_depth
                     86:  *                     Save and restore the nesting of the conditions, at
                     87:  *                     the start and end of including another makefile, to
                     88:  *                     ensure that in each makefile the conditional
                     89:  *                     directives are well-balanced.
1.1       cgd        90:  */
                     91:
1.99      rillig     92: #include <errno.h>
                     93:
                     94: #include "make.h"
                     95: #include "dir.h"
1.1       cgd        96:
1.144     rillig     97: /*     "@(#)cond.c     8.2 (Berkeley) 1/2/94"  */
1.296   ! rillig     98: MAKE_RCSID("$NetBSD: cond.c,v 1.295 2021/12/11 10:41:31 rillig Exp $");
1.144     rillig     99:
1.1       cgd       100: /*
                    101:  * The parsing of conditional expressions is based on this grammar:
1.285     rillig    102:  *     Or -> And ('||' And)*
1.288     rillig    103:  *     And -> Term ('&&' Term)*
1.239     rillig    104:  *     Term -> Function '(' Argument ')'
                    105:  *     Term -> Leaf Operator Leaf
                    106:  *     Term -> Leaf
                    107:  *     Term -> '(' Or ')'
                    108:  *     Term -> '!' Term
                    109:  *     Leaf -> "string"
                    110:  *     Leaf -> Number
                    111:  *     Leaf -> VariableExpression
                    112:  *     Leaf -> Symbol
                    113:  *     Operator -> '==' | '!=' | '>' | '<' | '>=' | '<='
1.1       cgd       114:  *
1.239     rillig    115:  * 'Symbol' is an unquoted string literal to which the default function is
                    116:  * applied.
1.1       cgd       117:  *
1.121     rillig    118:  * The tokens are scanned by CondToken, which returns:
1.239     rillig    119:  *     TOK_AND         for '&&'
                    120:  *     TOK_OR          for '||'
1.121     rillig    121:  *     TOK_NOT         for '!'
                    122:  *     TOK_LPAREN      for '('
                    123:  *     TOK_RPAREN      for ')'
1.239     rillig    124:  *
1.121     rillig    125:  * Other terminal symbols are evaluated using either the default function or
                    126:  * the function given in the terminal, they return either TOK_TRUE or
                    127:  * TOK_FALSE.
1.1       cgd       128:  */
1.164     rillig    129: typedef enum Token {
1.241     rillig    130:        TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT,
1.215     rillig    131:        TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
1.1       cgd       132: } Token;
                    133:
1.241     rillig    134: typedef enum CondResult {
                    135:        CR_FALSE, CR_TRUE, CR_ERROR
                    136: } CondResult;
                    137:
1.251     rillig    138: typedef enum ComparisonOp {
                    139:        LT, LE, GT, GE, EQ, NE
                    140: } ComparisonOp;
                    141:
1.150     rillig    142: typedef struct CondParser {
1.243     rillig    143:
                    144:        /*
                    145:         * The plain '.if ${VAR}' evaluates to true if the value of the
                    146:         * expression has length > 0.  The other '.if' variants delegate
                    147:         * to evalBare instead.
                    148:         */
1.260     rillig    149:        bool plain;
1.243     rillig    150:
                    151:        /* The function to apply on unquoted bare words. */
1.260     rillig    152:        bool (*evalBare)(size_t, const char *);
                    153:        bool negateEvalBare;
1.243     rillig    154:
1.276     rillig    155:        /*
1.277     rillig    156:         * Whether the left-hand side of a comparison may be an unquoted
1.276     rillig    157:         * string.  This is allowed for expressions of the form
                    158:         * ${condition:?:}, see ApplyModifier_IfElse.  Such a condition is
                    159:         * expanded before it is evaluated, due to ease of implementation.
                    160:         * This means that at the point where the condition is evaluated,
                    161:         * make cannot know anymore whether the left-hand side had originally
                    162:         * been a variable expression or a plain word.
                    163:         *
                    164:         * In all other contexts, the left-hand side must either be a
                    165:         * variable expression, a quoted string or a number.
                    166:         */
1.277     rillig    167:        bool leftUnquotedOK;
1.276     rillig    168:
1.215     rillig    169:        const char *p;          /* The remaining condition to parse */
                    170:        Token curr;             /* Single push-back token used in parsing */
                    171:
                    172:        /*
                    173:         * Whether an error message has already been printed for this
                    174:         * condition. The first available error message is usually the most
                    175:         * specific one, therefore it makes sense to suppress the standard
                    176:         * "Malformed conditional" message.
                    177:         */
1.260     rillig    178:        bool printedError;
1.121     rillig    179: } CondParser;
1.1       cgd       180:
1.260     rillig    181: static CondResult CondParser_Or(CondParser *par, bool);
1.1       cgd       182:
1.93      rillig    183: static unsigned int cond_depth = 0;    /* current .if nesting level */
                    184: static unsigned int cond_min_depth = 0;        /* depth at makefile open */
1.1       cgd       185:
1.268     rillig    186: /* Names for ComparisonOp. */
1.278     rillig    187: static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" };
1.251     rillig    188:
1.260     rillig    189: static bool
1.280     rillig    190: is_token(const char *str, const char *tok, unsigned char len)
1.26      christos  191: {
1.280     rillig    192:        return strncmp(str, tok, (size_t)len) == 0 && !ch_isalpha(str[len]);
1.26      christos  193: }
                    194:
1.183     rillig    195: static Token
1.260     rillig    196: ToToken(bool cond)
1.183     rillig    197: {
1.215     rillig    198:        return cond ? TOK_TRUE : TOK_FALSE;
1.183     rillig    199: }
                    200:
1.116     rillig    201: static void
1.121     rillig    202: CondParser_SkipWhitespace(CondParser *par)
1.116     rillig    203: {
1.215     rillig    204:        cpp_skip_whitespace(&par->p);
1.116     rillig    205: }
                    206:
1.233     rillig    207: /*
                    208:  * Parse the argument of a built-in function.
1.112     rillig    209:  *
                    210:  * Arguments:
1.149     rillig    211:  *     *pp initially points at the '(',
                    212:  *     upon successful return it points right after the ')'.
1.112     rillig    213:  *
                    214:  *     *out_arg receives the argument as string.
                    215:  *
                    216:  *     func says whether the argument belongs to an actual function, or
                    217:  *     whether the parsed argument is passed to the default function.
                    218:  *
1.233     rillig    219:  * Return the length of the argument, or 0 on error.
                    220:  */
1.161     rillig    221: static size_t
1.260     rillig    222: ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func,
1.215     rillig    223:             char **out_arg)
                    224: {
                    225:        const char *p = *pp;
                    226:        Buffer argBuf;
                    227:        int paren_depth;
                    228:        size_t argLen;
                    229:
                    230:        if (func != NULL)
                    231:                p++;            /* Skip opening '(' - verified by caller */
                    232:
                    233:        if (*p == '\0') {
                    234:                *out_arg = NULL; /* Missing closing parenthesis: */
                    235:                return 0;       /* .if defined( */
                    236:        }
                    237:
                    238:        cpp_skip_hspace(&p);
                    239:
                    240:        Buf_InitSize(&argBuf, 16);
                    241:
                    242:        paren_depth = 0;
                    243:        for (;;) {
                    244:                char ch = *p;
                    245:                if (ch == '\0' || ch == ' ' || ch == '\t')
                    246:                        break;
                    247:                if ((ch == '&' || ch == '|') && paren_depth == 0)
                    248:                        break;
                    249:                if (*p == '$') {
                    250:                        /*
                    251:                         * Parse the variable expression and install it as
                    252:                         * part of the argument if it's valid. We tell
                    253:                         * Var_Parse to complain on an undefined variable,
                    254:                         * (XXX: but Var_Parse ignores that request)
                    255:                         * so we don't need to do it. Nor do we return an
                    256:                         * error, though perhaps we should.
                    257:                         */
1.261     rillig    258:                        VarEvalMode emode = doEval
1.259     rillig    259:                            ? VARE_UNDEFERR
1.258     rillig    260:                            : VARE_PARSE_ONLY;
1.229     rillig    261:                        FStr nestedVal;
1.261     rillig    262:                        (void)Var_Parse(&p, SCOPE_CMDLINE, emode, &nestedVal);
1.215     rillig    263:                        /* TODO: handle errors */
1.229     rillig    264:                        Buf_AddStr(&argBuf, nestedVal.str);
                    265:                        FStr_Done(&nestedVal);
1.215     rillig    266:                        continue;
                    267:                }
                    268:                if (ch == '(')
                    269:                        paren_depth++;
                    270:                else if (ch == ')' && --paren_depth < 0)
                    271:                        break;
                    272:                Buf_AddByte(&argBuf, *p);
                    273:                p++;
                    274:        }
1.50      dsl       275:
1.254     rillig    276:        argLen = argBuf.len;
                    277:        *out_arg = Buf_DoneData(&argBuf);
1.215     rillig    278:
                    279:        cpp_skip_hspace(&p);
                    280:
                    281:        if (func != NULL && *p++ != ')') {
1.253     rillig    282:                Parse_Error(PARSE_FATAL,
                    283:                    "Missing closing parenthesis for %s()", func);
1.260     rillig    284:                par->printedError = true;
1.215     rillig    285:                return 0;
                    286:        }
                    287:
                    288:        *pp = p;
                    289:        return argLen;
1.1       cgd       290: }
1.90      rillig    291:
                    292: /* Test whether the given variable is defined. */
1.234     rillig    293: /*ARGSUSED*/
1.260     rillig    294: static bool
1.161     rillig    295: FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
1.1       cgd       296: {
1.256     rillig    297:        FStr value = Var_Value(SCOPE_CMDLINE, arg);
1.260     rillig    298:        bool result = value.str != NULL;
1.230     rillig    299:        FStr_Done(&value);
1.215     rillig    300:        return result;
1.1       cgd       301: }
1.90      rillig    302:
1.268     rillig    303: /* See if the given target is requested to be made. */
1.234     rillig    304: /*ARGSUSED*/
1.260     rillig    305: static bool
1.161     rillig    306: FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
1.1       cgd       307: {
1.215     rillig    308:        StringListNode *ln;
1.166     rillig    309:
1.219     rillig    310:        for (ln = opts.create.first; ln != NULL; ln = ln->next)
1.215     rillig    311:                if (Str_Match(ln->datum, arg))
1.260     rillig    312:                        return true;
                    313:        return false;
1.1       cgd       314: }
1.90      rillig    315:
                    316: /* See if the given file exists. */
1.234     rillig    317: /*ARGSUSED*/
1.260     rillig    318: static bool
1.161     rillig    319: FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
1.1       cgd       320: {
1.260     rillig    321:        bool result;
1.215     rillig    322:        char *path;
1.1       cgd       323:
1.220     rillig    324:        path = Dir_FindFile(arg, &dirSearchPath);
1.215     rillig    325:        DEBUG2(COND, "exists(%s) result is \"%s\"\n",
                    326:               arg, path != NULL ? path : "");
                    327:        result = path != NULL;
                    328:        free(path);
                    329:        return result;
1.1       cgd       330: }
1.90      rillig    331:
                    332: /* See if the given node exists and is an actual target. */
1.234     rillig    333: /*ARGSUSED*/
1.260     rillig    334: static bool
1.161     rillig    335: FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
1.1       cgd       336: {
1.215     rillig    337:        GNode *gn = Targ_FindNode(arg);
                    338:        return gn != NULL && GNode_IsTarget(gn);
1.1       cgd       339: }
                    340:
1.233     rillig    341: /*
                    342:  * See if the given node exists and is an actual target with commands
                    343:  * associated with it.
                    344:  */
1.234     rillig    345: /*ARGSUSED*/
1.260     rillig    346: static bool
1.161     rillig    347: FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
1.12      christos  348: {
1.215     rillig    349:        GNode *gn = Targ_FindNode(arg);
1.218     rillig    350:        return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(&gn->commands);
1.12      christos  351: }
1.90      rillig    352:
1.189     rillig    353: /*
1.90      rillig    354:  * Convert the given number into a double.
                    355:  * We try a base 10 or 16 integer conversion first, if that fails
                    356:  * then we try a floating point conversion instead.
1.1       cgd       357:  *
                    358:  * Results:
1.260     rillig    359:  *     Returns true if the conversion succeeded.
1.189     rillig    360:  *     Sets 'out_value' to the converted number.
1.1       cgd       361:  */
1.260     rillig    362: static bool
1.189     rillig    363: TryParseNumber(const char *str, double *out_value)
1.1       cgd       364: {
1.215     rillig    365:        char *end;
                    366:        unsigned long ul_val;
                    367:        double dbl_val;
                    368:
                    369:        if (str[0] == '\0') {   /* XXX: why is an empty string a number? */
                    370:                *out_value = 0.0;
1.260     rillig    371:                return true;
1.215     rillig    372:        }
1.189     rillig    373:
1.270     rillig    374:        errno = 0;
1.215     rillig    375:        ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10);
                    376:        if (*end == '\0' && errno != ERANGE) {
                    377:                *out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val;
1.260     rillig    378:                return true;
1.215     rillig    379:        }
1.46      dsl       380:
1.215     rillig    381:        if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E')
1.260     rillig    382:                return false;   /* skip the expensive strtod call */
1.215     rillig    383:        dbl_val = strtod(str, &end);
                    384:        if (*end != '\0')
1.260     rillig    385:                return false;
1.189     rillig    386:
1.215     rillig    387:        *out_value = dbl_val;
1.260     rillig    388:        return true;
1.1       cgd       389: }
1.46      dsl       390:
1.260     rillig    391: static bool
1.140     rillig    392: is_separator(char ch)
                    393: {
1.267     rillig    394:        return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' ||
                    395:               ch == '>' || ch == '<' || ch == ')' /* but not '(' */;
1.140     rillig    396: }
                    397:
1.242     rillig    398: /*
1.248     rillig    399:  * In a quoted or unquoted string literal or a number, parse a variable
                    400:  * expression.
                    401:  *
                    402:  * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX}
                    403:  */
1.260     rillig    404: static bool
1.248     rillig    405: CondParser_StringExpr(CondParser *par, const char *start,
1.274     rillig    406:                      bool doEval, bool quoted,
                    407:                      Buffer *buf, FStr *inout_str)
1.248     rillig    408: {
1.261     rillig    409:        VarEvalMode emode;
1.248     rillig    410:        const char *nested_p;
1.260     rillig    411:        bool atStart;
1.248     rillig    412:        VarParseResult parseResult;
                    413:
1.275     rillig    414:        emode = doEval && quoted ? VARE_WANTRES
                    415:            : doEval ? VARE_UNDEFERR
1.258     rillig    416:            : VARE_PARSE_ONLY;
1.248     rillig    417:
                    418:        nested_p = par->p;
                    419:        atStart = nested_p == start;
1.261     rillig    420:        parseResult = Var_Parse(&nested_p, SCOPE_CMDLINE, emode, inout_str);
1.248     rillig    421:        /* TODO: handle errors */
                    422:        if (inout_str->str == var_Error) {
                    423:                if (parseResult == VPR_ERR) {
                    424:                        /*
1.249     rillig    425:                         * FIXME: Even if an error occurs, there is no
                    426:                         *  guarantee that it is reported.
1.248     rillig    427:                         *
                    428:                         * See cond-token-plain.mk $$$$$$$$.
                    429:                         */
1.260     rillig    430:                        par->printedError = true;
1.248     rillig    431:                }
                    432:                /*
1.249     rillig    433:                 * XXX: Can there be any situation in which a returned
1.257     rillig    434:                 * var_Error needs to be freed?
1.248     rillig    435:                 */
                    436:                FStr_Done(inout_str);
                    437:                /*
1.249     rillig    438:                 * Even if !doEval, we still report syntax errors, which is
                    439:                 * what getting var_Error back with !doEval means.
1.248     rillig    440:                 */
                    441:                *inout_str = FStr_InitRefer(NULL);
1.260     rillig    442:                return false;
1.248     rillig    443:        }
                    444:        par->p = nested_p;
                    445:
                    446:        /*
1.249     rillig    447:         * If the '$' started the string literal (which means no quotes), and
                    448:         * the variable expression is followed by a space, looks like a
                    449:         * comparison operator or is the end of the expression, we are done.
1.248     rillig    450:         */
                    451:        if (atStart && is_separator(par->p[0]))
1.260     rillig    452:                return false;
1.248     rillig    453:
                    454:        Buf_AddStr(buf, inout_str->str);
                    455:        FStr_Done(inout_str);
                    456:        *inout_str = FStr_InitRefer(NULL); /* not finished yet */
1.260     rillig    457:        return true;
1.248     rillig    458: }
                    459:
                    460: /*
1.266     rillig    461:  * Parse a string from a variable expression or an optionally quoted
                    462:  * string.  This is called for the left-hand and right-hand sides of
                    463:  * comparisons.
1.23      sjg       464:  *
                    465:  * Results:
1.90      rillig    466:  *     Returns the string, absent any quotes, or NULL on error.
1.266     rillig    467:  *     Sets out_quoted if the leaf was a quoted string literal.
1.23      sjg       468:  */
1.227     rillig    469: static void
1.277     rillig    470: CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
1.260     rillig    471:                  FStr *out_str, bool *out_quoted)
1.23      sjg       472: {
1.215     rillig    473:        Buffer buf;
1.228     rillig    474:        FStr str;
1.260     rillig    475:        bool quoted;
1.215     rillig    476:        const char *start;
                    477:
                    478:        Buf_Init(&buf);
1.228     rillig    479:        str = FStr_InitRefer(NULL);
1.215     rillig    480:        *out_quoted = quoted = par->p[0] == '"';
                    481:        start = par->p;
                    482:        if (quoted)
1.122     rillig    483:                par->p++;
1.227     rillig    484:
1.228     rillig    485:        while (par->p[0] != '\0' && str.str == NULL) {
1.215     rillig    486:                switch (par->p[0]) {
                    487:                case '\\':
                    488:                        par->p++;
                    489:                        if (par->p[0] != '\0') {
                    490:                                Buf_AddByte(&buf, par->p[0]);
                    491:                                par->p++;
                    492:                        }
                    493:                        continue;
                    494:                case '"':
                    495:                        par->p++;
1.246     rillig    496:                        if (quoted)
                    497:                                goto got_str;   /* skip the closing quote */
                    498:                        Buf_AddByte(&buf, '"');
1.215     rillig    499:                        continue;
                    500:                case ')':       /* see is_separator */
                    501:                case '!':
                    502:                case '=':
                    503:                case '>':
                    504:                case '<':
                    505:                case ' ':
                    506:                case '\t':
                    507:                        if (!quoted)
                    508:                                goto got_str;
                    509:                        Buf_AddByte(&buf, par->p[0]);
                    510:                        par->p++;
                    511:                        continue;
                    512:                case '$':
1.248     rillig    513:                        if (!CondParser_StringExpr(par,
                    514:                            start, doEval, quoted, &buf, &str))
1.215     rillig    515:                                goto cleanup;
                    516:                        continue;
                    517:                default:
1.277     rillig    518:                        if (!unquotedOK && !quoted && *start != '$' &&
1.215     rillig    519:                            !ch_isdigit(*start)) {
                    520:                                /*
                    521:                                 * The left-hand side must be quoted,
1.277     rillig    522:                                 * a variable expression or a number.
1.215     rillig    523:                                 */
1.228     rillig    524:                                str = FStr_InitRefer(NULL);
1.215     rillig    525:                                goto cleanup;
                    526:                        }
                    527:                        Buf_AddByte(&buf, par->p[0]);
                    528:                        par->p++;
                    529:                        continue;
1.30      christos  530:                }
1.23      sjg       531:        }
1.93      rillig    532: got_str:
1.254     rillig    533:        str = FStr_InitOwn(buf.data);
1.93      rillig    534: cleanup:
1.266     rillig    535:        Buf_DoneData(&buf);     /* XXX: memory leak on failure? */
1.227     rillig    536:        *out_str = str;
1.23      sjg       537: }
1.89      rillig    538:
1.260     rillig    539: static bool
1.265     rillig    540: EvalBare(const CondParser *par, const char *arg, size_t arglen)
1.176     rillig    541: {
1.260     rillig    542:        bool res = par->evalBare(arglen, arg);
1.243     rillig    543:        return par->negateEvalBare ? !res : res;
1.176     rillig    544: }
                    545:
1.233     rillig    546: /*
                    547:  * Evaluate a "comparison without operator", such as in ".if ${VAR}" or
                    548:  * ".if 0".
                    549:  */
1.260     rillig    550: static bool
                    551: EvalNotEmpty(CondParser *par, const char *value, bool quoted)
1.131     rillig    552: {
1.215     rillig    553:        double num;
1.131     rillig    554:
1.215     rillig    555:        /* For .ifxxx "...", check for non-empty string. */
                    556:        if (quoted)
                    557:                return value[0] != '\0';
                    558:
                    559:        /* For .ifxxx <number>, compare against zero */
                    560:        if (TryParseNumber(value, &num))
                    561:                return num != 0.0;
                    562:
                    563:        /* For .if ${...}, check for non-empty string.  This is different from
                    564:         * the evaluation function from that .if variant, which would test
                    565:         * whether a variable of the given name were defined. */
1.295     rillig    566:        /*
                    567:         * XXX: Whitespace should count as empty, just as in
                    568:         * CondParser_FuncCallEmpty.
                    569:         */
1.243     rillig    570:        if (par->plain)
1.215     rillig    571:                return value[0] != '\0';
1.131     rillig    572:
1.265     rillig    573:        return EvalBare(par, value, strlen(value));
1.131     rillig    574: }
                    575:
1.133     rillig    576: /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
1.260     rillig    577: static bool
1.251     rillig    578: EvalCompareNum(double lhs, ComparisonOp op, double rhs)
1.130     rillig    579: {
1.251     rillig    580:        DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, opname[op]);
1.130     rillig    581:
1.251     rillig    582:        switch (op) {
                    583:        case LT:
                    584:                return lhs < rhs;
                    585:        case LE:
                    586:                return lhs <= rhs;
                    587:        case GT:
                    588:                return lhs > rhs;
                    589:        case GE:
                    590:                return lhs >= rhs;
                    591:        case NE:
                    592:                return lhs != rhs;
                    593:        default:
                    594:                return lhs == rhs;
1.215     rillig    595:        }
1.130     rillig    596: }
                    597:
1.133     rillig    598: static Token
1.252     rillig    599: EvalCompareStr(CondParser *par, const char *lhs,
                    600:               ComparisonOp op, const char *rhs)
1.133     rillig    601: {
1.251     rillig    602:        if (op != EQ && op != NE) {
1.252     rillig    603:                Parse_Error(PARSE_FATAL,
1.251     rillig    604:                    "String comparison operator must be either == or !=");
1.260     rillig    605:                par->printedError = true;
1.215     rillig    606:                return TOK_ERROR;
                    607:        }
1.133     rillig    608:
1.251     rillig    609:        DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
                    610:            lhs, rhs, opname[op]);
                    611:        return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0));
1.133     rillig    612: }
                    613:
                    614: /* Evaluate a comparison, such as "${VAR} == 12345". */
                    615: static Token
1.260     rillig    616: EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted,
                    617:            ComparisonOp op, const char *rhs, bool rhsQuoted)
1.133     rillig    618: {
1.215     rillig    619:        double left, right;
1.133     rillig    620:
1.215     rillig    621:        if (!rhsQuoted && !lhsQuoted)
                    622:                if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
1.251     rillig    623:                        return ToToken(EvalCompareNum(left, op, right));
1.133     rillig    624:
1.252     rillig    625:        return EvalCompareStr(par, lhs, op, rhs);
1.133     rillig    626: }
                    627:
1.260     rillig    628: static bool
1.251     rillig    629: CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
                    630: {
                    631:        const char *p = par->p;
                    632:
                    633:        if (p[0] == '<' && p[1] == '=') {
                    634:                *out_op = LE;
                    635:                goto length_2;
                    636:        } else if (p[0] == '<') {
                    637:                *out_op = LT;
                    638:                goto length_1;
                    639:        } else if (p[0] == '>' && p[1] == '=') {
                    640:                *out_op = GE;
                    641:                goto length_2;
                    642:        } else if (p[0] == '>') {
                    643:                *out_op = GT;
                    644:                goto length_1;
                    645:        } else if (p[0] == '=' && p[1] == '=') {
                    646:                *out_op = EQ;
                    647:                goto length_2;
                    648:        } else if (p[0] == '!' && p[1] == '=') {
                    649:                *out_op = NE;
                    650:                goto length_2;
                    651:        }
1.260     rillig    652:        return false;
1.251     rillig    653:
                    654: length_2:
                    655:        par->p = p + 2;
1.260     rillig    656:        return true;
1.251     rillig    657: length_1:
                    658:        par->p = p + 1;
1.260     rillig    659:        return true;
1.251     rillig    660: }
                    661:
1.233     rillig    662: /*
                    663:  * Parse a comparison condition such as:
1.129     rillig    664:  *
                    665:  *     0
                    666:  *     ${VAR:Mpattern}
                    667:  *     ${VAR} == value
                    668:  *     ${VAR:U0} < 12345
                    669:  */
1.1       cgd       670: static Token
1.260     rillig    671: CondParser_Comparison(CondParser *par, bool doEval)
1.44      dsl       672: {
1.215     rillig    673:        Token t = TOK_ERROR;
1.228     rillig    674:        FStr lhs, rhs;
1.251     rillig    675:        ComparisonOp op;
1.260     rillig    676:        bool lhsQuoted, rhsQuoted;
1.215     rillig    677:
1.277     rillig    678:        CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhs, &lhsQuoted);
1.228     rillig    679:        if (lhs.str == NULL)
1.215     rillig    680:                goto done_lhs;
                    681:
                    682:        CondParser_SkipWhitespace(par);
                    683:
1.251     rillig    684:        if (!CondParser_ComparisonOp(par, &op)) {
1.215     rillig    685:                /* Unknown operator, compare against an empty string or 0. */
1.228     rillig    686:                t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted));
1.215     rillig    687:                goto done_lhs;
                    688:        }
                    689:
                    690:        CondParser_SkipWhitespace(par);
                    691:
                    692:        if (par->p[0] == '\0') {
1.252     rillig    693:                Parse_Error(PARSE_FATAL,
1.269     rillig    694:                    "Missing right-hand side of operator '%s'", opname[op]);
1.260     rillig    695:                par->printedError = true;
1.215     rillig    696:                goto done_lhs;
                    697:        }
                    698:
1.277     rillig    699:        CondParser_Leaf(par, doEval, true, &rhs, &rhsQuoted);
1.228     rillig    700:        if (rhs.str == NULL)
1.215     rillig    701:                goto done_rhs;
                    702:
                    703:        if (!doEval) {
                    704:                t = TOK_FALSE;
                    705:                goto done_rhs;
                    706:        }
1.79      sjg       707:
1.252     rillig    708:        t = EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
1.58      dsl       709:
1.185     rillig    710: done_rhs:
1.228     rillig    711:        FStr_Done(&rhs);
1.185     rillig    712: done_lhs:
1.228     rillig    713:        FStr_Done(&lhs);
1.215     rillig    714:        return t;
1.44      dsl       715: }
                    716:
1.233     rillig    717: /*
                    718:  * The argument to empty() is a variable name, optionally followed by
                    719:  * variable modifiers.
                    720:  */
1.295     rillig    721: static bool
                    722: CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
1.47      dsl       723: {
1.295     rillig    724:        const char *cp = par->p;
                    725:        Token tok;
1.229     rillig    726:        FStr val;
1.215     rillig    727:
1.295     rillig    728:        if (!is_token(cp, "empty", 5))
                    729:                return false;
                    730:        cp += 5;
                    731:
                    732:        cpp_skip_whitespace(&cp);
                    733:        if (*cp != '(')
                    734:                return false;
                    735:
                    736:        cp--;                   /* Make cp[1] point to the '('. */
                    737:        (void)Var_Parse(&cp, SCOPE_CMDLINE,
1.258     rillig    738:            doEval ? VARE_WANTRES : VARE_PARSE_ONLY, &val);
1.215     rillig    739:        /* TODO: handle errors */
                    740:
1.293     rillig    741:        if (val.str == var_Error)
                    742:                tok = TOK_ERROR;
                    743:        else {
                    744:                cpp_skip_whitespace(&val.str);
1.296   ! rillig    745:                tok = val.str[0] != '\0' && doEval ? TOK_FALSE : TOK_TRUE;
1.215     rillig    746:        }
                    747:
1.229     rillig    748:        FStr_Done(&val);
1.294     rillig    749:        *out_token = tok;
1.290     rillig    750:        par->p = cp;
                    751:        return true;
                    752: }
                    753:
1.263     rillig    754: /* Parse a function call expression, such as 'defined(${file})'. */
1.260     rillig    755: static bool
1.263     rillig    756: CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
1.44      dsl       757: {
1.215     rillig    758:        static const struct fn_def {
1.279     rillig    759:                const char fn_name[9];
                    760:                unsigned char fn_name_len;
1.260     rillig    761:                bool (*fn_eval)(size_t, const char *);
1.215     rillig    762:        } fns[] = {
1.290     rillig    763:                { "defined",  7, FuncDefined },
                    764:                { "make",     4, FuncMake },
                    765:                { "exists",   6, FuncExists },
                    766:                { "target",   6, FuncTarget },
                    767:                { "commands", 8, FuncCommands }
1.215     rillig    768:        };
                    769:        const struct fn_def *fn;
                    770:        char *arg = NULL;
                    771:        size_t arglen;
                    772:        const char *cp = par->p;
1.273     rillig    773:        const struct fn_def *last_fn = fns + sizeof fns / sizeof fns[0] - 1;
1.215     rillig    774:
1.272     rillig    775:        for (fn = fns; !is_token(cp, fn->fn_name, fn->fn_name_len); fn++)
1.273     rillig    776:                if (fn == last_fn)
1.272     rillig    777:                        return false;
                    778:
                    779:        cp += fn->fn_name_len;
                    780:        cpp_skip_whitespace(&cp);
                    781:        if (*cp != '(')
                    782:                return false;
1.215     rillig    783:
1.290     rillig    784:        arglen = ParseFuncArg(par, &cp, doEval, fn->fn_name, &arg);
1.272     rillig    785:        if (arglen == 0 || arglen == (size_t)-1) {
1.215     rillig    786:                par->p = cp;
1.272     rillig    787:                *out_token = arglen == 0 ? TOK_FALSE : TOK_ERROR;
1.260     rillig    788:                return true;
1.44      dsl       789:        }
1.200     rillig    790:
1.272     rillig    791:        /* Evaluate the argument using the required function. */
                    792:        *out_token = ToToken(!doEval || fn->fn_eval(arglen, arg));
                    793:        free(arg);
                    794:        par->p = cp;
                    795:        return true;
1.196     rillig    796: }
                    797:
1.233     rillig    798: /*
1.266     rillig    799:  * Parse a comparison such as '${VAR} == "value"', or a simple leaf without
                    800:  * operator, which is a number, a variable expression or a string literal.
1.233     rillig    801:  */
1.196     rillig    802: static Token
1.266     rillig    803: CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
1.196     rillig    804: {
1.215     rillig    805:        Token t;
                    806:        char *arg = NULL;
                    807:        size_t arglen;
1.221     rillig    808:        const char *cp;
1.215     rillig    809:        const char *cp1;
                    810:
                    811:        /* Push anything numeric through the compare expression */
                    812:        cp = par->p;
                    813:        if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
                    814:                return CondParser_Comparison(par, doEval);
1.196     rillig    815:
1.215     rillig    816:        /*
                    817:         * Most likely we have a naked token to apply the default function to.
                    818:         * However ".if a == b" gets here when the "a" is unquoted and doesn't
                    819:         * start with a '$'. This surprises people.
                    820:         * If what follows the function argument is a '=' or '!' then the
                    821:         * syntax would be invalid if we did "defined(a)" - so instead treat
                    822:         * as an expression.
                    823:         */
1.266     rillig    824:        /*
                    825:         * XXX: Is it possible to have a variable expression evaluated twice
                    826:         *  at this point?
                    827:         */
1.253     rillig    828:        arglen = ParseFuncArg(par, &cp, doEval, NULL, &arg);
1.215     rillig    829:        cp1 = cp;
                    830:        cpp_skip_whitespace(&cp1);
1.262     rillig    831:        if (*cp1 == '=' || *cp1 == '!' || *cp1 == '<' || *cp1 == '>')
1.215     rillig    832:                return CondParser_Comparison(par, doEval);
                    833:        par->p = cp;
                    834:
                    835:        /*
                    836:         * Evaluate the argument using the default function.
                    837:         * This path always treats .if as .ifdef. To get here, the character
                    838:         * after .if must have been taken literally, so the argument cannot
                    839:         * be empty - even if it contained a variable expansion.
                    840:         */
1.265     rillig    841:        t = ToToken(!doEval || EvalBare(par, arg, arglen));
1.215     rillig    842:        free(arg);
1.44      dsl       843:        return t;
                    844: }
                    845:
1.121     rillig    846: /* Return the next token or comparison result from the parser. */
1.44      dsl       847: static Token
1.260     rillig    848: CondParser_Token(CondParser *par, bool doEval)
1.1       cgd       849: {
1.215     rillig    850:        Token t;
                    851:
                    852:        t = par->curr;
                    853:        if (t != TOK_NONE) {
                    854:                par->curr = TOK_NONE;
                    855:                return t;
                    856:        }
                    857:
                    858:        cpp_skip_hspace(&par->p);
                    859:
                    860:        switch (par->p[0]) {
                    861:
                    862:        case '(':
                    863:                par->p++;
                    864:                return TOK_LPAREN;
                    865:
                    866:        case ')':
                    867:                par->p++;
                    868:                return TOK_RPAREN;
                    869:
                    870:        case '|':
                    871:                par->p++;
                    872:                if (par->p[0] == '|')
                    873:                        par->p++;
1.231     rillig    874:                else if (opts.strict) {
1.215     rillig    875:                        Parse_Error(PARSE_FATAL, "Unknown operator '|'");
1.260     rillig    876:                        par->printedError = true;
1.215     rillig    877:                        return TOK_ERROR;
                    878:                }
                    879:                return TOK_OR;
                    880:
                    881:        case '&':
                    882:                par->p++;
                    883:                if (par->p[0] == '&')
                    884:                        par->p++;
1.231     rillig    885:                else if (opts.strict) {
1.215     rillig    886:                        Parse_Error(PARSE_FATAL, "Unknown operator '&'");
1.260     rillig    887:                        par->printedError = true;
1.215     rillig    888:                        return TOK_ERROR;
                    889:                }
                    890:                return TOK_AND;
1.47      dsl       891:
1.215     rillig    892:        case '!':
                    893:                par->p++;
                    894:                return TOK_NOT;
1.47      dsl       895:
1.215     rillig    896:        case '#':               /* XXX: see unit-tests/cond-token-plain.mk */
                    897:        case '\n':              /* XXX: why should this end the condition? */
                    898:                /* Probably obsolete now, from 1993-03-21. */
                    899:        case '\0':
                    900:                return TOK_EOF;
1.47      dsl       901:
1.215     rillig    902:        case '"':
                    903:        case '$':
                    904:                return CondParser_Comparison(par, doEval);
1.47      dsl       905:
1.215     rillig    906:        default:
1.290     rillig    907:                if (CondParser_FuncCallEmpty(par, doEval, &t))
                    908:                        return t;
1.264     rillig    909:                if (CondParser_FuncCall(par, doEval, &t))
                    910:                        return t;
1.266     rillig    911:                return CondParser_ComparisonOrLeaf(par, doEval);
1.215     rillig    912:        }
1.1       cgd       913: }
1.47      dsl       914:
1.289     rillig    915: /* Skip the next token if it equals t. */
                    916: static bool
                    917: CondParser_Skip(CondParser *par, Token t)
                    918: {
                    919:        Token actual;
                    920:
                    921:        actual = CondParser_Token(par, false);
                    922:        if (actual == t)
                    923:                return true;
                    924:
                    925:        assert(par->curr == TOK_NONE);
                    926:        assert(actual != TOK_NONE);
                    927:        par->curr = actual;
                    928:        return false;
                    929: }
                    930:
1.233     rillig    931: /*
1.239     rillig    932:  * Term -> '(' Or ')'
                    933:  * Term -> '!' Term
                    934:  * Term -> Leaf Operator Leaf
                    935:  * Term -> Leaf
1.1       cgd       936:  */
1.241     rillig    937: static CondResult
1.260     rillig    938: CondParser_Term(CondParser *par, bool doEval)
1.1       cgd       939: {
1.241     rillig    940:        CondResult res;
1.215     rillig    941:        Token t;
1.1       cgd       942:
1.215     rillig    943:        t = CondParser_Token(par, doEval);
1.241     rillig    944:        if (t == TOK_TRUE)
                    945:                return CR_TRUE;
                    946:        if (t == TOK_FALSE)
                    947:                return CR_FALSE;
1.1       cgd       948:
1.237     rillig    949:        if (t == TOK_LPAREN) {
1.241     rillig    950:                res = CondParser_Or(par, doEval);
                    951:                if (res == CR_ERROR)
                    952:                        return CR_ERROR;
1.239     rillig    953:                if (CondParser_Token(par, doEval) != TOK_RPAREN)
1.241     rillig    954:                        return CR_ERROR;
                    955:                return res;
1.239     rillig    956:        }
                    957:
                    958:        if (t == TOK_NOT) {
1.241     rillig    959:                res = CondParser_Term(par, doEval);
                    960:                if (res == CR_TRUE)
                    961:                        res = CR_FALSE;
                    962:                else if (res == CR_FALSE)
                    963:                        res = CR_TRUE;
                    964:                return res;
1.239     rillig    965:        }
1.236     rillig    966:
1.241     rillig    967:        return CR_ERROR;
1.1       cgd       968: }
1.90      rillig    969:
1.233     rillig    970: /*
1.288     rillig    971:  * And -> Term ('&&' Term)*
1.1       cgd       972:  */
1.241     rillig    973: static CondResult
1.260     rillig    974: CondParser_And(CondParser *par, bool doEval)
1.1       cgd       975: {
1.288     rillig    976:        CondResult res, rhs;
1.1       cgd       977:
1.288     rillig    978:        res = CR_TRUE;
                    979:        do {
                    980:                if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR)
1.241     rillig    981:                        return CR_ERROR;
1.288     rillig    982:                if (rhs == CR_FALSE) {
                    983:                        res = CR_FALSE;
                    984:                        doEval = false;
                    985:                }
1.289     rillig    986:        } while (CondParser_Skip(par, TOK_AND));
1.239     rillig    987:
                    988:        return res;
1.1       cgd       989: }
1.90      rillig    990:
1.233     rillig    991: /*
1.285     rillig    992:  * Or -> And ('||' And)*
1.1       cgd       993:  */
1.241     rillig    994: static CondResult
1.260     rillig    995: CondParser_Or(CondParser *par, bool doEval)
1.1       cgd       996: {
1.285     rillig    997:        CondResult res, rhs;
1.1       cgd       998:
1.285     rillig    999:        res = CR_FALSE;
                   1000:        do {
                   1001:                if ((rhs = CondParser_And(par, doEval)) == CR_ERROR)
1.241     rillig   1002:                        return CR_ERROR;
1.285     rillig   1003:                if (rhs == CR_TRUE) {
                   1004:                        res = CR_TRUE;
                   1005:                        doEval = false;
                   1006:                }
1.289     rillig   1007:        } while (CondParser_Skip(par, TOK_OR));
1.239     rillig   1008:
                   1009:        return res;
1.1       cgd      1010: }
1.10      christos 1011:
1.89      rillig   1012: static CondEvalResult
1.260     rillig   1013: CondParser_Eval(CondParser *par, bool *out_value)
1.89      rillig   1014: {
1.241     rillig   1015:        CondResult res;
1.128     rillig   1016:
1.215     rillig   1017:        DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
1.128     rillig   1018:
1.260     rillig   1019:        res = CondParser_Or(par, true);
1.241     rillig   1020:        if (res == CR_ERROR)
1.215     rillig   1021:                return COND_INVALID;
1.89      rillig   1022:
1.260     rillig   1023:        if (CondParser_Token(par, false) != TOK_EOF)
1.215     rillig   1024:                return COND_INVALID;
1.89      rillig   1025:
1.243     rillig   1026:        *out_value = res == CR_TRUE;
1.215     rillig   1027:        return COND_PARSE;
1.89      rillig   1028: }
                   1029:
1.233     rillig   1030: /*
                   1031:  * Evaluate the condition, including any side effects from the variable
1.128     rillig   1032:  * expressions in the condition. The condition consists of &&, ||, !,
                   1033:  * function(arg), comparisons and parenthetical groupings thereof.
1.10      christos 1034:  *
                   1035:  * Results:
                   1036:  *     COND_PARSE      if the condition was valid grammatically
1.153     rillig   1037:  *     COND_INVALID    if not a valid conditional.
1.10      christos 1038:  *
1.281     rillig   1039:  *     *out_value      is set to the boolean value of the condition
1.10      christos 1040:  */
1.142     rillig   1041: static CondEvalResult
1.260     rillig   1042: CondEvalExpression(const char *cond, bool *out_value, bool plain,
                   1043:                   bool (*evalBare)(size_t, const char *), bool negate,
1.277     rillig   1044:                   bool eprint, bool leftUnquotedOK)
1.10      christos 1045: {
1.215     rillig   1046:        CondParser par;
                   1047:        CondEvalResult rval;
1.10      christos 1048:
1.215     rillig   1049:        cpp_skip_hspace(&cond);
1.10      christos 1050:
1.243     rillig   1051:        par.plain = plain;
                   1052:        par.evalBare = evalBare;
                   1053:        par.negateEvalBare = negate;
1.277     rillig   1054:        par.leftUnquotedOK = leftUnquotedOK;
1.215     rillig   1055:        par.p = cond;
                   1056:        par.curr = TOK_NONE;
1.260     rillig   1057:        par.printedError = false;
1.10      christos 1058:
1.243     rillig   1059:        rval = CondParser_Eval(&par, out_value);
1.56      dsl      1060:
1.215     rillig   1061:        if (rval == COND_INVALID && eprint && !par.printedError)
                   1062:                Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
1.56      dsl      1063:
1.215     rillig   1064:        return rval;
1.56      dsl      1065: }
                   1066:
1.233     rillig   1067: /*
                   1068:  * Evaluate a condition in a :? modifier, such as
                   1069:  * ${"${VAR}" == value:?yes:no}.
                   1070:  */
1.142     rillig   1071: CondEvalResult
1.260     rillig   1072: Cond_EvalCondition(const char *cond, bool *out_value)
1.142     rillig   1073: {
1.260     rillig   1074:        return CondEvalExpression(cond, out_value, true,
1.277     rillig   1075:            FuncDefined, false, false, true);
1.142     rillig   1076: }
1.90      rillig   1077:
1.260     rillig   1078: static bool
1.224     rillig   1079: IsEndif(const char *p)
                   1080: {
                   1081:        return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' &&
                   1082:               p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]);
                   1083: }
                   1084:
1.260     rillig   1085: static bool
                   1086: DetermineKindOfConditional(const char **pp, bool *out_plain,
                   1087:                           bool (**out_evalBare)(size_t, const char *),
                   1088:                           bool *out_negate)
1.244     rillig   1089: {
                   1090:        const char *p = *pp;
                   1091:
                   1092:        p += 2;
1.260     rillig   1093:        *out_plain = false;
1.244     rillig   1094:        *out_evalBare = FuncDefined;
1.260     rillig   1095:        *out_negate = false;
1.244     rillig   1096:        if (*p == 'n') {
                   1097:                p++;
1.260     rillig   1098:                *out_negate = true;
1.244     rillig   1099:        }
                   1100:        if (is_token(p, "def", 3)) {            /* .ifdef and .ifndef */
                   1101:                p += 3;
                   1102:        } else if (is_token(p, "make", 4)) {    /* .ifmake and .ifnmake */
                   1103:                p += 4;
                   1104:                *out_evalBare = FuncMake;
                   1105:        } else if (is_token(p, "", 0) && !*out_negate) { /* plain .if */
1.260     rillig   1106:                *out_plain = true;
1.244     rillig   1107:        } else {
                   1108:                /*
                   1109:                 * TODO: Add error message about unknown directive,
                   1110:                 * since there is no other known directive that starts
                   1111:                 * with 'el' or 'if'.
                   1112:                 *
                   1113:                 * Example: .elifx 123
                   1114:                 */
1.260     rillig   1115:                return false;
1.244     rillig   1116:        }
                   1117:
                   1118:        *pp = p;
1.260     rillig   1119:        return true;
1.244     rillig   1120: }
                   1121:
1.233     rillig   1122: /*
                   1123:  * Evaluate the conditional directive in the line, which is one of:
1.207     rillig   1124:  *
1.206     rillig   1125:  *     .if <cond>
                   1126:  *     .ifmake <cond>
                   1127:  *     .ifnmake <cond>
                   1128:  *     .ifdef <cond>
                   1129:  *     .ifndef <cond>
                   1130:  *     .elif <cond>
                   1131:  *     .elifmake <cond>
                   1132:  *     .elifnmake <cond>
                   1133:  *     .elifdef <cond>
                   1134:  *     .elifndef <cond>
                   1135:  *     .else
                   1136:  *     .endif
1.207     rillig   1137:  *
                   1138:  * In these directives, <cond> consists of &&, ||, !, function(arg),
                   1139:  * comparisons, expressions, bare words, numbers and strings, and
                   1140:  * parenthetical groupings thereof.
1.1       cgd      1141:  *
1.108     rillig   1142:  * Results:
1.207     rillig   1143:  *     COND_PARSE      to continue parsing the lines that follow the
1.260     rillig   1144:  *                     conditional (when <cond> evaluates to true)
1.108     rillig   1145:  *     COND_SKIP       to skip the lines after the conditional
1.260     rillig   1146:  *                     (when <cond> evaluates to false, or when a previous
1.108     rillig   1147:  *                     branch has already been taken)
1.153     rillig   1148:  *     COND_INVALID    if the conditional was not valid, either because of
1.108     rillig   1149:  *                     a syntax error or because some variable was undefined
                   1150:  *                     or because the condition could not be evaluated
1.1       cgd      1151:  */
1.86      rillig   1152: CondEvalResult
1.222     rillig   1153: Cond_EvalLine(const char *line)
1.1       cgd      1154: {
1.215     rillig   1155:        typedef enum IfState {
                   1156:
1.260     rillig   1157:                /* None of the previous <cond> evaluated to true. */
1.215     rillig   1158:                IFS_INITIAL     = 0,
                   1159:
1.260     rillig   1160:                /* The previous <cond> evaluated to true.
1.215     rillig   1161:                 * The lines following this condition are interpreted. */
                   1162:                IFS_ACTIVE      = 1 << 0,
                   1163:
                   1164:                /* The previous directive was an '.else'. */
                   1165:                IFS_SEEN_ELSE   = 1 << 1,
                   1166:
1.260     rillig   1167:                /* One of the previous <cond> evaluated to true. */
1.215     rillig   1168:                IFS_WAS_ACTIVE  = 1 << 2
1.207     rillig   1169:
1.215     rillig   1170:        } IfState;
1.213     rillig   1171:
1.215     rillig   1172:        static enum IfState *cond_states = NULL;
                   1173:        static unsigned int cond_states_cap = 128;
                   1174:
1.260     rillig   1175:        bool plain;
                   1176:        bool (*evalBare)(size_t, const char *);
                   1177:        bool negate;
                   1178:        bool isElif;
                   1179:        bool value;
1.215     rillig   1180:        IfState state;
                   1181:        const char *p = line;
                   1182:
                   1183:        if (cond_states == NULL) {
                   1184:                cond_states = bmake_malloc(
                   1185:                    cond_states_cap * sizeof *cond_states);
                   1186:                cond_states[0] = IFS_ACTIVE;
                   1187:        }
                   1188:
                   1189:        p++;                    /* skip the leading '.' */
                   1190:        cpp_skip_hspace(&p);
                   1191:
1.224     rillig   1192:        if (IsEndif(p)) {       /* It is an '.endif'. */
1.225     rillig   1193:                if (p[5] != '\0') {
                   1194:                        Parse_Error(PARSE_FATAL,
1.282     rillig   1195:                            "The .endif directive does not take arguments");
1.225     rillig   1196:                }
1.224     rillig   1197:
                   1198:                if (cond_depth == cond_min_depth) {
                   1199:                        Parse_Error(PARSE_FATAL, "if-less endif");
                   1200:                        return COND_PARSE;
                   1201:                }
                   1202:
                   1203:                /* Return state for previous conditional */
                   1204:                cond_depth--;
                   1205:                return cond_states[cond_depth] & IFS_ACTIVE
                   1206:                    ? COND_PARSE : COND_SKIP;
                   1207:        }
                   1208:
1.215     rillig   1209:        /* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
                   1210:        if (p[0] == 'e') {
                   1211:                if (p[1] != 'l') {
1.224     rillig   1212:                        /*
                   1213:                         * Unknown directive.  It might still be a
1.281     rillig   1214:                         * transformation rule like '.err.txt',
1.224     rillig   1215:                         * therefore no error message here.
                   1216:                         */
                   1217:                        return COND_INVALID;
1.215     rillig   1218:                }
1.211     rillig   1219:
1.215     rillig   1220:                /* Quite likely this is 'else' or 'elif' */
                   1221:                p += 2;
1.216     rillig   1222:                if (is_token(p, "se", 2)) {     /* It is an 'else'. */
1.215     rillig   1223:
1.226     rillig   1224:                        if (p[2] != '\0')
1.215     rillig   1225:                                Parse_Error(PARSE_FATAL,
1.282     rillig   1226:                                    "The .else directive "
                   1227:                                    "does not take arguments");
1.215     rillig   1228:
                   1229:                        if (cond_depth == cond_min_depth) {
                   1230:                                Parse_Error(PARSE_FATAL, "if-less else");
                   1231:                                return COND_PARSE;
                   1232:                        }
                   1233:
                   1234:                        state = cond_states[cond_depth];
                   1235:                        if (state == IFS_INITIAL) {
                   1236:                                state = IFS_ACTIVE | IFS_SEEN_ELSE;
                   1237:                        } else {
                   1238:                                if (state & IFS_SEEN_ELSE)
                   1239:                                        Parse_Error(PARSE_WARNING,
                   1240:                                                    "extra else");
                   1241:                                state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
                   1242:                        }
                   1243:                        cond_states[cond_depth] = state;
1.211     rillig   1244:
1.215     rillig   1245:                        return state & IFS_ACTIVE ? COND_PARSE : COND_SKIP;
                   1246:                }
                   1247:                /* Assume for now it is an elif */
1.260     rillig   1248:                isElif = true;
1.215     rillig   1249:        } else
1.260     rillig   1250:                isElif = false;
1.211     rillig   1251:
1.215     rillig   1252:        if (p[0] != 'i' || p[1] != 'f') {
                   1253:                /*
                   1254:                 * Unknown directive.  It might still be a transformation rule
                   1255:                 * like '.elisp.scm', therefore no error message here.
                   1256:                 */
                   1257:                return COND_INVALID;    /* Not an ifxxx or elifxxx line */
1.36      dsl      1258:        }
                   1259:
1.244     rillig   1260:        if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
1.243     rillig   1261:                return COND_INVALID;
1.206     rillig   1262:
1.215     rillig   1263:        if (isElif) {
                   1264:                if (cond_depth == cond_min_depth) {
                   1265:                        Parse_Error(PARSE_FATAL, "if-less elif");
                   1266:                        return COND_PARSE;
                   1267:                }
                   1268:                state = cond_states[cond_depth];
                   1269:                if (state & IFS_SEEN_ELSE) {
                   1270:                        Parse_Error(PARSE_WARNING, "extra elif");
                   1271:                        cond_states[cond_depth] =
                   1272:                            IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
                   1273:                        return COND_SKIP;
                   1274:                }
                   1275:                if (state != IFS_INITIAL) {
                   1276:                        cond_states[cond_depth] = IFS_WAS_ACTIVE;
                   1277:                        return COND_SKIP;
                   1278:                }
                   1279:        } else {
                   1280:                /* Normal .if */
                   1281:                if (cond_depth + 1 >= cond_states_cap) {
                   1282:                        /*
                   1283:                         * This is rare, but not impossible.
                   1284:                         * In meta mode, dirdeps.mk (only runs at level 0)
                   1285:                         * can need more than the default.
                   1286:                         */
                   1287:                        cond_states_cap += 32;
                   1288:                        cond_states = bmake_realloc(cond_states,
                   1289:                                                    cond_states_cap *
                   1290:                                                    sizeof *cond_states);
                   1291:                }
                   1292:                state = cond_states[cond_depth];
                   1293:                cond_depth++;
                   1294:                if (!(state & IFS_ACTIVE)) {
                   1295:                        /*
                   1296:                         * If we aren't parsing the data,
                   1297:                         * treat as always false.
                   1298:                         */
                   1299:                        cond_states[cond_depth] = IFS_WAS_ACTIVE;
                   1300:                        return COND_SKIP;
                   1301:                }
                   1302:        }
                   1303:
                   1304:        /* And evaluate the conditional expression */
1.243     rillig   1305:        if (CondEvalExpression(p, &value, plain, evalBare, negate,
1.277     rillig   1306:            true, false) == COND_INVALID) {
1.215     rillig   1307:                /* Syntax error in conditional, error message already output. */
                   1308:                /* Skip everything to matching .endif */
                   1309:                /* XXX: An extra '.else' is not detected in this case. */
                   1310:                cond_states[cond_depth] = IFS_WAS_ACTIVE;
                   1311:                return COND_SKIP;
                   1312:        }
                   1313:
                   1314:        if (!value) {
                   1315:                cond_states[cond_depth] = IFS_INITIAL;
                   1316:                return COND_SKIP;
                   1317:        }
                   1318:        cond_states[cond_depth] = IFS_ACTIVE;
                   1319:        return COND_PARSE;
1.1       cgd      1320: }
1.10      christos 1321:
1.1       cgd      1322: void
1.37      dsl      1323: Cond_restore_depth(unsigned int saved_depth)
1.1       cgd      1324: {
1.215     rillig   1325:        unsigned int open_conds = cond_depth - cond_min_depth;
1.37      dsl      1326:
1.215     rillig   1327:        if (open_conds != 0 || saved_depth > cond_depth) {
                   1328:                Parse_Error(PARSE_FATAL, "%u open conditional%s",
                   1329:                            open_conds, open_conds == 1 ? "" : "s");
                   1330:                cond_depth = cond_min_depth;
                   1331:        }
1.37      dsl      1332:
1.215     rillig   1333:        cond_min_depth = saved_depth;
1.37      dsl      1334: }
                   1335:
                   1336: unsigned int
                   1337: Cond_save_depth(void)
                   1338: {
1.215     rillig   1339:        unsigned int depth = cond_min_depth;
1.37      dsl      1340:
1.215     rillig   1341:        cond_min_depth = cond_depth;
                   1342:        return depth;
1.1       cgd      1343: }

CVSweb <webmaster@jp.NetBSD.org>