[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.145

1.145   ! rillig      1: /*     $NetBSD: cond.c,v 1.144 2020/09/13 15:15:51 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.117     rillig     72: /* Handling of conditionals in a makefile.
1.1       cgd        73:  *
                     74:  * Interface:
1.142     rillig     75:  *     Cond_EvalLine   Evaluate the conditional in the passed line.
1.1       cgd        76:  *
1.142     rillig     77:  *     Cond_EvalCondition
1.117     rillig     78:  *                     Evaluate the conditional in the passed line, which
                     79:  *                     is either the argument of one of the .if directives
                     80:  *                     or the condition in a :?true:false variable modifier.
                     81:  *
                     82:  *     Cond_save_depth
                     83:  *     Cond_restore_depth
                     84:  *                     Save and restore the nesting of the conditions, at
                     85:  *                     the start and end of including another makefile, to
                     86:  *                     ensure that in each makefile the conditional
                     87:  *                     directives are well-balanced.
1.1       cgd        88:  */
                     89:
1.99      rillig     90: #include <errno.h>
                     91:
                     92: #include "make.h"
                     93: #include "dir.h"
1.1       cgd        94:
1.144     rillig     95: /*     "@(#)cond.c     8.2 (Berkeley) 1/2/94"  */
1.145   ! rillig     96: MAKE_RCSID("$NetBSD: cond.c,v 1.144 2020/09/13 15:15:51 rillig Exp $");
1.144     rillig     97:
1.1       cgd        98: /*
                     99:  * The parsing of conditional expressions is based on this grammar:
                    100:  *     E -> F || E
                    101:  *     E -> F
                    102:  *     F -> T && F
                    103:  *     F -> T
                    104:  *     T -> defined(variable)
                    105:  *     T -> make(target)
                    106:  *     T -> exists(file)
                    107:  *     T -> empty(varspec)
                    108:  *     T -> target(name)
1.12      christos  109:  *     T -> commands(name)
1.1       cgd       110:  *     T -> symbol
                    111:  *     T -> $(varspec) op value
                    112:  *     T -> $(varspec) == "string"
                    113:  *     T -> $(varspec) != "string"
1.23      sjg       114:  *     T -> "string"
1.1       cgd       115:  *     T -> ( E )
                    116:  *     T -> ! T
                    117:  *     op -> == | != | > | < | >= | <=
                    118:  *
1.96      rillig    119:  * 'symbol' is some other symbol to which the default function is applied.
1.1       cgd       120:  *
1.121     rillig    121:  * The tokens are scanned by CondToken, which returns:
                    122:  *     TOK_AND         for '&' or '&&'
                    123:  *     TOK_OR          for '|' or '||'
                    124:  *     TOK_NOT         for '!'
                    125:  *     TOK_LPAREN      for '('
                    126:  *     TOK_RPAREN      for ')'
                    127:  * Other terminal symbols are evaluated using either the default function or
                    128:  * the function given in the terminal, they return either TOK_TRUE or
                    129:  * TOK_FALSE.
1.1       cgd       130:  *
1.60      dsl       131:  * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons.
                    132:  *
1.125     rillig    133:  * All non-terminal functions (CondParser_Expr, CondParser_Factor and
                    134:  * CondParser_Term) return either TOK_FALSE, TOK_TRUE, or TOK_ERROR on error.
1.1       cgd       135:  */
                    136: typedef enum {
1.60      dsl       137:     TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT,
                    138:     TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
1.1       cgd       139: } Token;
                    140:
1.114     rillig    141: typedef struct {
                    142:     const struct If *if_info;  /* Info for current statement */
1.122     rillig    143:     const char *p;             /* The remaining condition to parse */
1.115     rillig    144:     Token curr;                        /* Single push-back token used in parsing */
1.121     rillig    145: } CondParser;
1.1       cgd       146:
1.125     rillig    147: static Token CondParser_Expr(CondParser *par, Boolean);
                    148: static CondEvalResult CondParser_Eval(CondParser *par, Boolean *value);
1.1       cgd       149:
1.93      rillig    150: static unsigned int cond_depth = 0;    /* current .if nesting level */
                    151: static unsigned int cond_min_depth = 0;        /* depth at makefile open */
1.1       cgd       152:
1.68      sjg       153: /*
                    154:  * Indicate when we should be strict about lhs of comparisons.
1.117     rillig    155:  * In strict mode, the lhs must be a variable expression or a string literal
                    156:  * in quotes. In non-strict mode it may also be an unquoted string literal.
                    157:  *
1.142     rillig    158:  * TRUE when CondEvalExpression is called from Cond_EvalLine (.if etc)
                    159:  * FALSE when CondEvalExpression is called from ApplyModifier_IfElse
1.78      rillig    160:  * since lhs is already expanded and we cannot tell if
1.68      sjg       161:  * it was a variable reference or not.
                    162:  */
                    163: static Boolean lhsStrict;
                    164:
1.26      christos  165: static int
1.125     rillig    166: is_token(const char *str, const char *tok, size_t len)
1.26      christos  167: {
1.134     rillig    168:     return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]);
1.26      christos  169: }
                    170:
1.115     rillig    171: /* Push back the most recent token read. We only need one level of this. */
1.1       cgd       172: static void
1.121     rillig    173: CondParser_PushBack(CondParser *par, Token t)
1.1       cgd       174: {
1.121     rillig    175:     assert(par->curr == TOK_NONE);
1.115     rillig    176:     assert(t != TOK_NONE);
                    177:
1.121     rillig    178:     par->curr = t;
1.1       cgd       179: }
1.90      rillig    180:
1.116     rillig    181: static void
1.121     rillig    182: CondParser_SkipWhitespace(CondParser *par)
1.116     rillig    183: {
1.134     rillig    184:     while (ch_isspace(par->p[0]))
1.122     rillig    185:        par->p++;
1.116     rillig    186: }
                    187:
1.112     rillig    188: /* Parse the argument of a built-in function.
                    189:  *
                    190:  * Arguments:
1.127     rillig    191:  *     *linePtr initially points at the '(', upon successful return points
                    192:  *     right after the ')'.
1.112     rillig    193:  *
                    194:  *     *out_arg receives the argument as string.
                    195:  *
                    196:  *     func says whether the argument belongs to an actual function, or
                    197:  *     whether the parsed argument is passed to the default function.
                    198:  *
                    199:  * Return the length of the argument. */
1.1       cgd       200: static int
1.127     rillig    201: ParseFuncArg(const char **linePtr, Boolean doEval, const char *func,
                    202:             char **out_arg) {
1.93      rillig    203:     const char *cp;
                    204:     Buffer buf;
                    205:     int paren_depth;
                    206:     char ch;
                    207:     size_t argLen;
1.1       cgd       208:
                    209:     cp = *linePtr;
1.56      dsl       210:     if (func != NULL)
1.87      rillig    211:        /* Skip opening '(' - verified by caller */
1.56      dsl       212:        cp++;
1.1       cgd       213:
                    214:     if (*cp == '\0') {
                    215:        /*
                    216:         * No arguments whatsoever. Because 'make' and 'defined' aren't really
                    217:         * "reserved words", we don't print a message. I think this is better
                    218:         * than hitting the user with a warning message every time s/he uses
                    219:         * the word 'make' or 'defined' at the beginning of a symbol...
                    220:         */
1.112     rillig    221:        *out_arg = NULL;
1.77      rillig    222:        return 0;
1.1       cgd       223:     }
                    224:
                    225:     while (*cp == ' ' || *cp == '\t') {
                    226:        cp++;
                    227:     }
                    228:
                    229:     /*
                    230:      * Create a buffer for the argument and start it out at 16 characters
                    231:      * long. Why 16? Why not?
                    232:      */
1.92      rillig    233:     Buf_Init(&buf, 16);
1.7       christos  234:
1.56      dsl       235:     paren_depth = 0;
                    236:     for (;;) {
                    237:        ch = *cp;
                    238:        if (ch == 0 || ch == ' ' || ch == '\t')
                    239:            break;
                    240:        if ((ch == '&' || ch == '|') && paren_depth == 0)
                    241:            break;
1.1       cgd       242:        if (*cp == '$') {
                    243:            /*
                    244:             * Parse the variable spec and install it as part of the argument
                    245:             * if it's valid. We tell Var_Parse to complain on an undefined
1.117     rillig    246:             * variable, so we don't need to do it. Nor do we return an error,
1.1       cgd       247:             * though perhaps we should...
                    248:             */
1.93      rillig    249:            void *freeIt;
1.81      rillig    250:            VarEvalFlags eflags = VARE_UNDEFERR | (doEval ? VARE_WANTRES : 0);
1.145   ! rillig    251:            const char *cp2;
        !           252:            (void)Var_Parse(&cp, VAR_CMD, eflags, &cp2, &freeIt);
        !           253:            /* TODO: handle errors */
1.87      rillig    254:            Buf_AddStr(&buf, cp2);
1.73      christos  255:            free(freeIt);
1.56      dsl       256:            continue;
1.1       cgd       257:        }
1.56      dsl       258:        if (ch == '(')
                    259:            paren_depth++;
1.93      rillig    260:        else if (ch == ')' && --paren_depth < 0)
                    261:            break;
1.56      dsl       262:        Buf_AddByte(&buf, *cp);
                    263:        cp++;
1.1       cgd       264:     }
                    265:
1.112     rillig    266:     *out_arg = Buf_GetAll(&buf, &argLen);
1.50      dsl       267:     Buf_Destroy(&buf, FALSE);
1.1       cgd       268:
                    269:     while (*cp == ' ' || *cp == '\t') {
                    270:        cp++;
                    271:     }
1.56      dsl       272:
                    273:     if (func != NULL && *cp++ != ')') {
1.25      christos  274:        Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
1.93      rillig    275:                    func);
1.142     rillig    276:        /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
1.77      rillig    277:        return 0;
1.50      dsl       278:     }
                    279:
1.1       cgd       280:     *linePtr = cp;
1.77      rillig    281:     return argLen;
1.1       cgd       282: }
1.90      rillig    283:
                    284: /* Test whether the given variable is defined. */
1.1       cgd       285: static Boolean
1.125     rillig    286: FuncDefined(int argLen MAKE_ATTR_UNUSED, const char *arg)
1.1       cgd       287: {
1.83      rillig    288:     char *freeIt;
                    289:     Boolean result = Var_Value(arg, VAR_CMD, &freeIt) != NULL;
1.84      rillig    290:     bmake_free(freeIt);
1.77      rillig    291:     return result;
1.1       cgd       292: }
1.90      rillig    293:
1.105     rillig    294: /* Wrapper around Str_Match, to be used by Lst_Find. */
1.104     rillig    295: static Boolean
1.90      rillig    296: CondFindStrMatch(const void *string, const void *pattern)
1.1       cgd       297: {
1.104     rillig    298:     return Str_Match(string, pattern);
1.1       cgd       299: }
1.90      rillig    300:
                    301: /* See if the given target is being made. */
1.1       cgd       302: static Boolean
1.125     rillig    303: FuncMake(int argLen MAKE_ATTR_UNUSED, const char *arg)
1.1       cgd       304: {
1.105     rillig    305:     return Lst_Find(create, CondFindStrMatch, arg) != NULL;
1.1       cgd       306: }
1.90      rillig    307:
                    308: /* See if the given file exists. */
1.1       cgd       309: static Boolean
1.125     rillig    310: FuncExists(int argLen MAKE_ATTR_UNUSED, const char *arg)
1.1       cgd       311: {
                    312:     Boolean result;
1.93      rillig    313:     char *path;
1.1       cgd       314:
                    315:     path = Dir_FindFile(arg, dirSearchPath);
1.62      sjg       316:     if (DEBUG(COND)) {
                    317:        fprintf(debug_file, "exists(%s) result is \"%s\"\n",
1.93      rillig    318:                arg, path ? path : "");
1.78      rillig    319:     }
1.29      christos  320:     if (path != NULL) {
1.1       cgd       321:        result = TRUE;
                    322:        free(path);
                    323:     } else {
                    324:        result = FALSE;
                    325:     }
1.77      rillig    326:     return result;
1.1       cgd       327: }
1.90      rillig    328:
                    329: /* See if the given node exists and is an actual target. */
1.1       cgd       330: static Boolean
1.125     rillig    331: FuncTarget(int argLen MAKE_ATTR_UNUSED, const char *arg)
1.1       cgd       332: {
1.93      rillig    333:     GNode *gn;
1.1       cgd       334:
                    335:     gn = Targ_FindNode(arg, TARG_NOCREATE);
1.77      rillig    336:     return gn != NULL && !OP_NOP(gn->type);
1.1       cgd       337: }
                    338:
1.90      rillig    339: /* See if the given node exists and is an actual target with commands
                    340:  * associated with it. */
1.12      christos  341: static Boolean
1.125     rillig    342: FuncCommands(int argLen MAKE_ATTR_UNUSED, const char *arg)
1.12      christos  343: {
1.93      rillig    344:     GNode *gn;
1.12      christos  345:
                    346:     gn = Targ_FindNode(arg, TARG_NOCREATE);
1.103     rillig    347:     return gn != NULL && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands);
1.12      christos  348: }
1.90      rillig    349:
1.1       cgd       350: /*-
1.90      rillig    351:  * Convert the given number into a double.
                    352:  * We try a base 10 or 16 integer conversion first, if that fails
                    353:  * then we try a floating point conversion instead.
1.1       cgd       354:  *
                    355:  * Results:
1.4       cgd       356:  *     Sets 'value' to double value of string.
1.90      rillig    357:  *     Returns TRUE if the conversion succeeded.
1.1       cgd       358:  */
1.46      dsl       359: static Boolean
1.125     rillig    360: TryParseNumber(const char *str, double *value)
1.1       cgd       361: {
1.46      dsl       362:     char *eptr, ech;
                    363:     unsigned long l_val;
                    364:     double d_val;
                    365:
1.71      sjg       366:     errno = 0;
1.70      sjg       367:     if (!*str) {
1.71      sjg       368:        *value = (double)0;
                    369:        return TRUE;
1.70      sjg       370:     }
1.46      dsl       371:     l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10);
                    372:     ech = *eptr;
1.139     rillig    373:     if (ech == '\0' && errno != ERANGE) {
1.46      dsl       374:        d_val = str[0] == '-' ? -(double)-l_val : (double)l_val;
1.19      sjg       375:     } else {
1.139     rillig    376:        if (ech != '\0' && ech != '.' && ech != 'e' && ech != 'E')
1.46      dsl       377:            return FALSE;
                    378:        d_val = strtod(str, &eptr);
                    379:        if (*eptr)
                    380:            return FALSE;
1.1       cgd       381:     }
1.46      dsl       382:
                    383:     *value = d_val;
                    384:     return TRUE;
1.1       cgd       385: }
1.46      dsl       386:
1.140     rillig    387: static Boolean
                    388: is_separator(char ch)
                    389: {
                    390:     return ch == '\0' || ch_isspace(ch) || strchr("!=><)", ch);
                    391: }
                    392:
1.1       cgd       393: /*-
1.117     rillig    394:  * Parse a string from a variable reference or an optionally quoted
1.124     rillig    395:  * string.  This is called for the lhs and rhs of string comparisons.
1.23      sjg       396:  *
                    397:  * Results:
1.90      rillig    398:  *     Returns the string, absent any quotes, or NULL on error.
                    399:  *     Sets quoted if the string was quoted.
                    400:  *     Sets freeIt if needed.
1.23      sjg       401:  */
1.117     rillig    402: /* coverity:[+alloc : arg-*3] */
1.82      rillig    403: static const char *
1.124     rillig    404: CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
                    405:                  Boolean *quoted, void **freeIt)
1.23      sjg       406: {
                    407:     Buffer buf;
1.82      rillig    408:     const char *str;
1.140     rillig    409:     Boolean atStart;
                    410:     const char *nested_p;
1.94      rillig    411:     Boolean qt;
1.91      rillig    412:     const char *start;
1.98      rillig    413:     VarEvalFlags eflags;
1.23      sjg       414:
1.92      rillig    415:     Buf_Init(&buf, 0);
1.23      sjg       416:     str = NULL;
1.30      christos  417:     *freeIt = NULL;
1.123     rillig    418:     *quoted = qt = par->p[0] == '"' ? 1 : 0;
1.23      sjg       419:     if (qt)
1.122     rillig    420:        par->p++;
1.138     rillig    421:     start = par->p;
                    422:     while (par->p[0] && str == NULL) {
1.123     rillig    423:        switch (par->p[0]) {
1.23      sjg       424:        case '\\':
1.122     rillig    425:            par->p++;
                    426:            if (par->p[0] != '\0') {
1.123     rillig    427:                Buf_AddByte(&buf, par->p[0]);
1.122     rillig    428:                par->p++;
1.23      sjg       429:            }
1.119     rillig    430:            continue;
1.23      sjg       431:        case '"':
                    432:            if (qt) {
1.122     rillig    433:                par->p++;       /* we don't want the quotes */
1.23      sjg       434:                goto got_str;
1.120     rillig    435:            }
1.123     rillig    436:            Buf_AddByte(&buf, par->p[0]); /* likely? */
1.122     rillig    437:            par->p++;
1.119     rillig    438:            continue;
1.23      sjg       439:        case ')':
                    440:        case '!':
                    441:        case '=':
                    442:        case '>':
                    443:        case '<':
                    444:        case ' ':
                    445:        case '\t':
                    446:            if (!qt)
                    447:                goto got_str;
1.123     rillig    448:            Buf_AddByte(&buf, par->p[0]);
1.122     rillig    449:            par->p++;
1.119     rillig    450:            continue;
1.23      sjg       451:        case '$':
1.140     rillig    452:            /* if we are in quotes, an undefined variable is ok */
1.98      rillig    453:            eflags = ((!qt && doEval) ? VARE_UNDEFERR : 0) |
                    454:                     (doEval ? VARE_WANTRES : 0);
1.140     rillig    455:            nested_p = par->p;
                    456:            atStart = nested_p == start;
1.145   ! rillig    457:            (void)Var_Parse(&nested_p, VAR_CMD, eflags, &str, freeIt);
        !           458:            /* TODO: handle errors */
1.23      sjg       459:            if (str == var_Error) {
1.30      christos  460:                if (*freeIt) {
                    461:                    free(*freeIt);
                    462:                    *freeIt = NULL;
                    463:                }
1.23      sjg       464:                /*
                    465:                 * Even if !doEval, we still report syntax errors, which
                    466:                 * is what getting var_Error back with !doEval means.
                    467:                 */
                    468:                str = NULL;
                    469:                goto cleanup;
                    470:            }
1.140     rillig    471:            par->p = nested_p;
                    472:
1.23      sjg       473:            /*
1.140     rillig    474:             * If the '$' started the string literal (which means no quotes),
                    475:             * and the variable expression is followed by a space, looks like
                    476:             * a comparison operator or is the end of the expression, we are
                    477:             * done.
1.23      sjg       478:             */
1.140     rillig    479:            if (atStart && is_separator(par->p[0]))
1.23      sjg       480:                goto cleanup;
1.113     rillig    481:
                    482:            Buf_AddStr(&buf, str);
1.30      christos  483:            if (*freeIt) {
                    484:                free(*freeIt);
                    485:                *freeIt = NULL;
                    486:            }
1.93      rillig    487:            str = NULL;         /* not finished yet */
1.119     rillig    488:            continue;
1.23      sjg       489:        default:
1.134     rillig    490:            if (strictLHS && !qt && *start != '$' && !ch_isdigit(*start)) {
1.68      sjg       491:                /* lhs must be quoted, a variable reference or number */
                    492:                if (*freeIt) {
                    493:                    free(*freeIt);
                    494:                    *freeIt = NULL;
                    495:                }
                    496:                str = NULL;
                    497:                goto cleanup;
                    498:            }
1.123     rillig    499:            Buf_AddByte(&buf, par->p[0]);
1.122     rillig    500:            par->p++;
1.119     rillig    501:            continue;
1.23      sjg       502:        }
                    503:     }
1.93      rillig    504: got_str:
1.92      rillig    505:     *freeIt = Buf_GetAll(&buf, NULL);
1.82      rillig    506:     str = *freeIt;
1.93      rillig    507: cleanup:
1.50      dsl       508:     Buf_Destroy(&buf, FALSE);
1.23      sjg       509:     return str;
                    510: }
1.89      rillig    511:
1.117     rillig    512: /* The different forms of .if directives. */
1.89      rillig    513: static const struct If {
1.93      rillig    514:     const char *form;          /* Form of if */
1.97      rillig    515:     size_t formlen;            /* Length of form */
1.93      rillig    516:     Boolean doNot;             /* TRUE if default function should be negated */
                    517:     Boolean (*defProc)(int, const char *); /* Default function to apply */
1.89      rillig    518: } ifs[] = {
1.125     rillig    519:     { "def",   3, FALSE, FuncDefined },
                    520:     { "ndef",  4, TRUE,  FuncDefined },
                    521:     { "make",  4, FALSE, FuncMake },
                    522:     { "nmake", 5, TRUE,  FuncMake },
                    523:     { "",      0, FALSE, FuncDefined },
1.93      rillig    524:     { NULL,    0, FALSE, NULL }
1.89      rillig    525: };
                    526:
1.131     rillig    527: /* Evaluate a "comparison without operator", such as in ".if ${VAR}" or
                    528:  * ".if 0". */
                    529: static Token
                    530: EvalNotEmpty(CondParser *par, const char *lhs, Boolean lhsQuoted)
                    531: {
                    532:     double left;
                    533:
                    534:     /* For .ifxxx "..." check for non-empty string. */
                    535:     if (lhsQuoted)
                    536:        return lhs[0] != '\0';
                    537:
                    538:     /* For .ifxxx <number> compare against zero */
                    539:     if (TryParseNumber(lhs, &left))
                    540:        return left != 0.0;
                    541:
                    542:     /* For .if ${...} check for non-empty string (defProc is ifdef). */
                    543:     if (par->if_info->form[0] == '\0')
                    544:        return lhs[0] != 0;
                    545:
                    546:     /* Otherwise action default test ... */
                    547:     return par->if_info->defProc(strlen(lhs), lhs) != par->if_info->doNot;
                    548: }
                    549:
1.133     rillig    550: /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
1.130     rillig    551: static Token
1.133     rillig    552: EvalCompareNum(double lhs, const char *op, double rhs)
1.130     rillig    553: {
1.133     rillig    554:     if (DEBUG(COND))
1.135     rillig    555:        fprintf(debug_file, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, op);
1.130     rillig    556:
                    557:     switch (op[0]) {
                    558:     case '!':
                    559:        if (op[1] != '=') {
1.132     rillig    560:            Parse_Error(PARSE_WARNING, "Unknown operator");
1.142     rillig    561:            /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
1.130     rillig    562:            return TOK_ERROR;
                    563:        }
1.133     rillig    564:        return lhs != rhs;
1.130     rillig    565:     case '=':
                    566:        if (op[1] != '=') {
1.132     rillig    567:            Parse_Error(PARSE_WARNING, "Unknown operator");
1.142     rillig    568:            /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
1.130     rillig    569:            return TOK_ERROR;
                    570:        }
1.133     rillig    571:        return lhs == rhs;
1.130     rillig    572:     case '<':
1.133     rillig    573:        return op[1] == '=' ? lhs <= rhs : lhs < rhs;
1.130     rillig    574:     case '>':
1.133     rillig    575:        return op[1] == '=' ? lhs >= rhs : lhs > rhs;
1.130     rillig    576:     }
                    577:     return TOK_ERROR;
                    578: }
                    579:
1.133     rillig    580: static Token
                    581: EvalCompareStr(const char *lhs, const char *op, const char *rhs)
                    582: {
1.135     rillig    583:     if (!((op[0] == '!' || op[0] == '=') && op[1] == '=')) {
1.133     rillig    584:        Parse_Error(PARSE_WARNING,
1.135     rillig    585:                    "String comparison operator must be either == or !=");
1.142     rillig    586:        /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
1.133     rillig    587:        return TOK_ERROR;
                    588:     }
                    589:
                    590:     if (DEBUG(COND)) {
                    591:        fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
                    592:                lhs, rhs, op);
                    593:     }
                    594:     return (*op == '=') == (strcmp(lhs, rhs) == 0);
                    595: }
                    596:
                    597: /* Evaluate a comparison, such as "${VAR} == 12345". */
                    598: static Token
                    599: EvalCompare(const char *lhs, Boolean lhsQuoted, const char *op,
                    600:            const char *rhs, Boolean rhsQuoted)
                    601: {
                    602:     double left, right;
                    603:
                    604:     if (!rhsQuoted && !lhsQuoted)
                    605:        if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
                    606:            return EvalCompareNum(left, op, right);
                    607:
                    608:     return EvalCompareStr(lhs, op, rhs);
                    609: }
                    610:
1.129     rillig    611: /* Parse a comparison condition such as:
                    612:  *
                    613:  *     0
                    614:  *     ${VAR:Mpattern}
                    615:  *     ${VAR} == value
                    616:  *     ${VAR:U0} < 12345
                    617:  */
1.1       cgd       618: static Token
1.125     rillig    619: CondParser_Comparison(CondParser *par, Boolean doEval)
1.44      dsl       620: {
1.132     rillig    621:     Token t = TOK_ERROR;
                    622:     const char *lhs, *op, *rhs;
                    623:     void *lhsFree, *rhsFree;
                    624:     Boolean lhsQuoted, rhsQuoted;
1.44      dsl       625:
                    626:     rhs = NULL;
1.95      rillig    627:     lhsFree = rhsFree = NULL;
1.44      dsl       628:     lhsQuoted = rhsQuoted = FALSE;
1.78      rillig    629:
1.44      dsl       630:     /*
                    631:      * Parse the variable spec and skip over it, saving its
                    632:      * value in lhs.
                    633:      */
1.124     rillig    634:     lhs = CondParser_String(par, doEval, lhsStrict, &lhsQuoted, &lhsFree);
1.58      dsl       635:     if (!lhs)
                    636:        goto done;
                    637:
1.121     rillig    638:     CondParser_SkipWhitespace(par);
1.44      dsl       639:
                    640:     /*
                    641:      * Make sure the operator is a valid one. If it isn't a
                    642:      * known relational operator, pretend we got a
                    643:      * != 0 comparison.
                    644:      */
1.122     rillig    645:     op = par->p;
1.123     rillig    646:     switch (par->p[0]) {
1.93      rillig    647:     case '!':
                    648:     case '=':
                    649:     case '<':
                    650:     case '>':
1.122     rillig    651:        if (par->p[1] == '=') {
                    652:            par->p += 2;
1.93      rillig    653:        } else {
1.122     rillig    654:            par->p += 1;
1.93      rillig    655:        }
                    656:        break;
                    657:     default:
1.137     rillig    658:        t = doEval ? EvalNotEmpty(par, lhs, lhsQuoted) : TOK_FALSE;
1.93      rillig    659:        goto done;
1.58      dsl       660:     }
1.44      dsl       661:
1.121     rillig    662:     CondParser_SkipWhitespace(par);
1.58      dsl       663:
1.123     rillig    664:     if (par->p[0] == '\0') {
1.132     rillig    665:        Parse_Error(PARSE_WARNING, "Missing right-hand-side of operator");
1.142     rillig    666:        /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
1.58      dsl       667:        goto done;
1.44      dsl       668:     }
1.58      dsl       669:
1.124     rillig    670:     rhs = CondParser_String(par, doEval, FALSE, &rhsQuoted, &rhsFree);
1.132     rillig    671:     if (rhs == NULL)
1.58      dsl       672:        goto done;
                    673:
1.79      sjg       674:     if (!doEval) {
                    675:        t = TOK_FALSE;
                    676:        goto done;
                    677:     }
                    678:
1.133     rillig    679:     t = EvalCompare(lhs, lhsQuoted, op, rhs, rhsQuoted);
1.58      dsl       680:
                    681: done:
1.73      christos  682:     free(lhsFree);
                    683:     free(rhsFree);
1.44      dsl       684:     return t;
                    685: }
                    686:
1.47      dsl       687: static int
1.127     rillig    688: ParseEmptyArg(const char **linePtr, Boolean doEval,
                    689:              const char *func MAKE_ATTR_UNUSED, char **argPtr)
1.47      dsl       690: {
1.106     rillig    691:     void *val_freeIt;
1.82      rillig    692:     const char *val;
1.111     rillig    693:     int magic_res;
1.47      dsl       694:
                    695:     /* We do all the work here and return the result as the length */
                    696:     *argPtr = NULL;
                    697:
1.111     rillig    698:     (*linePtr)--;              /* Make (*linePtr)[1] point to the '('. */
1.145   ! rillig    699:     (void)Var_Parse(linePtr, VAR_CMD, doEval ? VARE_WANTRES : 0,
        !           700:                    &val, &val_freeIt);
        !           701:     /* TODO: handle errors */
1.111     rillig    702:     /* If successful, *linePtr points beyond the closing ')' now. */
1.47      dsl       703:
                    704:     if (val == var_Error) {
1.106     rillig    705:        free(val_freeIt);
1.47      dsl       706:        return -1;
                    707:     }
                    708:
                    709:     /* A variable is empty when it just contains spaces... 4/15/92, christos */
1.134     rillig    710:     while (ch_isspace(val[0]))
1.47      dsl       711:        val++;
                    712:
                    713:     /*
                    714:      * For consistency with the other functions we can't generate the
                    715:      * true/false here.
                    716:      */
1.111     rillig    717:     magic_res = *val != '\0' ? 2 : 1;
1.106     rillig    718:     free(val_freeIt);
1.111     rillig    719:     return magic_res;
1.47      dsl       720: }
                    721:
                    722: static Boolean
1.125     rillig    723: FuncEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED)
1.47      dsl       724: {
1.112     rillig    725:     /* Magic values ahead, see ParseEmptyArg. */
1.47      dsl       726:     return arglen == 1;
                    727: }
                    728:
1.44      dsl       729: static Token
1.125     rillig    730: CondParser_Func(CondParser *par, Boolean doEval)
1.44      dsl       731: {
1.47      dsl       732:     static const struct fn_def {
1.93      rillig    733:        const char *fn_name;
1.97      rillig    734:        size_t fn_name_len;
1.127     rillig    735:        int (*fn_parse)(const char **, Boolean, const char *, char **);
                    736:        Boolean (*fn_eval)(int, const char *);
1.47      dsl       737:     } fn_defs[] = {
1.125     rillig    738:        { "defined",  7, ParseFuncArg,  FuncDefined },
                    739:        { "make",     4, ParseFuncArg,  FuncMake },
                    740:        { "exists",   6, ParseFuncArg,  FuncExists },
                    741:        { "empty",    5, ParseEmptyArg, FuncEmpty },
                    742:        { "target",   6, ParseFuncArg,  FuncTarget },
                    743:        { "commands", 8, ParseFuncArg,  FuncCommands },
1.93      rillig    744:        { NULL,       0, NULL, NULL },
1.47      dsl       745:     };
                    746:     const struct fn_def *fn_def;
1.93      rillig    747:     Token t;
                    748:     char *arg = NULL;
                    749:     int arglen;
1.122     rillig    750:     const char *cp = par->p;
1.91      rillig    751:     const char *cp1;
1.44      dsl       752:
1.47      dsl       753:     for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) {
1.125     rillig    754:        if (!is_token(cp, fn_def->fn_name, fn_def->fn_name_len))
1.47      dsl       755:            continue;
                    756:        cp += fn_def->fn_name_len;
                    757:        /* There can only be whitespace before the '(' */
1.134     rillig    758:        while (ch_isspace(*cp))
1.47      dsl       759:            cp++;
                    760:        if (*cp != '(')
                    761:            break;
1.44      dsl       762:
1.127     rillig    763:        arglen = fn_def->fn_parse(&cp, doEval, fn_def->fn_name, &arg);
1.47      dsl       764:        if (arglen <= 0) {
1.122     rillig    765:            par->p = cp;
1.59      dsl       766:            return arglen < 0 ? TOK_ERROR : TOK_FALSE;
1.44      dsl       767:        }
1.47      dsl       768:        /* Evaluate the argument using the required function. */
1.127     rillig    769:        t = !doEval || fn_def->fn_eval(arglen, arg);
1.73      christos  770:        free(arg);
1.122     rillig    771:        par->p = cp;
1.44      dsl       772:        return t;
                    773:     }
                    774:
1.47      dsl       775:     /* Push anything numeric through the compare expression */
1.122     rillig    776:     cp = par->p;
1.134     rillig    777:     if (ch_isdigit(cp[0]) || strchr("+-", cp[0]))
1.125     rillig    778:        return CondParser_Comparison(par, doEval);
1.47      dsl       779:
1.44      dsl       780:     /*
1.48      dsl       781:      * Most likely we have a naked token to apply the default function to.
                    782:      * However ".if a == b" gets here when the "a" is unquoted and doesn't
1.58      dsl       783:      * start with a '$'. This surprises people.
1.48      dsl       784:      * If what follows the function argument is a '=' or '!' then the syntax
                    785:      * would be invalid if we did "defined(a)" - so instead treat as an
                    786:      * expression.
                    787:      */
1.127     rillig    788:     arglen = ParseFuncArg(&cp, doEval, NULL, &arg);
1.134     rillig    789:     for (cp1 = cp; ch_isspace(*cp1); cp1++)
1.48      dsl       790:        continue;
                    791:     if (*cp1 == '=' || *cp1 == '!')
1.125     rillig    792:        return CondParser_Comparison(par, doEval);
1.122     rillig    793:     par->p = cp;
1.48      dsl       794:
                    795:     /*
1.58      dsl       796:      * Evaluate the argument using the default function.
1.117     rillig    797:      * This path always treats .if as .ifdef. To get here, the character
1.58      dsl       798:      * after .if must have been taken literally, so the argument cannot
                    799:      * be empty - even if it contained a variable expansion.
1.44      dsl       800:      */
1.121     rillig    801:     t = !doEval || par->if_info->defProc(arglen, arg) != par->if_info->doNot;
1.73      christos  802:     free(arg);
1.44      dsl       803:     return t;
                    804: }
                    805:
1.121     rillig    806: /* Return the next token or comparison result from the parser. */
1.44      dsl       807: static Token
1.125     rillig    808: CondParser_Token(CondParser *par, Boolean doEval)
1.1       cgd       809: {
1.47      dsl       810:     Token t;
                    811:
1.121     rillig    812:     t = par->curr;
1.59      dsl       813:     if (t != TOK_NONE) {
1.121     rillig    814:        par->curr = TOK_NONE;
1.47      dsl       815:        return t;
                    816:     }
                    817:
1.122     rillig    818:     while (par->p[0] == ' ' || par->p[0] == '\t') {
                    819:        par->p++;
1.47      dsl       820:     }
                    821:
1.122     rillig    822:     switch (par->p[0]) {
1.47      dsl       823:
                    824:     case '(':
1.122     rillig    825:        par->p++;
1.59      dsl       826:        return TOK_LPAREN;
1.47      dsl       827:
                    828:     case ')':
1.122     rillig    829:        par->p++;
1.59      dsl       830:        return TOK_RPAREN;
1.47      dsl       831:
                    832:     case '|':
1.122     rillig    833:        par->p++;
                    834:        if (par->p[0] == '|') {
                    835:            par->p++;
1.47      dsl       836:        }
1.59      dsl       837:        return TOK_OR;
1.1       cgd       838:
1.47      dsl       839:     case '&':
1.122     rillig    840:        par->p++;
                    841:        if (par->p[0] == '&') {
                    842:            par->p++;
1.1       cgd       843:        }
1.59      dsl       844:        return TOK_AND;
1.47      dsl       845:
                    846:     case '!':
1.122     rillig    847:        par->p++;
1.59      dsl       848:        return TOK_NOT;
1.4       cgd       849:
1.47      dsl       850:     case '#':
                    851:     case '\n':
                    852:     case '\0':
1.59      dsl       853:        return TOK_EOF;
1.47      dsl       854:
                    855:     case '"':
                    856:     case '$':
1.125     rillig    857:        return CondParser_Comparison(par, doEval);
1.1       cgd       858:
1.47      dsl       859:     default:
1.125     rillig    860:        return CondParser_Func(par, doEval);
1.1       cgd       861:     }
                    862: }
1.47      dsl       863:
1.117     rillig    864: /* Parse a single term in the expression. This consists of a terminal symbol
                    865:  * or TOK_NOT and a term (not including the binary operators):
                    866:  *
                    867:  *     T -> defined(variable) | make(target) | exists(file) | symbol
                    868:  *     T -> ! T | ( E )
1.1       cgd       869:  *
                    870:  * Results:
1.59      dsl       871:  *     TOK_TRUE, TOK_FALSE or TOK_ERROR.
1.1       cgd       872:  */
                    873: static Token
1.125     rillig    874: CondParser_Term(CondParser *par, Boolean doEval)
1.1       cgd       875: {
1.93      rillig    876:     Token t;
1.1       cgd       877:
1.125     rillig    878:     t = CondParser_Token(par, doEval);
1.1       cgd       879:
1.59      dsl       880:     if (t == TOK_EOF) {
1.1       cgd       881:        /*
                    882:         * If we reached the end of the expression, the expression
                    883:         * is malformed...
                    884:         */
1.59      dsl       885:        t = TOK_ERROR;
                    886:     } else if (t == TOK_LPAREN) {
1.1       cgd       887:        /*
                    888:         * T -> ( E )
                    889:         */
1.125     rillig    890:        t = CondParser_Expr(par, doEval);
1.59      dsl       891:        if (t != TOK_ERROR) {
1.125     rillig    892:            if (CondParser_Token(par, doEval) != TOK_RPAREN) {
1.59      dsl       893:                t = TOK_ERROR;
1.1       cgd       894:            }
                    895:        }
1.59      dsl       896:     } else if (t == TOK_NOT) {
1.125     rillig    897:        t = CondParser_Term(par, doEval);
1.59      dsl       898:        if (t == TOK_TRUE) {
                    899:            t = TOK_FALSE;
                    900:        } else if (t == TOK_FALSE) {
                    901:            t = TOK_TRUE;
1.1       cgd       902:        }
                    903:     }
1.77      rillig    904:     return t;
1.1       cgd       905: }
1.90      rillig    906:
1.117     rillig    907: /* Parse a conjunctive factor (nice name, wot?)
                    908:  *
                    909:  *     F -> T && F | T
1.1       cgd       910:  *
                    911:  * Results:
1.59      dsl       912:  *     TOK_TRUE, TOK_FALSE or TOK_ERROR
1.1       cgd       913:  */
                    914: static Token
1.125     rillig    915: CondParser_Factor(CondParser *par, Boolean doEval)
1.1       cgd       916: {
1.93      rillig    917:     Token l, o;
1.1       cgd       918:
1.125     rillig    919:     l = CondParser_Term(par, doEval);
1.59      dsl       920:     if (l != TOK_ERROR) {
1.125     rillig    921:        o = CondParser_Token(par, doEval);
1.1       cgd       922:
1.59      dsl       923:        if (o == TOK_AND) {
1.1       cgd       924:            /*
                    925:             * F -> T && F
                    926:             *
1.117     rillig    927:             * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we
                    928:             * have to parse the r.h.s. anyway (to throw it away).
                    929:             * If T is TOK_TRUE, the result is the r.h.s., be it a TOK_ERROR
                    930:             * or not.
1.1       cgd       931:             */
1.59      dsl       932:            if (l == TOK_TRUE) {
1.125     rillig    933:                l = CondParser_Factor(par, doEval);
1.1       cgd       934:            } else {
1.125     rillig    935:                (void)CondParser_Factor(par, FALSE);
1.1       cgd       936:            }
                    937:        } else {
                    938:            /*
                    939:             * F -> T
                    940:             */
1.121     rillig    941:            CondParser_PushBack(par, o);
1.1       cgd       942:        }
                    943:     }
1.77      rillig    944:     return l;
1.1       cgd       945: }
1.90      rillig    946:
1.117     rillig    947: /* Main expression production.
                    948:  *
                    949:  *     E -> F || E | F
1.1       cgd       950:  *
                    951:  * Results:
1.59      dsl       952:  *     TOK_TRUE, TOK_FALSE or TOK_ERROR.
1.1       cgd       953:  */
                    954: static Token
1.125     rillig    955: CondParser_Expr(CondParser *par, Boolean doEval)
1.1       cgd       956: {
1.93      rillig    957:     Token l, o;
1.1       cgd       958:
1.125     rillig    959:     l = CondParser_Factor(par, doEval);
1.59      dsl       960:     if (l != TOK_ERROR) {
1.125     rillig    961:        o = CondParser_Token(par, doEval);
1.1       cgd       962:
1.59      dsl       963:        if (o == TOK_OR) {
1.1       cgd       964:            /*
                    965:             * E -> F || E
                    966:             *
                    967:             * A similar thing occurs for ||, except that here we make sure
1.59      dsl       968:             * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s.
                    969:             * Once again, if l is TOK_FALSE, the result is the r.h.s. and once
                    970:             * again if l is TOK_TRUE, we parse the r.h.s. to throw it away.
1.1       cgd       971:             */
1.59      dsl       972:            if (l == TOK_FALSE) {
1.125     rillig    973:                l = CondParser_Expr(par, doEval);
1.1       cgd       974:            } else {
1.125     rillig    975:                (void)CondParser_Expr(par, FALSE);
1.1       cgd       976:            }
                    977:        } else {
                    978:            /*
                    979:             * E -> F
                    980:             */
1.121     rillig    981:            CondParser_PushBack(par, o);
1.1       cgd       982:        }
                    983:     }
1.77      rillig    984:     return l;
1.1       cgd       985: }
1.10      christos  986:
1.89      rillig    987: static CondEvalResult
1.125     rillig    988: CondParser_Eval(CondParser *par, Boolean *value)
1.89      rillig    989: {
1.128     rillig    990:     Token res;
                    991:
                    992:     if (DEBUG(COND))
                    993:        fprintf(debug_file, "CondParser_Eval: %s\n", par->p);
                    994:
                    995:     res = CondParser_Expr(par, TRUE);
1.126     rillig    996:     if (res != TOK_FALSE && res != TOK_TRUE)
1.137     rillig    997:        return COND_INVALID;
1.89      rillig    998:
1.126     rillig    999:     if (CondParser_Token(par, TRUE /* XXX: Why TRUE? */) != TOK_EOF)
1.137     rillig   1000:        return COND_INVALID;
1.89      rillig   1001:
1.126     rillig   1002:     *value = res == TOK_TRUE;
                   1003:     return COND_PARSE;
1.89      rillig   1004: }
                   1005:
1.128     rillig   1006: /* Evaluate the condition, including any side effects from the variable
                   1007:  * expressions in the condition. The condition consists of &&, ||, !,
                   1008:  * function(arg), comparisons and parenthetical groupings thereof.
1.10      christos 1009:  *
                   1010:  * Results:
                   1011:  *     COND_PARSE      if the condition was valid grammatically
                   1012:  *     COND_INVALID    if not a valid conditional.
                   1013:  *
                   1014:  *     (*value) is set to the boolean value of the condition
                   1015:  */
1.142     rillig   1016: static CondEvalResult
                   1017: CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
1.143     rillig   1018:                    Boolean eprint, Boolean strictLHS)
1.10      christos 1019: {
1.56      dsl      1020:     static const struct If *dflt_info;
1.121     rillig   1021:     CondParser par;
1.56      dsl      1022:     int rval;
1.10      christos 1023:
1.68      sjg      1024:     lhsStrict = strictLHS;
                   1025:
1.128     rillig   1026:     while (*cond == ' ' || *cond == '\t')
                   1027:        cond++;
1.10      christos 1028:
1.56      dsl      1029:     if (info == NULL && (info = dflt_info) == NULL) {
                   1030:        /* Scan for the entry for .if - it can't be first */
1.93      rillig   1031:        for (info = ifs;; info++)
1.57      enami    1032:            if (info->form[0] == 0)
1.56      dsl      1033:                break;
                   1034:        dflt_info = info;
                   1035:     }
1.75      riastrad 1036:     assert(info != NULL);
1.56      dsl      1037:
1.121     rillig   1038:     par.if_info = info;
1.128     rillig   1039:     par.p = cond;
1.121     rillig   1040:     par.curr = TOK_NONE;
1.10      christos 1041:
1.125     rillig   1042:     rval = CondParser_Eval(&par, value);
1.56      dsl      1043:
                   1044:     if (rval == COND_INVALID && eprint)
1.128     rillig   1045:        Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
1.56      dsl      1046:
                   1047:     return rval;
                   1048: }
                   1049:
1.142     rillig   1050: CondEvalResult
                   1051: Cond_EvalCondition(const char *cond, Boolean *out_value)
                   1052: {
1.143     rillig   1053:        return CondEvalExpression(NULL, cond, out_value, FALSE, FALSE);
1.142     rillig   1054: }
1.90      rillig   1055:
1.108     rillig   1056: /* Evaluate the conditional in the passed line. The line looks like this:
                   1057:  *     .<cond-type> <expr>
                   1058:  * In this line, <cond-type> is any of if, ifmake, ifnmake, ifdef, ifndef,
                   1059:  * elif, elifmake, elifnmake, elifdef, elifndef.
                   1060:  * In this line, <expr> consists of &&, ||, !, function(arg), comparisons
                   1061:  * and parenthetical groupings thereof.
1.1       cgd      1062:  *
1.36      dsl      1063:  * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order
1.86      rillig   1064:  * to detect spurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF),
1.36      dsl      1065:  * otherwise .else could be treated as '.elif 1'.
1.108     rillig   1066:  *
                   1067:  * Results:
                   1068:  *     COND_PARSE      to continue parsing the lines after the conditional
                   1069:  *                     (when .if or .else returns TRUE)
                   1070:  *     COND_SKIP       to skip the lines after the conditional
                   1071:  *                     (when .if or .elif returns FALSE, or when a previous
                   1072:  *                     branch has already been taken)
                   1073:  *     COND_INVALID    if the conditional was not valid, either because of
                   1074:  *                     a syntax error or because some variable was undefined
                   1075:  *                     or because the condition could not be evaluated
1.1       cgd      1076:  */
1.86      rillig   1077: CondEvalResult
1.142     rillig   1078: Cond_EvalLine(const char *line)
1.1       cgd      1079: {
1.93      rillig   1080:     enum { MAXIF = 128 };      /* maximum depth of .if'ing */
                   1081:     enum { MAXIF_BUMP = 32 };  /* how much to grow by */
1.36      dsl      1082:     enum if_states {
                   1083:        IF_ACTIVE,              /* .if or .elif part active */
                   1084:        ELSE_ACTIVE,            /* .else part active */
                   1085:        SEARCH_FOR_ELIF,        /* searching for .elif/else to execute */
1.93      rillig   1086:        SKIP_TO_ELSE,           /* has been true, but not seen '.else' */
1.36      dsl      1087:        SKIP_TO_ENDIF           /* nothing else to execute */
                   1088:     };
1.65      sjg      1089:     static enum if_states *cond_state = NULL;
1.66      pgoyette 1090:     static unsigned int max_if_depth = MAXIF;
1.36      dsl      1091:
                   1092:     const struct If *ifp;
1.93      rillig   1093:     Boolean isElif;
                   1094:     Boolean value;
                   1095:     enum if_states state;
1.1       cgd      1096:
1.65      sjg      1097:     if (!cond_state) {
                   1098:        cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state));
                   1099:        cond_state[0] = IF_ACTIVE;
                   1100:     }
1.36      dsl      1101:     /* skip leading character (the '.') and any whitespace */
                   1102:     for (line++; *line == ' ' || *line == '\t'; line++)
1.1       cgd      1103:        continue;
                   1104:
1.36      dsl      1105:     /* Find what type of if we're dealing with.  */
                   1106:     if (line[0] == 'e') {
                   1107:        if (line[1] != 'l') {
1.125     rillig   1108:            if (!is_token(line + 1, "ndif", 4))
1.36      dsl      1109:                return COND_INVALID;
                   1110:            /* End of conditional section */
1.37      dsl      1111:            if (cond_depth == cond_min_depth) {
1.136     rillig   1112:                Parse_Error(PARSE_FATAL, "if-less endif");
1.36      dsl      1113:                return COND_PARSE;
                   1114:            }
                   1115:            /* Return state for previous conditional */
                   1116:            cond_depth--;
1.93      rillig   1117:            return cond_state[cond_depth] <= ELSE_ACTIVE
                   1118:                   ? COND_PARSE : COND_SKIP;
1.36      dsl      1119:        }
                   1120:
                   1121:        /* Quite likely this is 'else' or 'elif' */
1.1       cgd      1122:        line += 2;
1.125     rillig   1123:        if (is_token(line, "se", 2)) {
1.36      dsl      1124:            /* It is else... */
1.37      dsl      1125:            if (cond_depth == cond_min_depth) {
1.136     rillig   1126:                Parse_Error(PARSE_FATAL, "if-less else");
1.43      dsl      1127:                return COND_PARSE;
1.36      dsl      1128:            }
                   1129:
                   1130:            state = cond_state[cond_depth];
                   1131:            switch (state) {
                   1132:            case SEARCH_FOR_ELIF:
                   1133:                state = ELSE_ACTIVE;
                   1134:                break;
                   1135:            case ELSE_ACTIVE:
                   1136:            case SKIP_TO_ENDIF:
                   1137:                Parse_Error(PARSE_WARNING, "extra else");
                   1138:                /* FALLTHROUGH */
                   1139:            default:
                   1140:            case IF_ACTIVE:
                   1141:            case SKIP_TO_ELSE:
                   1142:                state = SKIP_TO_ENDIF;
                   1143:                break;
1.1       cgd      1144:            }
1.36      dsl      1145:            cond_state[cond_depth] = state;
                   1146:            return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
1.1       cgd      1147:        }
1.36      dsl      1148:        /* Assume for now it is an elif */
                   1149:        isElif = TRUE;
                   1150:     } else
                   1151:        isElif = FALSE;
                   1152:
                   1153:     if (line[0] != 'i' || line[1] != 'f')
                   1154:        /* Not an ifxxx or elifxxx line */
                   1155:        return COND_INVALID;
1.7       christos 1156:
1.1       cgd      1157:     /*
                   1158:      * Figure out what sort of conditional it is -- what its default
                   1159:      * function is, etc. -- by looking in the table of valid "ifs"
                   1160:      */
1.36      dsl      1161:     line += 2;
1.93      rillig   1162:     for (ifp = ifs;; ifp++) {
1.36      dsl      1163:        if (ifp->form == NULL)
                   1164:            return COND_INVALID;
1.125     rillig   1165:        if (is_token(ifp->form, line, ifp->formlen)) {
1.36      dsl      1166:            line += ifp->formlen;
1.1       cgd      1167:            break;
                   1168:        }
                   1169:     }
                   1170:
1.36      dsl      1171:     /* Now we know what sort of 'if' it is... */
                   1172:
                   1173:     if (isElif) {
1.37      dsl      1174:        if (cond_depth == cond_min_depth) {
1.136     rillig   1175:            Parse_Error(PARSE_FATAL, "if-less elif");
1.43      dsl      1176:            return COND_PARSE;
1.36      dsl      1177:        }
1.43      dsl      1178:        state = cond_state[cond_depth];
                   1179:        if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) {
1.36      dsl      1180:            Parse_Error(PARSE_WARNING, "extra elif");
1.43      dsl      1181:            cond_state[cond_depth] = SKIP_TO_ENDIF;
                   1182:            return COND_SKIP;
                   1183:        }
1.36      dsl      1184:        if (state != SEARCH_FOR_ELIF) {
                   1185:            /* Either just finished the 'true' block, or already SKIP_TO_ELSE */
                   1186:            cond_state[cond_depth] = SKIP_TO_ELSE;
                   1187:            return COND_SKIP;
1.1       cgd      1188:        }
                   1189:     } else {
1.43      dsl      1190:        /* Normal .if */
1.67      christos 1191:        if (cond_depth + 1 >= max_if_depth) {
1.65      sjg      1192:            /*
                   1193:             * This is rare, but not impossible.
                   1194:             * In meta mode, dirdeps.mk (only runs at level 0)
                   1195:             * can need more than the default.
                   1196:             */
                   1197:            max_if_depth += MAXIF_BUMP;
1.93      rillig   1198:            cond_state = bmake_realloc(cond_state,
                   1199:                                       max_if_depth * sizeof(*cond_state));
1.1       cgd      1200:        }
1.43      dsl      1201:        state = cond_state[cond_depth];
1.36      dsl      1202:        cond_depth++;
                   1203:        if (state > ELSE_ACTIVE) {
                   1204:            /* If we aren't parsing the data, treat as always false */
                   1205:            cond_state[cond_depth] = SKIP_TO_ELSE;
                   1206:            return COND_SKIP;
1.32      christos 1207:        }
1.1       cgd      1208:     }
                   1209:
1.109     rillig   1210:     /* And evaluate the conditional expression */
1.143     rillig   1211:     if (CondEvalExpression(ifp, line, &value, TRUE, TRUE) == COND_INVALID) {
1.43      dsl      1212:        /* Syntax error in conditional, error message already output. */
                   1213:        /* Skip everything to matching .endif */
                   1214:        cond_state[cond_depth] = SKIP_TO_ELSE;
                   1215:        return COND_SKIP;
1.36      dsl      1216:     }
                   1217:
                   1218:     if (!value) {
                   1219:        cond_state[cond_depth] = SEARCH_FOR_ELIF;
                   1220:        return COND_SKIP;
                   1221:     }
                   1222:     cond_state[cond_depth] = IF_ACTIVE;
                   1223:     return COND_PARSE;
1.1       cgd      1224: }
1.10      christos 1225:
1.1       cgd      1226: void
1.37      dsl      1227: Cond_restore_depth(unsigned int saved_depth)
1.1       cgd      1228: {
1.37      dsl      1229:     int open_conds = cond_depth - cond_min_depth;
                   1230:
                   1231:     if (open_conds != 0 || saved_depth > cond_depth) {
                   1232:        Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds,
                   1233:                    open_conds == 1 ? "" : "s");
                   1234:        cond_depth = cond_min_depth;
1.1       cgd      1235:     }
1.37      dsl      1236:
                   1237:     cond_min_depth = saved_depth;
                   1238: }
                   1239:
                   1240: unsigned int
                   1241: Cond_save_depth(void)
                   1242: {
                   1243:     int depth = cond_min_depth;
                   1244:
                   1245:     cond_min_depth = cond_depth;
                   1246:     return depth;
1.1       cgd      1247: }

CVSweb <webmaster@jp.NetBSD.org>