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