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