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