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