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