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