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