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