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: }