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