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