1/*	$NetBSD: cond.c,v 1.363 2024/04/23 22:51:28 rillig Exp $	*/
2
3/*
4 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Adam de Boor.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35/*
36 * Copyright (c) 1988, 1989 by Adam de Boor
37 * Copyright (c) 1989 by Berkeley Softworks
38 * All rights reserved.
39 *
40 * This code is derived from software contributed to Berkeley by
41 * Adam de Boor.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 *    notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 *    notice, this list of conditions and the following disclaimer in the
50 *    documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 *    must display the following acknowledgement:
53 *	This product includes software developed by the University of
54 *	California, Berkeley and its contributors.
55 * 4. Neither the name of the University nor the names of its contributors
56 *    may be used to endorse or promote products derived from this software
57 *    without specific prior written permission.
58 *
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE.
70 */
71
72/*
73 * Handling of conditionals in a makefile.
74 *
75 * Interface:
76 *	Cond_EvalLine   Evaluate the conditional directive, such as
77 *			'.if <cond>', '.elifnmake <cond>', '.else', '.endif'.
78 *
79 *	Cond_EvalCondition
80 *			Evaluate the conditional, which is either the argument
81 *			of one of the .if directives or the condition in a
82 *			':?then:else' variable modifier.
83 *
84 *	Cond_EndFile	At the end of reading a makefile, ensure that the
85 *			conditional directives are well-balanced.
86 */
87
88#include <errno.h>
89
90#include "make.h"
91#include "dir.h"
92
93/*	"@(#)cond.c	8.2 (Berkeley) 1/2/94"	*/
94MAKE_RCSID("$NetBSD: cond.c,v 1.363 2024/04/23 22:51:28 rillig Exp $");
95
96/*
97 * Conditional expressions conform to this grammar:
98 *	Or -> And ('||' And)*
99 *	And -> Term ('&&' Term)*
100 *	Term -> Function '(' Argument ')'
101 *	Term -> Leaf Operator Leaf
102 *	Term -> Leaf
103 *	Term -> '(' Or ')'
104 *	Term -> '!' Term
105 *	Leaf -> "string"
106 *	Leaf -> Number
107 *	Leaf -> VariableExpression
108 *	Leaf -> BareWord
109 *	Operator -> '==' | '!=' | '>' | '<' | '>=' | '<='
110 *
111 * BareWord is an unquoted string literal, its evaluation depends on the kind
112 * of '.if' directive.
113 *
114 * The tokens are scanned by CondParser_Token, which returns:
115 *	TOK_AND		for '&&'
116 *	TOK_OR		for '||'
117 *	TOK_NOT		for '!'
118 *	TOK_LPAREN	for '('
119 *	TOK_RPAREN	for ')'
120 *
121 * Other terminal symbols are evaluated using either the default function or
122 * the function given in the terminal, they return either TOK_TRUE, TOK_FALSE
123 * or TOK_ERROR.
124 */
125typedef enum Token {
126	TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT,
127	TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
128} Token;
129
130typedef enum ComparisonOp {
131	LT, LE, GT, GE, EQ, NE
132} ComparisonOp;
133
134typedef struct CondParser {
135
136	/*
137	 * The plain '.if ${VAR}' evaluates to true if the value of the
138	 * expression has length > 0 and is not numerically zero.  The other
139	 * '.if' variants delegate to evalBare instead, for example '.ifdef
140	 * ${VAR}' is equivalent to '.if defined(${VAR})', checking whether
141	 * the variable named by the expression '${VAR}' is defined.
142	 */
143	bool plain;
144
145	/* The function to apply on unquoted bare words. */
146	bool (*evalBare)(const char *);
147	bool negateEvalBare;
148
149	/*
150	 * Whether the left-hand side of a comparison may be an unquoted
151	 * string.  This is allowed for expressions of the form
152	 * ${condition:?:}, see ApplyModifier_IfElse.  Such a condition is
153	 * expanded before it is evaluated, due to ease of implementation.
154	 * This means that at the point where the condition is evaluated,
155	 * make cannot know anymore whether the left-hand side had originally
156	 * been an expression or a plain word.
157	 *
158	 * In conditional directives like '.if', the left-hand side must
159	 * either be an expression, a quoted string or a number.
160	 */
161	bool leftUnquotedOK;
162
163	const char *p;		/* The remaining condition to parse */
164	Token curr;		/* Single push-back token used in parsing */
165
166	/*
167	 * Whether an error message has already been printed for this
168	 * condition.
169	 */
170	bool printedError;
171} CondParser;
172
173static CondResult CondParser_Or(CondParser *, bool);
174
175unsigned int cond_depth = 0;	/* current .if nesting level */
176
177/* Names for ComparisonOp. */
178static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" };
179
180MAKE_INLINE bool
181skip_string(const char **pp, const char *str)
182{
183	size_t len = strlen(str);
184	bool ok = strncmp(*pp, str, len) == 0;
185	if (ok)
186		*pp += len;
187	return ok;
188}
189
190static Token
191ToToken(bool cond)
192{
193	return cond ? TOK_TRUE : TOK_FALSE;
194}
195
196static void
197CondParser_SkipWhitespace(CondParser *par)
198{
199	cpp_skip_whitespace(&par->p);
200}
201
202/*
203 * Parse a single word, taking into account balanced parentheses as well as
204 * embedded expressions.  Used for the argument of a built-in function as
205 * well as for bare words, which are then passed to the default function.
206 */
207static char *
208ParseWord(const char **pp, bool doEval)
209{
210	const char *p = *pp;
211	Buffer word;
212	int depth;
213
214	Buf_Init(&word);
215
216	depth = 0;
217	for (;;) {
218		char ch = *p;
219		if (ch == '\0' || ch == ' ' || ch == '\t')
220			break;
221		if ((ch == '&' || ch == '|') && depth == 0)
222			break;
223		if (ch == '$') {
224			VarEvalMode emode = doEval
225			    ? VARE_UNDEFERR
226			    : VARE_PARSE_ONLY;
227			/*
228			 * TODO: make Var_Parse complain about undefined
229			 * variables.
230			 */
231			FStr nestedVal = Var_Parse(&p, SCOPE_CMDLINE, emode);
232			/* TODO: handle errors */
233			Buf_AddStr(&word, nestedVal.str);
234			FStr_Done(&nestedVal);
235			continue;
236		}
237		if (ch == '(')
238			depth++;
239		else if (ch == ')' && --depth < 0)
240			break;
241		Buf_AddByte(&word, ch);
242		p++;
243	}
244
245	cpp_skip_hspace(&p);
246	*pp = p;
247
248	return Buf_DoneData(&word);
249}
250
251/* Parse the function argument, including the surrounding parentheses. */
252static char *
253ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func)
254{
255	const char *p = *pp;
256	char *res;
257
258	p++;			/* skip the '(' */
259	cpp_skip_hspace(&p);
260	res = ParseWord(&p, doEval);
261	cpp_skip_hspace(&p);
262
263	if (*p++ != ')') {
264		int len = 0;
265		while (ch_isalpha(func[len]))
266			len++;
267
268		Parse_Error(PARSE_FATAL,
269		    "Missing closing parenthesis for %.*s()", len, func);
270		par->printedError = true;
271		free(res);
272		return NULL;
273	}
274
275	*pp = p;
276	return res;
277}
278
279/* See if the given variable is defined. */
280static bool
281FuncDefined(const char *var)
282{
283	return Var_Exists(SCOPE_CMDLINE, var);
284}
285
286/* See if a target matching targetPattern is requested to be made. */
287static bool
288FuncMake(const char *targetPattern)
289{
290	StringListNode *ln;
291	bool warned = false;
292
293	for (ln = opts.create.first; ln != NULL; ln = ln->next) {
294		StrMatchResult res = Str_Match(ln->datum, targetPattern);
295		if (res.error != NULL && !warned) {
296			warned = true;
297			Parse_Error(PARSE_WARNING,
298			    "%s in pattern argument '%s' to function 'make'",
299			    res.error, targetPattern);
300		}
301		if (res.matched)
302			return true;
303	}
304	return false;
305}
306
307/* See if the given file exists. */
308static bool
309FuncExists(const char *file)
310{
311	bool result;
312	char *path;
313
314	path = Dir_FindFile(file, &dirSearchPath);
315	DEBUG2(COND, "exists(%s) result is \"%s\"\n",
316	    file, path != NULL ? path : "");
317	result = path != NULL;
318	free(path);
319	return result;
320}
321
322/* See if the given node exists and is an actual target. */
323static bool
324FuncTarget(const char *node)
325{
326	GNode *gn = Targ_FindNode(node);
327	return gn != NULL && GNode_IsTarget(gn);
328}
329
330/*
331 * See if the given node exists and is an actual target with commands
332 * associated with it.
333 */
334static bool
335FuncCommands(const char *node)
336{
337	GNode *gn = Targ_FindNode(node);
338	return gn != NULL && GNode_IsTarget(gn) &&
339	       !Lst_IsEmpty(&gn->commands);
340}
341
342/*
343 * Convert the string to a floating point number.  Accepted formats are
344 * base-10 integer, base-16 integer and finite floating point numbers.
345 */
346static bool
347TryParseNumber(const char *str, double *out_value)
348{
349	char *end;
350	unsigned long ul_val;
351	double dbl_val;
352
353	if (str[0] == '\0') {	/* XXX: why is an empty string a number? */
354		*out_value = 0.0;
355		return true;
356	}
357
358	errno = 0;
359	ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10);
360	if (*end == '\0' && errno != ERANGE) {
361		*out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val;
362		return true;
363	}
364
365	if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E')
366		return false;	/* skip the expensive strtod call */
367	dbl_val = strtod(str, &end);
368	if (*end != '\0')
369		return false;
370
371	*out_value = dbl_val;
372	return true;
373}
374
375static bool
376is_separator(char ch)
377{
378	return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' ||
379	       ch == '>' || ch == '<' || ch == ')' /* but not '(' */;
380}
381
382/*
383 * In a quoted or unquoted string literal or a number, parse an
384 * expression and add its value to the buffer.
385 *
386 * Return whether to continue parsing the leaf.
387 *
388 * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX}
389 */
390static bool
391CondParser_StringExpr(CondParser *par, const char *start,
392		      bool doEval, bool quoted,
393		      Buffer *buf, FStr *inout_str)
394{
395	VarEvalMode emode;
396	const char *p;
397	bool atStart;		/* true means an expression outside quotes */
398
399	emode = doEval && quoted ? VARE_WANTRES
400	    : doEval ? VARE_UNDEFERR
401	    : VARE_PARSE_ONLY;
402
403	p = par->p;
404	atStart = p == start;
405	*inout_str = Var_Parse(&p, SCOPE_CMDLINE, emode);
406	/* TODO: handle errors */
407	if (inout_str->str == var_Error) {
408		FStr_Done(inout_str);
409		*inout_str = FStr_InitRefer(NULL);
410		return false;
411	}
412	par->p = p;
413
414	if (atStart && is_separator(par->p[0]))
415		return false;
416
417	Buf_AddStr(buf, inout_str->str);
418	FStr_Done(inout_str);
419	*inout_str = FStr_InitRefer(NULL);	/* not finished yet */
420	return true;
421}
422
423/*
424 * Parse a string from an expression or an optionally quoted string,
425 * on the left-hand and right-hand sides of comparisons.
426 *
427 * Return the string without any enclosing quotes, or NULL on error.
428 * Sets out_quoted if the leaf was a quoted string literal.
429 */
430static FStr
431CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
432		bool *out_quoted)
433{
434	Buffer buf;
435	FStr str;
436	bool quoted;
437	const char *start;
438
439	Buf_Init(&buf);
440	str = FStr_InitRefer(NULL);
441	*out_quoted = quoted = par->p[0] == '"';
442	start = par->p;
443	if (quoted)
444		par->p++;
445
446	while (par->p[0] != '\0' && str.str == NULL) {
447		switch (par->p[0]) {
448		case '\\':
449			par->p++;
450			if (par->p[0] != '\0') {
451				Buf_AddByte(&buf, par->p[0]);
452				par->p++;
453			}
454			continue;
455		case '"':
456			par->p++;
457			if (quoted)
458				goto return_buf;	/* skip the closing quote */
459			Buf_AddByte(&buf, '"');
460			continue;
461		case ')':	/* see is_separator */
462		case '!':
463		case '=':
464		case '>':
465		case '<':
466		case ' ':
467		case '\t':
468			if (!quoted)
469				goto return_buf;
470			Buf_AddByte(&buf, par->p[0]);
471			par->p++;
472			continue;
473		case '$':
474			if (!CondParser_StringExpr(par,
475			    start, doEval, quoted, &buf, &str))
476				goto return_str;
477			continue;
478		default:
479			if (!unquotedOK && !quoted && *start != '$' &&
480			    !ch_isdigit(*start)) {
481				str = FStr_InitRefer(NULL);
482				goto return_str;
483			}
484			Buf_AddByte(&buf, par->p[0]);
485			par->p++;
486			continue;
487		}
488	}
489return_buf:
490	str = FStr_InitOwn(buf.data);
491	buf.data = NULL;
492return_str:
493	Buf_Done(&buf);
494	return str;
495}
496
497/*
498 * Evaluate a "comparison without operator", such as in ".if ${VAR}" or
499 * ".if 0".
500 */
501static bool
502EvalTruthy(CondParser *par, const char *value, bool quoted)
503{
504	double num;
505
506	if (quoted)
507		return value[0] != '\0';
508	if (TryParseNumber(value, &num))
509		return num != 0.0;
510	if (par->plain)
511		return value[0] != '\0';
512	return par->evalBare(value) != par->negateEvalBare;
513}
514
515/* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
516static bool
517EvalCompareNum(double lhs, ComparisonOp op, double rhs)
518{
519	DEBUG3(COND, "Comparing %f %s %f\n", lhs, opname[op], rhs);
520
521	switch (op) {
522	case LT:
523		return lhs < rhs;
524	case LE:
525		return lhs <= rhs;
526	case GT:
527		return lhs > rhs;
528	case GE:
529		return lhs >= rhs;
530	case EQ:
531		return lhs == rhs;
532	default:
533		return lhs != rhs;
534	}
535}
536
537static Token
538EvalCompareStr(CondParser *par, const char *lhs,
539	       ComparisonOp op, const char *rhs)
540{
541	if (op != EQ && op != NE) {
542		Parse_Error(PARSE_FATAL,
543		    "Comparison with '%s' requires both operands "
544		    "'%s' and '%s' to be numeric",
545		    opname[op], lhs, rhs);
546		par->printedError = true;
547		return TOK_ERROR;
548	}
549
550	DEBUG3(COND, "Comparing \"%s\" %s \"%s\"\n", lhs, opname[op], rhs);
551	return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0));
552}
553
554/* Evaluate a comparison, such as "${VAR} == 12345". */
555static Token
556EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted,
557	    ComparisonOp op, const char *rhs, bool rhsQuoted)
558{
559	double left, right;
560
561	if (!rhsQuoted && !lhsQuoted)
562		if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
563			return ToToken(EvalCompareNum(left, op, right));
564
565	return EvalCompareStr(par, lhs, op, rhs);
566}
567
568static bool
569CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
570{
571	const char *p = par->p;
572
573	if (p[0] == '<' && p[1] == '=')
574		return par->p += 2, *out_op = LE, true;
575	if (p[0] == '<')
576		return par->p += 1, *out_op = LT, true;
577	if (p[0] == '>' && p[1] == '=')
578		return par->p += 2, *out_op = GE, true;
579	if (p[0] == '>')
580		return par->p += 1, *out_op = GT, true;
581	if (p[0] == '=' && p[1] == '=')
582		return par->p += 2, *out_op = EQ, true;
583	if (p[0] == '!' && p[1] == '=')
584		return par->p += 2, *out_op = NE, true;
585	return false;
586}
587
588/*
589 * Parse a comparison condition such as:
590 *
591 *	0
592 *	${VAR:Mpattern}
593 *	${VAR} == value
594 *	${VAR:U0} < 12345
595 */
596static Token
597CondParser_Comparison(CondParser *par, bool doEval)
598{
599	Token t = TOK_ERROR;
600	FStr lhs, rhs;
601	ComparisonOp op;
602	bool lhsQuoted, rhsQuoted;
603
604	lhs = CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhsQuoted);
605	if (lhs.str == NULL)
606		goto done_lhs;
607
608	CondParser_SkipWhitespace(par);
609
610	if (!CondParser_ComparisonOp(par, &op)) {
611		t = ToToken(doEval && EvalTruthy(par, lhs.str, lhsQuoted));
612		goto done_lhs;
613	}
614
615	CondParser_SkipWhitespace(par);
616
617	if (par->p[0] == '\0') {
618		Parse_Error(PARSE_FATAL,
619		    "Missing right-hand side of operator '%s'", opname[op]);
620		par->printedError = true;
621		goto done_lhs;
622	}
623
624	rhs = CondParser_Leaf(par, doEval, true, &rhsQuoted);
625	t = rhs.str == NULL ? TOK_ERROR
626	    : !doEval ? TOK_FALSE
627	    : EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
628	FStr_Done(&rhs);
629
630done_lhs:
631	FStr_Done(&lhs);
632	return t;
633}
634
635/*
636 * The argument to empty() is a variable name, optionally followed by
637 * variable modifiers.
638 */
639static bool
640CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
641{
642	const char *p = par->p;
643	Token tok;
644	FStr val;
645
646	if (!skip_string(&p, "empty"))
647		return false;
648
649	cpp_skip_whitespace(&p);
650	if (*p != '(')
651		return false;
652
653	p--;			/* Make p[1] point to the '('. */
654	val = Var_Parse(&p, SCOPE_CMDLINE,
655	    doEval ? VARE_WANTRES : VARE_PARSE_ONLY);
656	/* TODO: handle errors */
657
658	if (val.str == var_Error)
659		tok = TOK_ERROR;
660	else {
661		cpp_skip_whitespace(&val.str);
662		tok = ToToken(doEval && val.str[0] == '\0');
663	}
664
665	FStr_Done(&val);
666	*out_token = tok;
667	par->p = p;
668	return true;
669}
670
671/* Parse a function call expression, such as 'exists(${file})'. */
672static bool
673CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
674{
675	char *arg;
676	const char *p = par->p;
677	bool (*fn)(const char *);
678	const char *fn_name = p;
679
680	if (skip_string(&p, "defined"))
681		fn = FuncDefined;
682	else if (skip_string(&p, "make"))
683		fn = FuncMake;
684	else if (skip_string(&p, "exists"))
685		fn = FuncExists;
686	else if (skip_string(&p, "target"))
687		fn = FuncTarget;
688	else if (skip_string(&p, "commands"))
689		fn = FuncCommands;
690	else
691		return false;
692
693	cpp_skip_whitespace(&p);
694	if (*p != '(')
695		return false;
696
697	arg = ParseFuncArg(par, &p, doEval, fn_name);
698	*out_token = ToToken(doEval &&
699	    arg != NULL && arg[0] != '\0' && fn(arg));
700	free(arg);
701
702	par->p = p;
703	return true;
704}
705
706/*
707 * Parse a comparison that neither starts with '"' nor '$', such as the
708 * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without
709 * operator, which is a number, an expression or a string literal.
710 *
711 * TODO: Can this be merged into CondParser_Comparison?
712 */
713static Token
714CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
715{
716	Token t;
717	char *arg;
718	const char *p;
719
720	p = par->p;
721	if (ch_isdigit(p[0]) || p[0] == '-' || p[0] == '+')
722		return CondParser_Comparison(par, doEval);
723
724	/*
725	 * Most likely we have a bare word to apply the default function to.
726	 * However, ".if a == b" gets here when the "a" is unquoted and
727	 * doesn't start with a '$'. This surprises people.
728	 * If what follows the function argument is a '=' or '!' then the
729	 * syntax would be invalid if we did "defined(a)" - so instead treat
730	 * as an expression.
731	 */
732	/*
733	 * XXX: In edge cases, an expression may be evaluated twice,
734	 *  see cond-token-plain.mk, keyword 'twice'.
735	 */
736	arg = ParseWord(&p, doEval);
737	assert(arg[0] != '\0');
738
739	if (*p == '=' || *p == '!' || *p == '<' || *p == '>')
740		return CondParser_Comparison(par, doEval);
741	par->p = p;
742
743	/*
744	 * Evaluate the argument using the default function.
745	 * This path always treats .if as .ifdef. To get here, the character
746	 * after .if must have been taken literally, so the argument cannot
747	 * be empty - even if it contained an expression.
748	 */
749	t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare);
750	free(arg);
751	return t;
752}
753
754/* Return the next token or comparison result from the parser. */
755static Token
756CondParser_Token(CondParser *par, bool doEval)
757{
758	Token t;
759
760	t = par->curr;
761	if (t != TOK_NONE) {
762		par->curr = TOK_NONE;
763		return t;
764	}
765
766	cpp_skip_hspace(&par->p);
767
768	switch (par->p[0]) {
769
770	case '(':
771		par->p++;
772		return TOK_LPAREN;
773
774	case ')':
775		par->p++;
776		return TOK_RPAREN;
777
778	case '|':
779		par->p++;
780		if (par->p[0] == '|')
781			par->p++;
782		else if (opts.strict) {
783			Parse_Error(PARSE_FATAL, "Unknown operator '|'");
784			par->printedError = true;
785			return TOK_ERROR;
786		}
787		return TOK_OR;
788
789	case '&':
790		par->p++;
791		if (par->p[0] == '&')
792			par->p++;
793		else if (opts.strict) {
794			Parse_Error(PARSE_FATAL, "Unknown operator '&'");
795			par->printedError = true;
796			return TOK_ERROR;
797		}
798		return TOK_AND;
799
800	case '!':
801		par->p++;
802		return TOK_NOT;
803
804	case '#':		/* XXX: see unit-tests/cond-token-plain.mk */
805	case '\n':		/* XXX: why should this end the condition? */
806		/* Probably obsolete now, from 1993-03-21. */
807	case '\0':
808		return TOK_EOF;
809
810	case '"':
811	case '$':
812		return CondParser_Comparison(par, doEval);
813
814	default:
815		if (CondParser_FuncCallEmpty(par, doEval, &t))
816			return t;
817		if (CondParser_FuncCall(par, doEval, &t))
818			return t;
819		return CondParser_ComparisonOrLeaf(par, doEval);
820	}
821}
822
823/* Skip the next token if it equals t. */
824static bool
825CondParser_Skip(CondParser *par, Token t)
826{
827	Token actual;
828
829	actual = CondParser_Token(par, false);
830	if (actual == t)
831		return true;
832
833	assert(par->curr == TOK_NONE);
834	assert(actual != TOK_NONE);
835	par->curr = actual;
836	return false;
837}
838
839/*
840 * Term -> '(' Or ')'
841 * Term -> '!' Term
842 * Term -> Leaf Operator Leaf
843 * Term -> Leaf
844 */
845static CondResult
846CondParser_Term(CondParser *par, bool doEval)
847{
848	CondResult res;
849	Token t;
850	bool neg = false;
851
852	while ((t = CondParser_Token(par, doEval)) == TOK_NOT)
853		neg = !neg;
854
855	if (t == TOK_TRUE || t == TOK_FALSE)
856		return neg == (t == TOK_FALSE) ? CR_TRUE : CR_FALSE;
857
858	if (t == TOK_LPAREN) {
859		res = CondParser_Or(par, doEval);
860		if (res == CR_ERROR)
861			return CR_ERROR;
862		if (CondParser_Token(par, doEval) != TOK_RPAREN)
863			return CR_ERROR;
864		return neg == (res == CR_FALSE) ? CR_TRUE : CR_FALSE;
865	}
866
867	return CR_ERROR;
868}
869
870/*
871 * And -> Term ('&&' Term)*
872 */
873static CondResult
874CondParser_And(CondParser *par, bool doEval)
875{
876	CondResult res, rhs;
877
878	res = CR_TRUE;
879	do {
880		if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR)
881			return CR_ERROR;
882		if (rhs == CR_FALSE) {
883			res = CR_FALSE;
884			doEval = false;
885		}
886	} while (CondParser_Skip(par, TOK_AND));
887
888	return res;
889}
890
891/*
892 * Or -> And ('||' And)*
893 */
894static CondResult
895CondParser_Or(CondParser *par, bool doEval)
896{
897	CondResult res, rhs;
898
899	res = CR_FALSE;
900	do {
901		if ((rhs = CondParser_And(par, doEval)) == CR_ERROR)
902			return CR_ERROR;
903		if (rhs == CR_TRUE) {
904			res = CR_TRUE;
905			doEval = false;
906		}
907	} while (CondParser_Skip(par, TOK_OR));
908
909	return res;
910}
911
912/*
913 * Evaluate the condition, including any side effects from the
914 * expressions in the condition. The condition consists of &&, ||, !,
915 * function(arg), comparisons and parenthetical groupings thereof.
916 */
917static CondResult
918CondEvalExpression(const char *cond, bool plain,
919		   bool (*evalBare)(const char *), bool negate,
920		   bool eprint, bool leftUnquotedOK)
921{
922	CondParser par;
923	CondResult rval;
924
925	cpp_skip_hspace(&cond);
926
927	par.plain = plain;
928	par.evalBare = evalBare;
929	par.negateEvalBare = negate;
930	par.leftUnquotedOK = leftUnquotedOK;
931	par.p = cond;
932	par.curr = TOK_NONE;
933	par.printedError = false;
934
935	DEBUG1(COND, "CondParser_Eval: %s\n", par.p);
936	rval = CondParser_Or(&par, true);
937	if (par.curr != TOK_EOF)
938		rval = CR_ERROR;
939
940	if (rval == CR_ERROR && eprint && !par.printedError)
941		Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
942
943	return rval;
944}
945
946/*
947 * Evaluate a condition in a :? modifier, such as
948 * ${"${VAR}" == value:?yes:no}.
949 */
950CondResult
951Cond_EvalCondition(const char *cond)
952{
953	return CondEvalExpression(cond, true,
954	    FuncDefined, false, false, true);
955}
956
957static bool
958IsEndif(const char *p)
959{
960	return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' &&
961	       p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]);
962}
963
964static bool
965DetermineKindOfConditional(const char **pp, bool *out_plain,
966			   bool (**out_evalBare)(const char *),
967			   bool *out_negate)
968{
969	const char *p = *pp + 2;
970
971	*out_plain = false;
972	*out_evalBare = FuncDefined;
973	*out_negate = skip_string(&p, "n");
974
975	if (skip_string(&p, "def")) {		/* .ifdef and .ifndef */
976	} else if (skip_string(&p, "make"))	/* .ifmake and .ifnmake */
977		*out_evalBare = FuncMake;
978	else if (!*out_negate)			/* plain .if */
979		*out_plain = true;
980	else
981		goto unknown_directive;
982	if (ch_isalpha(*p))
983		goto unknown_directive;
984
985	*pp = p;
986	return true;
987
988unknown_directive:
989	return false;
990}
991
992/*
993 * Evaluate the conditional directive in the line, which is one of:
994 *
995 *	.if <cond>
996 *	.ifmake <cond>
997 *	.ifnmake <cond>
998 *	.ifdef <cond>
999 *	.ifndef <cond>
1000 *	.elif <cond>
1001 *	.elifmake <cond>
1002 *	.elifnmake <cond>
1003 *	.elifdef <cond>
1004 *	.elifndef <cond>
1005 *	.else
1006 *	.endif
1007 *
1008 * In these directives, <cond> consists of &&, ||, !, function(arg),
1009 * comparisons, expressions, bare words, numbers and strings, and
1010 * parenthetical groupings thereof.
1011 *
1012 * Results:
1013 *	CR_TRUE		to continue parsing the lines that follow the
1014 *			conditional (when <cond> evaluates to true)
1015 *	CR_FALSE	to skip the lines after the conditional
1016 *			(when <cond> evaluates to false, or when a previous
1017 *			branch was already taken)
1018 *	CR_ERROR	if the conditional was not valid, either because of
1019 *			a syntax error or because some variable was undefined
1020 *			or because the condition could not be evaluated
1021 */
1022CondResult
1023Cond_EvalLine(const char *line)
1024{
1025	typedef enum IfState {
1026
1027		/* None of the previous <cond> evaluated to true. */
1028		IFS_INITIAL	= 0,
1029
1030		/*
1031		 * The previous <cond> evaluated to true. The lines following
1032		 * this condition are interpreted.
1033		 */
1034		IFS_ACTIVE	= 1 << 0,
1035
1036		/* The previous directive was an '.else'. */
1037		IFS_SEEN_ELSE	= 1 << 1,
1038
1039		/* One of the previous <cond> evaluated to true. */
1040		IFS_WAS_ACTIVE	= 1 << 2
1041
1042	} IfState;
1043
1044	static enum IfState *cond_states = NULL;
1045	static unsigned int cond_states_cap = 128;
1046
1047	bool plain;
1048	bool (*evalBare)(const char *);
1049	bool negate;
1050	bool isElif;
1051	CondResult res;
1052	IfState state;
1053	const char *p = line;
1054
1055	if (cond_states == NULL) {
1056		cond_states = bmake_malloc(
1057		    cond_states_cap * sizeof *cond_states);
1058		cond_states[0] = IFS_ACTIVE;
1059	}
1060
1061	p++;			/* skip the leading '.' */
1062	cpp_skip_hspace(&p);
1063
1064	if (IsEndif(p)) {
1065		if (p[5] != '\0') {
1066			Parse_Error(PARSE_FATAL,
1067			    "The .endif directive does not take arguments");
1068		}
1069
1070		if (cond_depth == CurFile_CondMinDepth()) {
1071			Parse_Error(PARSE_FATAL, "if-less endif");
1072			return CR_TRUE;
1073		}
1074
1075		/* Return state for previous conditional */
1076		cond_depth--;
1077		Parse_GuardEndif();
1078		return cond_states[cond_depth] & IFS_ACTIVE
1079		    ? CR_TRUE : CR_FALSE;
1080	}
1081
1082	/* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
1083	if (p[0] == 'e') {
1084		if (p[1] != 'l')
1085			return CR_ERROR;
1086
1087		/* Quite likely this is 'else' or 'elif' */
1088		p += 2;
1089		if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) {
1090			if (p[2] != '\0')
1091				Parse_Error(PARSE_FATAL,
1092				    "The .else directive "
1093				    "does not take arguments");
1094
1095			if (cond_depth == CurFile_CondMinDepth()) {
1096				Parse_Error(PARSE_FATAL, "if-less else");
1097				return CR_TRUE;
1098			}
1099			Parse_GuardElse();
1100
1101			state = cond_states[cond_depth];
1102			if (state == IFS_INITIAL) {
1103				state = IFS_ACTIVE | IFS_SEEN_ELSE;
1104			} else {
1105				if (state & IFS_SEEN_ELSE)
1106					Parse_Error(PARSE_WARNING,
1107					    "extra else");
1108				state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
1109			}
1110			cond_states[cond_depth] = state;
1111
1112			return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE;
1113		}
1114		/* Assume for now it is an elif */
1115		isElif = true;
1116	} else
1117		isElif = false;
1118
1119	if (p[0] != 'i' || p[1] != 'f')
1120		return CR_ERROR;
1121
1122	if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
1123		return CR_ERROR;
1124
1125	if (isElif) {
1126		if (cond_depth == CurFile_CondMinDepth()) {
1127			Parse_Error(PARSE_FATAL, "if-less elif");
1128			return CR_TRUE;
1129		}
1130		Parse_GuardElse();
1131		state = cond_states[cond_depth];
1132		if (state & IFS_SEEN_ELSE) {
1133			Parse_Error(PARSE_WARNING, "extra elif");
1134			cond_states[cond_depth] =
1135			    IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
1136			return CR_FALSE;
1137		}
1138		if (state != IFS_INITIAL) {
1139			cond_states[cond_depth] = IFS_WAS_ACTIVE;
1140			return CR_FALSE;
1141		}
1142	} else {
1143		/* Normal .if */
1144		if (cond_depth + 1 >= cond_states_cap) {
1145			/*
1146			 * This is rare, but not impossible.
1147			 * In meta mode, dirdeps.mk (only runs at level 0)
1148			 * can need more than the default.
1149			 */
1150			cond_states_cap += 32;
1151			cond_states = bmake_realloc(cond_states,
1152			    cond_states_cap * sizeof *cond_states);
1153		}
1154		state = cond_states[cond_depth];
1155		cond_depth++;
1156		if (!(state & IFS_ACTIVE)) {
1157			cond_states[cond_depth] = IFS_WAS_ACTIVE;
1158			return CR_FALSE;
1159		}
1160	}
1161
1162	res = CondEvalExpression(p, plain, evalBare, negate, true, false);
1163	if (res == CR_ERROR) {
1164		/* Syntax error, error message already output. */
1165		/* Skip everything to the matching '.endif'. */
1166		/* An extra '.else' is not detected in this case. */
1167		cond_states[cond_depth] = IFS_WAS_ACTIVE;
1168		return CR_FALSE;
1169	}
1170
1171	cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL;
1172	return res;
1173}
1174
1175static bool
1176ParseVarnameGuard(const char **pp, const char **varname)
1177{
1178	const char *p = *pp;
1179
1180	if (ch_isalpha(*p) || *p == '_') {
1181		while (ch_isalnum(*p) || *p == '_')
1182			p++;
1183		*varname = *pp;
1184		*pp = p;
1185		return true;
1186	}
1187	return false;
1188}
1189
1190/* Extracts the multiple-inclusion guard from a conditional, if any. */
1191Guard *
1192Cond_ExtractGuard(const char *line)
1193{
1194	const char *p, *varname;
1195	Substring dir;
1196	Guard *guard;
1197
1198	p = line + 1;		/* skip the '.' */
1199	cpp_skip_hspace(&p);
1200
1201	dir.start = p;
1202	while (ch_isalpha(*p))
1203		p++;
1204	dir.end = p;
1205	cpp_skip_hspace(&p);
1206
1207	if (Substring_Equals(dir, "if")) {
1208		if (skip_string(&p, "!defined(")) {
1209			if (ParseVarnameGuard(&p, &varname)
1210			    && strcmp(p, ")") == 0)
1211				goto found_variable;
1212		} else if (skip_string(&p, "!target(")) {
1213			const char *arg_p = p;
1214			free(ParseWord(&p, false));
1215			if (strcmp(p, ")") == 0) {
1216				guard = bmake_malloc(sizeof(*guard));
1217				guard->kind = GK_TARGET;
1218				guard->name = ParseWord(&arg_p, true);
1219				return guard;
1220			}
1221		}
1222	} else if (Substring_Equals(dir, "ifndef")) {
1223		if (ParseVarnameGuard(&p, &varname) && *p == '\0')
1224			goto found_variable;
1225	}
1226	return NULL;
1227
1228found_variable:
1229	guard = bmake_malloc(sizeof(*guard));
1230	guard->kind = GK_VARIABLE;
1231	guard->name = bmake_strsedup(varname, p);
1232	return guard;
1233}
1234
1235void
1236Cond_EndFile(void)
1237{
1238	unsigned int open_conds = cond_depth - CurFile_CondMinDepth();
1239
1240	if (open_conds != 0) {
1241		Parse_Error(PARSE_FATAL, "%u open conditional%s",
1242		    open_conds, open_conds == 1 ? "" : "s");
1243		cond_depth = CurFile_CondMinDepth();
1244	}
1245}
1246