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

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

1.24    ! espie       1: /*     $OpenPackages$ */
        !             2: /*     $OpenBSD: cond.c,v 1.4 1998/12/05 00:06:27 espie Exp $  */
1.3       millert     3: /*     $NetBSD: cond.c,v 1.7 1996/11/06 17:59:02 christos Exp $        */
1.1       deraadt     4:
                      5: /*
                      6:  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
                      7:  * Copyright (c) 1988, 1989 by Adam de Boor
                      8:  * Copyright (c) 1989 by Berkeley Softworks
                      9:  * All rights reserved.
                     10:  *
                     11:  * This code is derived from software contributed to Berkeley by
                     12:  * Adam de Boor.
                     13:  *
                     14:  * Redistribution and use in source and binary forms, with or without
                     15:  * modification, are permitted provided that the following conditions
                     16:  * are met:
                     17:  * 1. Redistributions of source code must retain the above copyright
                     18:  *    notice, this list of conditions and the following disclaimer.
                     19:  * 2. Redistributions in binary form must reproduce the above copyright
                     20:  *    notice, this list of conditions and the following disclaimer in the
                     21:  *    documentation and/or other materials provided with the distribution.
                     22:  * 3. All advertising materials mentioning features or use of this software
                     23:  *    must display the following acknowledgement:
                     24:  *     This product includes software developed by the University of
                     25:  *     California, Berkeley and its contributors.
                     26:  * 4. Neither the name of the University nor the names of its contributors
                     27:  *    may be used to endorse or promote products derived from this software
                     28:  *    without specific prior written permission.
                     29:  *
                     30:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     31:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     32:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     33:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     34:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     35:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     36:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     37:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     38:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     39:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     40:  * SUCH DAMAGE.
                     41:  */
                     42:
                     43: /*-
                     44:  * cond.c --
                     45:  *     Functions to handle conditionals in a makefile.
                     46:  *
                     47:  * Interface:
1.24    ! espie      48:  *     Cond_Eval       Evaluate the conditional in the passed line.
1.1       deraadt    49:  *
                     50:  */
                     51:
                     52: #include    <ctype.h>
                     53: #include    <math.h>
1.24    ! espie      54: #include    <stddef.h>
1.1       deraadt    55: #include    "make.h"
1.22      espie      56: #include    "ohash.h"
1.1       deraadt    57: #include    "dir.h"
                     58: #include    "buf.h"
1.21      espie      59:
                     60: #ifndef lint
                     61: #if 0
                     62: static char sccsid[] = "@(#)cond.c     8.2 (Berkeley) 1/2/94";
                     63: #else
                     64: UNUSED
1.24    ! espie      65: static char rcsid[] = "$OpenBSD: cond.c,v 1.4 1998/12/05 00:06:27 espie Exp $";
1.21      espie      66: #endif
                     67: #endif /* not lint */
1.1       deraadt    68:
1.24    ! espie      69:
        !            70: /* The parsing of conditional expressions is based on this grammar:
1.1       deraadt    71:  *     E -> F || E
                     72:  *     E -> F
                     73:  *     F -> T && F
                     74:  *     F -> T
                     75:  *     T -> defined(variable)
                     76:  *     T -> make(target)
                     77:  *     T -> exists(file)
                     78:  *     T -> empty(varspec)
                     79:  *     T -> target(name)
                     80:  *     T -> symbol
                     81:  *     T -> $(varspec) op value
                     82:  *     T -> $(varspec) == "string"
                     83:  *     T -> $(varspec) != "string"
                     84:  *     T -> ( E )
                     85:  *     T -> ! T
                     86:  *     op -> == | != | > | < | >= | <=
                     87:  *
                     88:  * 'symbol' is some other symbol to which the default function (condDefProc)
                     89:  * is applied.
                     90:  *
                     91:  * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
                     92:  * will return And for '&' and '&&', Or for '|' and '||', Not for '!',
                     93:  * LParen for '(', RParen for ')' and will evaluate the other terminal
                     94:  * symbols, using either the default function or the function given in the
                     95:  * terminal, and return the result as either True or False.
                     96:  *
1.24    ! espie      97:  * All Non-Terminal functions (CondE, CondF and CondT) return Err on error.  */
1.1       deraadt    98: typedef enum {
1.24    ! espie      99:     False = 0, True = 1, And, Or, Not, LParen, RParen, EndOfFile, None, Err
1.1       deraadt   100: } Token;
                    101:
                    102: /*-
                    103:  * Structures to handle elegantly the different forms of #if's. The
                    104:  * last two fields are stored in condInvert and condDefProc, respectively.
                    105:  */
1.24    ! espie     106: static Boolean CondGetArg(const char **, struct Name *,
        !           107:     const char *, Boolean);
        !           108: static Boolean CondDoDefined(struct Name *);
        !           109: static Boolean CondDoMake(struct Name *);
        !           110: static Boolean CondDoExists(struct Name *);
        !           111: static Boolean CondDoTarget(struct Name *);
        !           112: static Boolean CondCvtArg(const char *, double *);
        !           113: static Token CondToken(Boolean);
        !           114: static Token CondT(Boolean);
        !           115: static Token CondF(Boolean);
        !           116: static Token CondE(Boolean);
        !           117: static Token CondHandleVarSpec(Boolean);
        !           118: static Token CondHandleDefault(Boolean);
        !           119: static const char *find_cond(const char *);
        !           120:
1.1       deraadt   121:
                    122: static struct If {
1.24    ! espie     123:     char       *form;          /* Form of if */
        !           124:     int        formlen;        /* Length of form */
        !           125:     Boolean    doNot;          /* TRUE if default function should be negated */
        !           126:     Boolean    (*defProc)(struct Name *);
        !           127:                                /* Default function to apply */
1.1       deraadt   128: } ifs[] = {
                    129:     { "ifdef",   5,      FALSE,  CondDoDefined },
1.24    ! espie     130:     { "ifndef",   6,     TRUE,   CondDoDefined },
        !           131:     { "ifmake",   6,     FALSE,  CondDoMake },
        !           132:     { "ifnmake",  7,     TRUE,   CondDoMake },
1.1       deraadt   133:     { "if",      2,      FALSE,  CondDoDefined },
1.3       millert   134:     { NULL,      0,      FALSE,  NULL }
1.1       deraadt   135: };
                    136:
1.24    ! espie     137: static Boolean   condInvert;           /* Invert the default function */
1.3       millert   138: static Boolean   (*condDefProc)        /* Default function to apply */
1.24    ! espie     139:                   (struct Name *);
        !           140: static const char *condExpr;           /* The expression to parse */
1.1       deraadt   141: static Token     condPushBack=None;    /* Single push-back token used in
                    142:                                         * parsing */
                    143:
1.24    ! espie     144: #define MAXIF          30        /* greatest depth of #if'ing */
1.1       deraadt   145:
1.16      espie     146: static struct {
                    147:        Boolean         value;
1.24    ! espie     148:        unsigned long   lineno;
        !           149:        const char      *filename;
        !           150: } condStack[MAXIF];                    /* Stack of conditionals */
        !           151: static int       condTop = MAXIF;      /* Top-most conditional */
        !           152: static int       skipIfLevel=0;        /* Depth of skipped conditionals */
        !           153: static Boolean   skipLine = FALSE;     /* Whether the parse module is skipping
1.1       deraadt   154:                                         * lines */
                    155:
1.24    ! espie     156: static const char *
        !           157: find_cond(p)
        !           158:     const char *p;
        !           159: {
        !           160:     for (;;p++) {
        !           161:        if (strchr(" \t)&|$", *p) != NULL)
        !           162:            return p;
        !           163:     }
1.1       deraadt   164: }
1.24    ! espie     165:
        !           166:
1.1       deraadt   167: /*-
                    168:  *-----------------------------------------------------------------------
                    169:  * CondGetArg --
                    170:  *     Find the argument of a built-in function.
                    171:  *
                    172:  * Results:
1.10      espie     173:  *     TRUE if evaluation went okay
1.1       deraadt   174:  *
                    175:  * Side Effects:
1.10      espie     176:  *     The line pointer is set to point to the closing parenthesis of the
1.24    ! espie     177:  *     function call. The argument is filled.
1.1       deraadt   178:  *-----------------------------------------------------------------------
                    179:  */
1.10      espie     180: static Boolean
1.24    ! espie     181: CondGetArg(linePtr, arg, func, parens)
        !           182:     const char                 **linePtr;
        !           183:     struct Name                *arg;
        !           184:     const char         *func;
        !           185:     Boolean            parens; /* TRUE if arg should be bounded by parens */
1.1       deraadt   186: {
1.24    ! espie     187:     const char         *cp;
1.1       deraadt   188:
                    189:     cp = *linePtr;
                    190:     if (parens) {
1.24    ! espie     191:        while (*cp != '(' && *cp != '\0')
1.1       deraadt   192:            cp++;
1.24    ! espie     193:        if (*cp == '(')
1.1       deraadt   194:            cp++;
                    195:     }
                    196:
                    197:     if (*cp == '\0') {
1.24    ! espie     198:        /* No arguments whatsoever. Because 'make' and 'defined' aren't really
1.1       deraadt   199:         * "reserved words", we don't print a message. I think this is better
                    200:         * than hitting the user with a warning message every time s/he uses
1.24    ! espie     201:         * the word 'make' or 'defined' at the beginning of a symbol...  */
        !           202:        arg->s = cp;
        !           203:        arg->e = cp;
        !           204:        arg->tofree = FALSE;
1.10      espie     205:        return FALSE;
1.1       deraadt   206:     }
                    207:
1.24    ! espie     208:     while (*cp == ' ' || *cp == '\t')
1.1       deraadt   209:        cp++;
                    210:
                    211:
1.24    ! espie     212:     cp = Var_Name_Get(cp, arg, NULL, TRUE, find_cond);
1.1       deraadt   213:
1.24    ! espie     214:     while (*cp == ' ' || *cp == '\t')
1.1       deraadt   215:        cp++;
                    216:     if (parens && *cp != ')') {
1.24    ! espie     217:        Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
1.1       deraadt   218:                     func);
1.10      espie     219:        return FALSE;
1.24    ! espie     220:     } else if (parens)
        !           221:        /* Advance pointer past close parenthesis.  */
1.1       deraadt   222:        cp++;
1.3       millert   223:
1.1       deraadt   224:     *linePtr = cp;
1.10      espie     225:     return TRUE;
1.1       deraadt   226: }
1.24    ! espie     227:
1.1       deraadt   228: /*-
                    229:  *-----------------------------------------------------------------------
                    230:  * CondDoDefined --
                    231:  *     Handle the 'defined' function for conditionals.
                    232:  *
                    233:  * Results:
                    234:  *     TRUE if the given variable is defined.
                    235:  *-----------------------------------------------------------------------
                    236:  */
                    237: static Boolean
1.24    ! espie     238: CondDoDefined(arg)
        !           239:     struct Name        *arg;
1.1       deraadt   240: {
1.24    ! espie     241:     if (Var_Value_interval(arg->s, arg->e) != NULL)
        !           242:        return TRUE;
1.6       espie     243:     else
1.24    ! espie     244:        return FALSE;
1.1       deraadt   245: }
1.24    ! espie     246:
1.1       deraadt   247: /*-
                    248:  *-----------------------------------------------------------------------
                    249:  * CondDoMake --
                    250:  *     Handle the 'make' function for conditionals.
                    251:  *
                    252:  * Results:
                    253:  *     TRUE if the given target is being made.
                    254:  *-----------------------------------------------------------------------
                    255:  */
                    256: static Boolean
1.24    ! espie     257: CondDoMake(arg)
        !           258:     struct Name        *arg;
1.1       deraadt   259: {
1.24    ! espie     260:     LstNode ln;
1.1       deraadt   261:
1.24    ! espie     262:     for (ln = Lst_First(&create); ln != NULL; ln = Lst_Adv(ln)) {
        !           263:        if (Str_Matchi((char *)Lst_Datum(ln), arg->s, arg->e))
        !           264:            return TRUE;
1.1       deraadt   265:     }
1.24    ! espie     266:
        !           267:     return FALSE;
1.1       deraadt   268: }
1.24    ! espie     269:
1.1       deraadt   270: /*-
                    271:  *-----------------------------------------------------------------------
                    272:  * CondDoExists --
                    273:  *     See if the given file exists.
                    274:  *
                    275:  * Results:
                    276:  *     TRUE if the file exists and FALSE if it does not.
                    277:  *-----------------------------------------------------------------------
                    278:  */
                    279: static Boolean
1.24    ! espie     280: CondDoExists(arg)
        !           281:     struct Name *arg;
1.1       deraadt   282: {
                    283:     Boolean result;
                    284:     char    *path;
                    285:
1.24    ! espie     286:     path = Dir_FindFilei(arg->s, arg->e, &dirSearchPath);
        !           287:     if (path != NULL) {
1.1       deraadt   288:        result = TRUE;
                    289:        free(path);
                    290:     } else {
                    291:        result = FALSE;
                    292:     }
1.24    ! espie     293:     return result;
1.1       deraadt   294: }
1.24    ! espie     295:
1.1       deraadt   296: /*-
                    297:  *-----------------------------------------------------------------------
                    298:  * CondDoTarget --
                    299:  *     See if the given node exists and is an actual target.
                    300:  *
                    301:  * Results:
                    302:  *     TRUE if the node exists as a target and FALSE if it does not.
                    303:  *-----------------------------------------------------------------------
                    304:  */
                    305: static Boolean
1.24    ! espie     306: CondDoTarget(arg)
        !           307:     struct Name        *arg;
1.1       deraadt   308: {
                    309:     GNode   *gn;
                    310:
1.24    ! espie     311:     gn = Targ_FindNode(arg->s, arg->e, TARG_NOCREATE);
        !           312:     if (gn != NULL && !OP_NOP(gn->type))
        !           313:        return TRUE;
        !           314:     else
        !           315:        return FALSE;
1.1       deraadt   316: }
                    317:
1.24    ! espie     318:
1.1       deraadt   319: /*-
                    320:  *-----------------------------------------------------------------------
                    321:  * CondCvtArg --
                    322:  *     Convert the given number into a double. If the number begins
                    323:  *     with 0x, it is interpreted as a hexadecimal integer
                    324:  *     and converted to a double from there. All other strings just have
                    325:  *     strtod called on them.
                    326:  *
                    327:  * Results:
                    328:  *     Sets 'value' to double value of string.
                    329:  *     Returns true if the string was a valid number, false o.w.
                    330:  *
                    331:  * Side Effects:
                    332:  *     Can change 'value' even if string is not a valid number.
                    333:  *-----------------------------------------------------------------------
                    334:  */
                    335: static Boolean
                    336: CondCvtArg(str, value)
1.24    ! espie     337:     const char         *str;
1.1       deraadt   338:     double             *value;
                    339: {
1.24    ! espie     340:     if (*str == '0' && str[1] == 'x') {
        !           341:        long i;
1.1       deraadt   342:
                    343:        for (str += 2, i = 0; *str; str++) {
                    344:            int x;
1.24    ! espie     345:            if (isdigit(*str))
1.1       deraadt   346:                x  = *str - '0';
1.24    ! espie     347:            else if (isxdigit(*str))
        !           348:                x = 10 + *str - isupper(*str) ? 'A' : 'a';
1.1       deraadt   349:            else
                    350:                return FALSE;
                    351:            i = (i << 4) + x;
                    352:        }
                    353:        *value = (double) i;
                    354:        return TRUE;
                    355:     }
                    356:     else {
                    357:        char *eptr;
                    358:        *value = strtod(str, &eptr);
                    359:        return *eptr == '\0';
                    360:     }
                    361: }
1.24    ! espie     362:
        !           363:
        !           364: static Token
        !           365: CondHandleVarSpec(doEval)
        !           366:     Boolean doEval;
        !           367: {
        !           368:     Token      t;
        !           369:     char       *lhs;
        !           370:     const char *rhs;
        !           371:     const char *op;
        !           372:     size_t     varSpecLen;
        !           373:     Boolean    doFree;
        !           374:
        !           375:     /* Parse the variable spec and skip over it, saving its
        !           376:      * value in lhs.  */
        !           377:     t = Err;
        !           378:     lhs = Var_Parse(condExpr, NULL, doEval,&varSpecLen,&doFree);
        !           379:     if (lhs == var_Error)
        !           380:        /* Even if !doEval, we still report syntax errors, which
        !           381:         * is what getting var_Error back with !doEval means.  */
        !           382:        return Err;
        !           383:     condExpr += varSpecLen;
        !           384:
        !           385:     if (!isspace(*condExpr) &&
        !           386:        strchr("!=><", *condExpr) == NULL) {
        !           387:        BUFFER buf;
        !           388:
        !           389:        Buf_Init(&buf, 0);
        !           390:
        !           391:        Buf_AddString(&buf, lhs);
        !           392:
        !           393:        if (doFree)
        !           394:            free(lhs);
        !           395:
        !           396:        for (;*condExpr && !isspace(*condExpr); condExpr++)
        !           397:            Buf_AddChar(&buf, *condExpr);
        !           398:
        !           399:        lhs = Buf_Retrieve(&buf);
        !           400:
        !           401:        doFree = TRUE;
        !           402:     }
        !           403:
        !           404:     /* Skip whitespace to get to the operator. */
        !           405:     while (isspace(*condExpr))
        !           406:        condExpr++;
        !           407:
        !           408:     /* Make sure the operator is a valid one. If it isn't a
        !           409:      * known relational operator, pretend we got a
        !           410:      * != 0 comparison.  */
        !           411:     op = condExpr;
        !           412:     switch (*condExpr) {
        !           413:        case '!':
        !           414:        case '=':
        !           415:        case '<':
        !           416:        case '>':
        !           417:            if (condExpr[1] == '=')
        !           418:                condExpr += 2;
        !           419:            else
        !           420:                condExpr += 1;
        !           421:            break;
        !           422:        default:
        !           423:            op = "!=";
        !           424:            rhs = "0";
        !           425:
        !           426:            goto do_compare;
        !           427:     }
        !           428:     while (isspace(*condExpr))
        !           429:        condExpr++;
        !           430:     if (*condExpr == '\0') {
        !           431:        Parse_Error(PARSE_WARNING,
        !           432:                    "Missing right-hand-side of operator");
        !           433:        goto error;
        !           434:     }
        !           435:     rhs = condExpr;
        !           436: do_compare:
        !           437:     if (*rhs == '"') {
        !           438:        /* Doing a string comparison. Only allow == and != for
        !           439:         * operators.  */
        !           440:        char    *string;
        !           441:        const char *cp;
        !           442:        int         qt;
        !           443:        BUFFER  buf;
        !           444:
        !           445: do_string_compare:
        !           446:        if ((*op != '!' && *op != '=') || op[1] != '=') {
        !           447:            Parse_Error(PARSE_WARNING,
        !           448:     "String comparison operator should be either == or !=");
        !           449:            goto error;
        !           450:        }
        !           451:
        !           452:        Buf_Init(&buf, 0);
        !           453:        qt = *rhs == '"' ? 1 : 0;
        !           454:
        !           455:        for (cp = &rhs[qt];
        !           456:             ((qt && *cp != '"') ||
        !           457:              (!qt && strchr(" \t)", *cp) == NULL)) &&
        !           458:             *cp != '\0';) {
        !           459:            if (*cp == '$') {
        !           460:                size_t  len;
        !           461:
        !           462:                if (Var_ParseBuffer(&buf, cp, NULL, doEval, &len)
        !           463:                    == SUCCESS) {
        !           464:                    cp += len;
        !           465:                    continue;
        !           466:                }
        !           467:            } else if (*cp == '\\' && cp[1] != '\0')
        !           468:                /* Backslash escapes things -- skip over next
        !           469:                 * character, if it exists.  */
        !           470:                cp++;
        !           471:            Buf_AddChar(&buf, *cp++);
        !           472:        }
        !           473:
        !           474:        string = Buf_Retrieve(&buf);
        !           475:
        !           476:        if (DEBUG(COND))
        !           477:            printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
        !           478:                   lhs, string, op);
        !           479:        /* Null-terminate rhs and perform the comparison.
        !           480:         * t is set to the result.  */
        !           481:        if (*op == '=')
        !           482:            t = strcmp(lhs, string) ? False : True;
        !           483:        else
        !           484:            t = strcmp(lhs, string) ? True : False;
        !           485:        free(string);
        !           486:        if (rhs == condExpr) {
        !           487:            if (!qt && *cp == ')')
        !           488:                condExpr = cp;
        !           489:            else if (*cp == '\0')
        !           490:                condExpr = cp;
        !           491:            else
        !           492:                condExpr = cp + 1;
        !           493:        }
        !           494:     } else {
        !           495:        /* rhs is either a float or an integer. Convert both the
        !           496:         * lhs and the rhs to a double and compare the two.  */
        !           497:        double          left, right;
        !           498:        char            *string;
        !           499:
        !           500:        if (!CondCvtArg(lhs, &left))
        !           501:            goto do_string_compare;
        !           502:        if (*rhs == '$') {
        !           503:            size_t      len;
        !           504:            Boolean     freeIt;
        !           505:
        !           506:            string = Var_Parse(rhs, NULL, doEval,&len,&freeIt);
        !           507:            if (string == var_Error)
        !           508:                right = 0.0;
        !           509:            else {
        !           510:                if (!CondCvtArg(string, &right)) {
        !           511:                    if (freeIt)
        !           512:                        free(string);
        !           513:                    goto do_string_compare;
        !           514:                }
        !           515:                if (freeIt)
        !           516:                    free(string);
        !           517:                if (rhs == condExpr)
        !           518:                    condExpr += len;
        !           519:            }
        !           520:        } else {
        !           521:            if (!CondCvtArg(rhs, &right))
        !           522:                goto do_string_compare;
        !           523:            if (rhs == condExpr) {
        !           524:                /* Skip over the right-hand side.  */
        !           525:                while (!isspace(*condExpr) &&
        !           526:                      *condExpr != '\0')
        !           527:                    condExpr++;
        !           528:
        !           529:            }
        !           530:        }
        !           531:
        !           532:        if (DEBUG(COND))
        !           533:            printf("left = %f, right = %f, op = %.2s\n", left,
        !           534:                   right, op);
        !           535:        switch (op[0]) {
        !           536:        case '!':
        !           537:            if (op[1] != '=') {
        !           538:                Parse_Error(PARSE_WARNING,
        !           539:                            "Unknown operator");
        !           540:                goto error;
        !           541:            }
        !           542:            t = left != right ? True : False;
        !           543:            break;
        !           544:        case '=':
        !           545:            if (op[1] != '=') {
        !           546:                Parse_Error(PARSE_WARNING,
        !           547:                            "Unknown operator");
        !           548:                goto error;
        !           549:            }
        !           550:            t = left == right ? True : False;
        !           551:            break;
        !           552:        case '<':
        !           553:            if (op[1] == '=')
        !           554:                t = left <= right ? True : False;
        !           555:            else
        !           556:                t = left < right ? True : False;
        !           557:            break;
        !           558:        case '>':
        !           559:            if (op[1] == '=')
        !           560:                t = left >= right ? True : False;
        !           561:            else
        !           562:                t = left > right ? True : False;
        !           563:            break;
        !           564:        }
        !           565:     }
        !           566: error:
        !           567:     if (doFree)
        !           568:        free(lhs);
        !           569:     return t;
        !           570: }
        !           571:
        !           572: #define S(s)   s, sizeof(s)-1
        !           573: static struct operator {
        !           574:     const char *s;
        !           575:     size_t len;
        !           576:     Boolean (*proc)(struct Name *);
        !           577: } ops[] = {
        !           578:     {S("defined"), CondDoDefined},
        !           579:     {S("make"), CondDoMake},
        !           580:     {S("exists"), CondDoExists},
        !           581:     {S("target"), CondDoTarget},
        !           582:     {NULL, 0, NULL}
        !           583: };
        !           584: static Token
        !           585: CondHandleDefault(doEval)
        !           586:     Boolean    doEval;
        !           587: {
        !           588:     Boolean    t;
        !           589:     Boolean    (*evalProc)(struct Name *);
        !           590:     Boolean    invert = FALSE;
        !           591:     struct Name        arg;
        !           592:     size_t arglen;
        !           593:
        !           594:     evalProc = NULL;
        !           595:     if (strncmp(condExpr, "empty", 5) == 0) {
        !           596:        /* Use Var_Parse to parse the spec in parens and return
        !           597:         * True if the resulting string is empty.  */
        !           598:        size_t   length;
        !           599:        Boolean doFree;
        !           600:        char    *val;
        !           601:
        !           602:        condExpr += 5;
        !           603:
        !           604:        for (arglen = 0; condExpr[arglen] != '(' && condExpr[arglen] != '\0';)
        !           605:             arglen++;
        !           606:
        !           607:        if (condExpr[arglen] != '\0') {
        !           608:            val = Var_Parse(&condExpr[arglen - 1], NULL,
        !           609:                            doEval, &length, &doFree);
        !           610:            if (val == var_Error)
        !           611:                t = Err;
        !           612:            else {
        !           613:                /* A variable is empty when it just contains
        !           614:                 * spaces... 4/15/92, christos */
        !           615:                char *p;
        !           616:                for (p = val; *p && isspace(*p); p++)
        !           617:                    continue;
        !           618:                t = *p == '\0' ? True : False;
        !           619:            }
        !           620:            if (doFree)
        !           621:                free(val);
        !           622:            /* Advance condExpr to beyond the closing ). Note that
        !           623:             * we subtract one from arglen + length b/c length
        !           624:             * is calculated from condExpr[arglen - 1].  */
        !           625:            condExpr += arglen + length - 1;
        !           626:            return t;
        !           627:        } else
        !           628:            condExpr -= 5;
        !           629:     } else {
        !           630:        struct operator *op;
        !           631:
        !           632:        for (op = ops; op != NULL; op++)
        !           633:            if (strncmp(condExpr, op->s, op->len) == 0) {
        !           634:                condExpr += op->len;
        !           635:                if (CondGetArg(&condExpr, &arg, op->s, TRUE))
        !           636:                    evalProc = op->proc;
        !           637:                else
        !           638:                    condExpr -= op->len;
        !           639:                break;
        !           640:            }
        !           641:     }
        !           642:     if (evalProc == NULL) {
        !           643:        /* The symbol is itself the argument to the default
        !           644:         * function. We advance condExpr to the end of the symbol
        !           645:         * by hand (the next whitespace, closing paren or
        !           646:         * binary operator) and set to invert the evaluation
        !           647:         * function if condInvert is TRUE.  */
        !           648:        invert = condInvert;
        !           649:        evalProc = condDefProc;
        !           650:        /* XXX should we ignore problems now ? */
        !           651:        CondGetArg(&condExpr, &arg, "", FALSE);
        !           652:     }
        !           653:
        !           654:     /* Evaluate the argument using the set function. If invert
        !           655:      * is TRUE, we invert the sense of the function.  */
        !           656:     t = (!doEval || (*evalProc)(&arg) ?
        !           657:         (invert ? False : True) :
        !           658:         (invert ? True : False));
        !           659:     Var_Name_Free(&arg);
        !           660:     return t;
        !           661: }
        !           662:
1.1       deraadt   663: /*-
                    664:  *-----------------------------------------------------------------------
                    665:  * CondToken --
                    666:  *     Return the next token from the input.
                    667:  *
                    668:  * Results:
                    669:  *     A Token for the next lexical token in the stream.
                    670:  *
                    671:  * Side Effects:
                    672:  *     condPushback will be set back to None if it is used.
                    673:  *-----------------------------------------------------------------------
                    674:  */
                    675: static Token
                    676: CondToken(doEval)
                    677:     Boolean doEval;
                    678: {
                    679:
1.24    ! espie     680:     if (condPushBack != None) {
        !           681:        Token     t;
        !           682:
        !           683:        t = condPushBack;
        !           684:        condPushBack = None;
        !           685:        return t;
        !           686:     }
        !           687:
        !           688:     while (*condExpr == ' ' || *condExpr == '\t')
        !           689:        condExpr++;
        !           690:     switch (*condExpr) {
        !           691:        case '(':
1.1       deraadt   692:            condExpr++;
1.24    ! espie     693:            return LParen;
        !           694:        case ')':
        !           695:            condExpr++;
        !           696:            return RParen;
        !           697:        case '|':
        !           698:            if (condExpr[1] == '|')
1.1       deraadt   699:                condExpr++;
1.24    ! espie     700:            condExpr++;
        !           701:            return Or;
        !           702:        case '&':
        !           703:            if (condExpr[1] == '&')
1.1       deraadt   704:                condExpr++;
1.24    ! espie     705:            condExpr++;
        !           706:            return And;
        !           707:        case '!':
        !           708:            condExpr++;
        !           709:            return Not;
        !           710:        case '\n':
        !           711:        case '\0':
        !           712:            return EndOfFile;
        !           713:        case '$':
        !           714:            return CondHandleVarSpec(doEval);
        !           715:        default:
        !           716:            return CondHandleDefault(doEval);
1.1       deraadt   717:     }
                    718: }
1.24    ! espie     719:
1.1       deraadt   720: /*-
                    721:  *-----------------------------------------------------------------------
                    722:  * CondT --
                    723:  *     Parse a single term in the expression. This consists of a terminal
                    724:  *     symbol or Not and a terminal symbol (not including the binary
                    725:  *     operators):
                    726:  *         T -> defined(variable) | make(target) | exists(file) | symbol
                    727:  *         T -> ! T | ( E )
                    728:  *
                    729:  * Results:
                    730:  *     True, False or Err.
                    731:  *
                    732:  * Side Effects:
                    733:  *     Tokens are consumed.
                    734:  *-----------------------------------------------------------------------
                    735:  */
                    736: static Token
                    737: CondT(doEval)
                    738:     Boolean doEval;
                    739: {
                    740:     Token   t;
                    741:
                    742:     t = CondToken(doEval);
                    743:
1.24    ! espie     744:     if (t == EndOfFile)
        !           745:        /* If we reached the end of the expression, the expression
        !           746:         * is malformed...  */
1.1       deraadt   747:        t = Err;
1.24    ! espie     748:     else if (t == LParen) {
        !           749:        /* T -> ( E ).  */
1.1       deraadt   750:        t = CondE(doEval);
1.24    ! espie     751:        if (t != Err)
        !           752:            if (CondToken(doEval) != RParen)
1.1       deraadt   753:                t = Err;
                    754:     } else if (t == Not) {
                    755:        t = CondT(doEval);
1.24    ! espie     756:        if (t == True)
1.1       deraadt   757:            t = False;
1.24    ! espie     758:        else if (t == False)
1.1       deraadt   759:            t = True;
                    760:     }
1.24    ! espie     761:     return t;
1.1       deraadt   762: }
1.24    ! espie     763:
1.1       deraadt   764: /*-
                    765:  *-----------------------------------------------------------------------
                    766:  * CondF --
                    767:  *     Parse a conjunctive factor (nice name, wot?)
                    768:  *         F -> T && F | T
                    769:  *
                    770:  * Results:
                    771:  *     True, False or Err
                    772:  *
                    773:  * Side Effects:
                    774:  *     Tokens are consumed.
                    775:  *-----------------------------------------------------------------------
                    776:  */
                    777: static Token
                    778: CondF(doEval)
                    779:     Boolean doEval;
                    780: {
                    781:     Token   l, o;
                    782:
                    783:     l = CondT(doEval);
                    784:     if (l != Err) {
                    785:        o = CondToken(doEval);
                    786:
                    787:        if (o == And) {
1.24    ! espie     788:            /* F -> T && F
1.1       deraadt   789:             *
                    790:             * If T is False, the whole thing will be False, but we have to
                    791:             * parse the r.h.s. anyway (to throw it away).
1.24    ! espie     792:             * If T is True, the result is the r.h.s., be it an Err or no.  */
        !           793:            if (l == True)
1.1       deraadt   794:                l = CondF(doEval);
1.24    ! espie     795:            else
        !           796:                (void)CondF(FALSE);
        !           797:        } else
        !           798:            /* F -> T.  */
        !           799:            condPushBack = o;
1.1       deraadt   800:     }
1.24    ! espie     801:     return l;
1.1       deraadt   802: }
1.24    ! espie     803:
1.1       deraadt   804: /*-
                    805:  *-----------------------------------------------------------------------
                    806:  * CondE --
                    807:  *     Main expression production.
                    808:  *         E -> F || E | F
                    809:  *
                    810:  * Results:
                    811:  *     True, False or Err.
                    812:  *
                    813:  * Side Effects:
                    814:  *     Tokens are, of course, consumed.
                    815:  *-----------------------------------------------------------------------
                    816:  */
                    817: static Token
                    818: CondE(doEval)
                    819:     Boolean doEval;
                    820: {
                    821:     Token   l, o;
                    822:
                    823:     l = CondF(doEval);
                    824:     if (l != Err) {
                    825:        o = CondToken(doEval);
                    826:
                    827:        if (o == Or) {
1.24    ! espie     828:            /* E -> F || E
1.1       deraadt   829:             *
                    830:             * A similar thing occurs for ||, except that here we make sure
                    831:             * the l.h.s. is False before we bother to evaluate the r.h.s.
                    832:             * Once again, if l is False, the result is the r.h.s. and once
1.24    ! espie     833:             * again if l is True, we parse the r.h.s. to throw it away.  */
        !           834:            if (l == False)
1.1       deraadt   835:                l = CondE(doEval);
1.24    ! espie     836:            else
        !           837:                (void)CondE(FALSE);
        !           838:        } else
        !           839:            /* E -> F.  */
        !           840:            condPushBack = o;
1.1       deraadt   841:     }
1.24    ! espie     842:     return l;
1.1       deraadt   843: }
1.24    ! espie     844:
1.1       deraadt   845: /*-
                    846:  *-----------------------------------------------------------------------
                    847:  * Cond_Eval --
1.24    ! espie     848:  *     Evaluate the conditional in the passed fragment. The fragment
1.1       deraadt   849:  *     looks like this:
1.24    ! espie     850:  *         <cond-type> <expr>
1.1       deraadt   851:  *     where <cond-type> is any of if, ifmake, ifnmake, ifdef,
                    852:  *     ifndef, elif, elifmake, elifnmake, elifdef, elifndef
                    853:  *     and <expr> consists of &&, ||, !, make(target), defined(variable)
                    854:  *     and parenthetical groupings thereof.
                    855:  *
                    856:  * Results:
                    857:  *     COND_PARSE      if should parse lines after the conditional
                    858:  *     COND_SKIP       if should skip lines after the conditional
1.24    ! espie     859:  *     COND_INVALID    if not a valid conditional.
1.1       deraadt   860:  *-----------------------------------------------------------------------
                    861:  */
                    862: int
1.24    ! espie     863: Cond_Eval(line)
        !           864:     char           *line;    /* Line to parse */
1.1       deraadt   865: {
                    866:     struct If      *ifp;
1.24    ! espie     867:     Boolean        isElse;
        !           868:     Boolean        value = FALSE;
        !           869:     int            level;      /* Level at which to report errors. */
1.1       deraadt   870:
                    871:     level = PARSE_FATAL;
                    872:
1.24    ! espie     873:     /* Stuff we are looking for can be if*, elif*, else, or endif.
        !           874:      * otherwise, this is not our turf.  */
1.1       deraadt   875:
1.24    ! espie     876:     /* Find what type of if we're dealing with. The result is left
        !           877:      * in ifp and isElse is set TRUE if it's an elif line.  */
1.1       deraadt   878:     if (line[0] == 'e' && line[1] == 'l') {
                    879:        line += 2;
                    880:        isElse = TRUE;
1.24    ! espie     881:     } else if (strncmp(line, "endif", 5) == 0) {
        !           882:        /* End of a conditional section. If skipIfLevel is non-zero, that
1.1       deraadt   883:         * conditional was skipped, so lines following it should also be
                    884:         * skipped. Hence, we return COND_SKIP. Otherwise, the conditional
                    885:         * was read so succeeding lines should be parsed (think about it...)
                    886:         * so we return COND_PARSE, unless this endif isn't paired with
1.24    ! espie     887:         * a decent if.  */
1.1       deraadt   888:        if (skipIfLevel != 0) {
                    889:            skipIfLevel -= 1;
1.24    ! espie     890:            return COND_SKIP;
1.1       deraadt   891:        } else {
                    892:            if (condTop == MAXIF) {
1.24    ! espie     893:                Parse_Error(level, "if-less endif");
        !           894:                return COND_INVALID;
1.1       deraadt   895:            } else {
                    896:                skipLine = FALSE;
                    897:                condTop += 1;
1.24    ! espie     898:                return COND_PARSE;
1.1       deraadt   899:            }
                    900:        }
1.24    ! espie     901:     } else
1.1       deraadt   902:        isElse = FALSE;
1.3       millert   903:
1.24    ! espie     904:     /* Figure out what sort of conditional it is -- what its default
        !           905:      * function is, etc. -- by looking in the table of valid "ifs" */
        !           906:     for (ifp = ifs; ifp->form != NULL; ifp++) {
        !           907:        if (strncmp(ifp->form, line, ifp->formlen) == 0)
1.1       deraadt   908:            break;
                    909:     }
                    910:
1.24    ! espie     911:     if (ifp->form == NULL) {
        !           912:        /* Nothing fits. If the first word on the line is actually
1.1       deraadt   913:         * "else", it's a valid conditional whose value is the inverse
1.24    ! espie     914:         * of the previous if we parsed.  */
        !           915:        if (isElse && line[0] == 's' && line[1] == 'e') {
1.1       deraadt   916:            if (condTop == MAXIF) {
1.24    ! espie     917:                Parse_Error(level, "if-less else");
        !           918:                return COND_INVALID;
        !           919:            } else if (skipIfLevel == 0)
1.16      espie     920:                value = !condStack[condTop].value;
1.24    ! espie     921:            else
        !           922:                return COND_SKIP;
        !           923:        } else
        !           924:            /* Not a valid conditional type. No error...  */
        !           925:            return COND_INVALID;
1.1       deraadt   926:     } else {
                    927:        if (isElse) {
                    928:            if (condTop == MAXIF) {
1.24    ! espie     929:                Parse_Error(level, "if-less elif");
        !           930:                return COND_INVALID;
1.1       deraadt   931:            } else if (skipIfLevel != 0) {
1.24    ! espie     932:                /* If skipping this conditional, just ignore the whole thing.
1.1       deraadt   933:                 * If we don't, the user might be employing a variable that's
                    934:                 * undefined, for which there's an enclosing ifdef that
1.24    ! espie     935:                 * we're skipping...  */
        !           936:                return COND_SKIP;
1.1       deraadt   937:            }
                    938:        } else if (skipLine) {
1.24    ! espie     939:            /* Don't even try to evaluate a conditional that's not an else if
        !           940:             * we're skipping things...  */
1.1       deraadt   941:            skipIfLevel += 1;
1.24    ! espie     942:            return COND_SKIP;
1.1       deraadt   943:        }
                    944:
1.24    ! espie     945:        /* Initialize file-global variables for parsing.  */
1.1       deraadt   946:        condDefProc = ifp->defProc;
                    947:        condInvert = ifp->doNot;
1.3       millert   948:
1.1       deraadt   949:        line += ifp->formlen;
1.3       millert   950:
1.24    ! espie     951:        while (*line == ' ' || *line == '\t')
1.1       deraadt   952:            line++;
1.3       millert   953:
1.1       deraadt   954:        condExpr = line;
                    955:        condPushBack = None;
1.3       millert   956:
1.1       deraadt   957:        switch (CondE(TRUE)) {
                    958:            case True:
                    959:                if (CondToken(TRUE) == EndOfFile) {
                    960:                    value = TRUE;
                    961:                    break;
                    962:                }
                    963:                goto err;
1.20      espie     964:                /* FALLTHROUGH */
1.1       deraadt   965:            case False:
                    966:                if (CondToken(TRUE) == EndOfFile) {
                    967:                    value = FALSE;
                    968:                    break;
                    969:                }
1.20      espie     970:                /* FALLTHROUGH */
1.1       deraadt   971:            case Err:
                    972:            err:
1.24    ! espie     973:                Parse_Error(level, "Malformed conditional (%s)", line);
        !           974:                return COND_INVALID;
1.1       deraadt   975:            default:
                    976:                break;
                    977:        }
                    978:     }
1.24    ! espie     979:     if (!isElse)
1.1       deraadt   980:        condTop -= 1;
1.24    ! espie     981:     else if (skipIfLevel != 0 || condStack[condTop].value) {
        !           982:        /* If this is an else-type conditional, it should only take effect
1.1       deraadt   983:         * if its corresponding if was evaluated and FALSE. If its if was
                    984:         * TRUE or skipped, we return COND_SKIP (and start skipping in case
                    985:         * we weren't already), leaving the stack unmolested so later elif's
1.24    ! espie     986:         * don't screw up...  */
1.1       deraadt   987:        skipLine = TRUE;
1.24    ! espie     988:        return COND_SKIP;
1.1       deraadt   989:     }
                    990:
                    991:     if (condTop < 0) {
1.24    ! espie     992:        /* This is the one case where we can definitely proclaim a fatal
        !           993:         * error. If we don't, we're hosed.  */
        !           994:        Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF);
        !           995:        return COND_INVALID;
1.1       deraadt   996:     } else {
1.16      espie     997:        condStack[condTop].value = value;
                    998:        condStack[condTop].lineno = Parse_Getlineno();
                    999:        condStack[condTop].filename = Parse_Getfilename();
1.1       deraadt  1000:        skipLine = !value;
1.24    ! espie    1001:        return value ? COND_PARSE : COND_SKIP;
1.1       deraadt  1002:     }
                   1003: }
1.24    ! espie    1004:
1.1       deraadt  1005: /*-
                   1006:  *-----------------------------------------------------------------------
                   1007:  * Cond_End --
                   1008:  *     Make sure everything's clean at the end of a makefile.
                   1009:  *
                   1010:  * Side Effects:
                   1011:  *     Parse_Error will be called if open conditionals are around.
                   1012:  *-----------------------------------------------------------------------
                   1013:  */
                   1014: void
                   1015: Cond_End()
                   1016: {
1.16      espie    1017:     int i;
                   1018:
1.1       deraadt  1019:     if (condTop != MAXIF) {
                   1020:        Parse_Error(PARSE_FATAL, "%d open conditional%s", MAXIF-condTop,
                   1021:                    MAXIF-condTop == 1 ? "" : "s");
1.16      espie    1022:        for (i = MAXIF-1; i >= condTop; i--) {
                   1023:            fprintf(stderr, "\t at line %lu of %s\n", condStack[i].lineno,
                   1024:                condStack[i].filename);
                   1025:        }
1.1       deraadt  1026:     }
                   1027:     condTop = MAXIF;
                   1028: }