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