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