xref: /freebsd/contrib/bmake/cond.c (revision e2eeea75eb8b6dd50c1298067a0655880d186734)
1*e2eeea75SSimon J. Gerraty /*	$NetBSD: cond.c,v 1.214 2020/11/13 09:01:59 rillig Exp $	*/
23955d011SMarcel Moolenaar 
33955d011SMarcel Moolenaar /*
43955d011SMarcel Moolenaar  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
53955d011SMarcel Moolenaar  * All rights reserved.
63955d011SMarcel Moolenaar  *
73955d011SMarcel Moolenaar  * This code is derived from software contributed to Berkeley by
83955d011SMarcel Moolenaar  * Adam de Boor.
93955d011SMarcel Moolenaar  *
103955d011SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
113955d011SMarcel Moolenaar  * modification, are permitted provided that the following conditions
123955d011SMarcel Moolenaar  * are met:
133955d011SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
143955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
153955d011SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
163955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
173955d011SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
183955d011SMarcel Moolenaar  * 3. Neither the name of the University nor the names of its contributors
193955d011SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
203955d011SMarcel Moolenaar  *    without specific prior written permission.
213955d011SMarcel Moolenaar  *
223955d011SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
233955d011SMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
243955d011SMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
253955d011SMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
263955d011SMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
273955d011SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
283955d011SMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
293955d011SMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
303955d011SMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
313955d011SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323955d011SMarcel Moolenaar  * SUCH DAMAGE.
333955d011SMarcel Moolenaar  */
343955d011SMarcel Moolenaar 
353955d011SMarcel Moolenaar /*
363955d011SMarcel Moolenaar  * Copyright (c) 1988, 1989 by Adam de Boor
373955d011SMarcel Moolenaar  * Copyright (c) 1989 by Berkeley Softworks
383955d011SMarcel Moolenaar  * All rights reserved.
393955d011SMarcel Moolenaar  *
403955d011SMarcel Moolenaar  * This code is derived from software contributed to Berkeley by
413955d011SMarcel Moolenaar  * Adam de Boor.
423955d011SMarcel Moolenaar  *
433955d011SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
443955d011SMarcel Moolenaar  * modification, are permitted provided that the following conditions
453955d011SMarcel Moolenaar  * are met:
463955d011SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
473955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
483955d011SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
493955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
503955d011SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
513955d011SMarcel Moolenaar  * 3. All advertising materials mentioning features or use of this software
523955d011SMarcel Moolenaar  *    must display the following acknowledgement:
533955d011SMarcel Moolenaar  *	This product includes software developed by the University of
543955d011SMarcel Moolenaar  *	California, Berkeley and its contributors.
553955d011SMarcel Moolenaar  * 4. Neither the name of the University nor the names of its contributors
563955d011SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
573955d011SMarcel Moolenaar  *    without specific prior written permission.
583955d011SMarcel Moolenaar  *
593955d011SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
603955d011SMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
613955d011SMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
623955d011SMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
633955d011SMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
643955d011SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
653955d011SMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
663955d011SMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
673955d011SMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
683955d011SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
693955d011SMarcel Moolenaar  * SUCH DAMAGE.
703955d011SMarcel Moolenaar  */
713955d011SMarcel Moolenaar 
72956e45f6SSimon J. Gerraty /* Handling of conditionals in a makefile.
733955d011SMarcel Moolenaar  *
743955d011SMarcel Moolenaar  * Interface:
75*e2eeea75SSimon J. Gerraty  *	Cond_EvalLine   Evaluate the conditional directive, such as
76*e2eeea75SSimon J. Gerraty  *			'.if <cond>', '.elifnmake <cond>', '.else', '.endif'.
773955d011SMarcel Moolenaar  *
78956e45f6SSimon J. Gerraty  *	Cond_EvalCondition
79956e45f6SSimon J. Gerraty  *			Evaluate the conditional, which is either the argument
80956e45f6SSimon J. Gerraty  *			of one of the .if directives or the condition in a
81956e45f6SSimon J. Gerraty  *			':?then:else' variable modifier.
82956e45f6SSimon J. Gerraty  *
83956e45f6SSimon J. Gerraty  *	Cond_save_depth
84956e45f6SSimon J. Gerraty  *	Cond_restore_depth
85956e45f6SSimon J. Gerraty  *			Save and restore the nesting of the conditions, at
86956e45f6SSimon J. Gerraty  *			the start and end of including another makefile, to
87956e45f6SSimon J. Gerraty  *			ensure that in each makefile the conditional
88956e45f6SSimon J. Gerraty  *			directives are well-balanced.
893955d011SMarcel Moolenaar  */
903955d011SMarcel Moolenaar 
912c3632d1SSimon J. Gerraty #include <errno.h>
923955d011SMarcel Moolenaar 
933955d011SMarcel Moolenaar #include "make.h"
943955d011SMarcel Moolenaar #include "dir.h"
953955d011SMarcel Moolenaar 
96956e45f6SSimon J. Gerraty /*	"@(#)cond.c	8.2 (Berkeley) 1/2/94"	*/
97*e2eeea75SSimon J. Gerraty MAKE_RCSID("$NetBSD: cond.c,v 1.214 2020/11/13 09:01:59 rillig Exp $");
98956e45f6SSimon J. Gerraty 
993955d011SMarcel Moolenaar /*
1003955d011SMarcel Moolenaar  * The parsing of conditional expressions is based on this grammar:
1013955d011SMarcel Moolenaar  *	E -> F || E
1023955d011SMarcel Moolenaar  *	E -> F
1033955d011SMarcel Moolenaar  *	F -> T && F
1043955d011SMarcel Moolenaar  *	F -> T
1053955d011SMarcel Moolenaar  *	T -> defined(variable)
1063955d011SMarcel Moolenaar  *	T -> make(target)
1073955d011SMarcel Moolenaar  *	T -> exists(file)
1083955d011SMarcel Moolenaar  *	T -> empty(varspec)
1093955d011SMarcel Moolenaar  *	T -> target(name)
1103955d011SMarcel Moolenaar  *	T -> commands(name)
1113955d011SMarcel Moolenaar  *	T -> symbol
1123955d011SMarcel Moolenaar  *	T -> $(varspec) op value
1133955d011SMarcel Moolenaar  *	T -> $(varspec) == "string"
1143955d011SMarcel Moolenaar  *	T -> $(varspec) != "string"
1153955d011SMarcel Moolenaar  *	T -> "string"
1163955d011SMarcel Moolenaar  *	T -> ( E )
1173955d011SMarcel Moolenaar  *	T -> ! T
1183955d011SMarcel Moolenaar  *	op -> == | != | > | < | >= | <=
1193955d011SMarcel Moolenaar  *
1202c3632d1SSimon J. Gerraty  * 'symbol' is some other symbol to which the default function is applied.
1213955d011SMarcel Moolenaar  *
122956e45f6SSimon J. Gerraty  * The tokens are scanned by CondToken, which returns:
123956e45f6SSimon J. Gerraty  *	TOK_AND		for '&' or '&&'
124956e45f6SSimon J. Gerraty  *	TOK_OR		for '|' or '||'
125956e45f6SSimon J. Gerraty  *	TOK_NOT		for '!'
126956e45f6SSimon J. Gerraty  *	TOK_LPAREN	for '('
127956e45f6SSimon J. Gerraty  *	TOK_RPAREN	for ')'
128956e45f6SSimon J. Gerraty  * Other terminal symbols are evaluated using either the default function or
129956e45f6SSimon J. Gerraty  * the function given in the terminal, they return either TOK_TRUE or
130956e45f6SSimon J. Gerraty  * TOK_FALSE.
1313955d011SMarcel Moolenaar  *
1323955d011SMarcel Moolenaar  * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons.
1333955d011SMarcel Moolenaar  *
134956e45f6SSimon J. Gerraty  * All non-terminal functions (CondParser_Expr, CondParser_Factor and
135956e45f6SSimon J. Gerraty  * CondParser_Term) return either TOK_FALSE, TOK_TRUE, or TOK_ERROR on error.
1363955d011SMarcel Moolenaar  */
137956e45f6SSimon J. Gerraty typedef enum Token {
1383955d011SMarcel Moolenaar     TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT,
1393955d011SMarcel Moolenaar     TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
1403955d011SMarcel Moolenaar } Token;
1413955d011SMarcel Moolenaar 
142956e45f6SSimon J. Gerraty typedef struct CondParser {
143956e45f6SSimon J. Gerraty     const struct If *if_info;	/* Info for current statement */
144956e45f6SSimon J. Gerraty     const char *p;		/* The remaining condition to parse */
145956e45f6SSimon J. Gerraty     Token curr;			/* Single push-back token used in parsing */
1463955d011SMarcel Moolenaar 
147956e45f6SSimon J. Gerraty     /* Whether an error message has already been printed for this condition.
148956e45f6SSimon J. Gerraty      * The first available error message is usually the most specific one,
149956e45f6SSimon J. Gerraty      * therefore it makes sense to suppress the standard "Malformed
150956e45f6SSimon J. Gerraty      * conditional" message. */
151956e45f6SSimon J. Gerraty     Boolean printedError;
152956e45f6SSimon J. Gerraty } CondParser;
153956e45f6SSimon J. Gerraty 
154956e45f6SSimon J. Gerraty static Token CondParser_Expr(CondParser *par, Boolean);
1553955d011SMarcel Moolenaar 
1563955d011SMarcel Moolenaar static unsigned int cond_depth = 0;	/* current .if nesting level */
1573955d011SMarcel Moolenaar static unsigned int cond_min_depth = 0;	/* depth at makefile open */
1583955d011SMarcel Moolenaar 
15928a6bc81SSimon J. Gerraty /*
16028a6bc81SSimon J. Gerraty  * Indicate when we should be strict about lhs of comparisons.
161956e45f6SSimon J. Gerraty  * In strict mode, the lhs must be a variable expression or a string literal
162956e45f6SSimon J. Gerraty  * in quotes. In non-strict mode it may also be an unquoted string literal.
163956e45f6SSimon J. Gerraty  *
164956e45f6SSimon J. Gerraty  * TRUE when CondEvalExpression is called from Cond_EvalLine (.if etc)
165956e45f6SSimon J. Gerraty  * FALSE when CondEvalExpression is called from ApplyModifier_IfElse
166*e2eeea75SSimon J. Gerraty  * since lhs is already expanded, and at that point we cannot tell if
16728a6bc81SSimon J. Gerraty  * it was a variable reference or not.
16828a6bc81SSimon J. Gerraty  */
16928a6bc81SSimon J. Gerraty static Boolean lhsStrict;
17028a6bc81SSimon J. Gerraty 
1713955d011SMarcel Moolenaar static int
172956e45f6SSimon J. Gerraty is_token(const char *str, const char *tok, size_t len)
1733955d011SMarcel Moolenaar {
174956e45f6SSimon J. Gerraty     return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]);
1753955d011SMarcel Moolenaar }
1763955d011SMarcel Moolenaar 
177*e2eeea75SSimon J. Gerraty static Token
178*e2eeea75SSimon J. Gerraty ToToken(Boolean cond)
179*e2eeea75SSimon J. Gerraty {
180*e2eeea75SSimon J. Gerraty     return cond ? TOK_TRUE : TOK_FALSE;
181*e2eeea75SSimon J. Gerraty }
182*e2eeea75SSimon J. Gerraty 
183956e45f6SSimon J. Gerraty /* Push back the most recent token read. We only need one level of this. */
1843955d011SMarcel Moolenaar static void
185956e45f6SSimon J. Gerraty CondParser_PushBack(CondParser *par, Token t)
1863955d011SMarcel Moolenaar {
187956e45f6SSimon J. Gerraty     assert(par->curr == TOK_NONE);
188956e45f6SSimon J. Gerraty     assert(t != TOK_NONE);
189956e45f6SSimon J. Gerraty 
190956e45f6SSimon J. Gerraty     par->curr = t;
1913955d011SMarcel Moolenaar }
1922c3632d1SSimon J. Gerraty 
193956e45f6SSimon J. Gerraty static void
194956e45f6SSimon J. Gerraty CondParser_SkipWhitespace(CondParser *par)
1953955d011SMarcel Moolenaar {
196956e45f6SSimon J. Gerraty     cpp_skip_whitespace(&par->p);
197956e45f6SSimon J. Gerraty }
198956e45f6SSimon J. Gerraty 
199956e45f6SSimon J. Gerraty /* Parse the argument of a built-in function.
200956e45f6SSimon J. Gerraty  *
201956e45f6SSimon J. Gerraty  * Arguments:
202956e45f6SSimon J. Gerraty  *	*pp initially points at the '(',
203956e45f6SSimon J. Gerraty  *	upon successful return it points right after the ')'.
204956e45f6SSimon J. Gerraty  *
205956e45f6SSimon J. Gerraty  *	*out_arg receives the argument as string.
206956e45f6SSimon J. Gerraty  *
207956e45f6SSimon J. Gerraty  *	func says whether the argument belongs to an actual function, or
208956e45f6SSimon J. Gerraty  *	whether the parsed argument is passed to the default function.
209956e45f6SSimon J. Gerraty  *
210*e2eeea75SSimon J. Gerraty  * Return the length of the argument, or 0 on error. */
211956e45f6SSimon J. Gerraty static size_t
212956e45f6SSimon J. Gerraty ParseFuncArg(const char **pp, Boolean doEval, const char *func,
213956e45f6SSimon J. Gerraty 	     char **out_arg) {
214956e45f6SSimon J. Gerraty     const char *p = *pp;
215956e45f6SSimon J. Gerraty     Buffer argBuf;
2163955d011SMarcel Moolenaar     int paren_depth;
2172c3632d1SSimon J. Gerraty     size_t argLen;
2183955d011SMarcel Moolenaar 
2193955d011SMarcel Moolenaar     if (func != NULL)
220956e45f6SSimon J. Gerraty 	p++;			/* Skip opening '(' - verified by caller */
2213955d011SMarcel Moolenaar 
222956e45f6SSimon J. Gerraty     if (*p == '\0') {
223*e2eeea75SSimon J. Gerraty 	*out_arg = NULL;	/* Missing closing parenthesis: */
224*e2eeea75SSimon J. Gerraty 	return 0;		/* .if defined( */
2253955d011SMarcel Moolenaar     }
2263955d011SMarcel Moolenaar 
227*e2eeea75SSimon J. Gerraty     cpp_skip_hspace(&p);
2283955d011SMarcel Moolenaar 
229*e2eeea75SSimon J. Gerraty     Buf_InitSize(&argBuf, 16);
2303955d011SMarcel Moolenaar 
2313955d011SMarcel Moolenaar     paren_depth = 0;
2323955d011SMarcel Moolenaar     for (;;) {
233956e45f6SSimon J. Gerraty 	char ch = *p;
234*e2eeea75SSimon J. Gerraty 	if (ch == '\0' || ch == ' ' || ch == '\t')
2353955d011SMarcel Moolenaar 	    break;
2363955d011SMarcel Moolenaar 	if ((ch == '&' || ch == '|') && paren_depth == 0)
2373955d011SMarcel Moolenaar 	    break;
238956e45f6SSimon J. Gerraty 	if (*p == '$') {
2393955d011SMarcel Moolenaar 	    /*
2403955d011SMarcel Moolenaar 	     * Parse the variable spec and install it as part of the argument
2413955d011SMarcel Moolenaar 	     * if it's valid. We tell Var_Parse to complain on an undefined
242956e45f6SSimon J. Gerraty 	     * variable, so we don't need to do it. Nor do we return an error,
2433955d011SMarcel Moolenaar 	     * though perhaps we should...
2443955d011SMarcel Moolenaar 	     */
245956e45f6SSimon J. Gerraty 	    void *nestedVal_freeIt;
246*e2eeea75SSimon J. Gerraty 	    VarEvalFlags eflags = doEval ? VARE_WANTRES | VARE_UNDEFERR
247*e2eeea75SSimon J. Gerraty 					 : VARE_NONE;
248956e45f6SSimon J. Gerraty 	    const char *nestedVal;
249956e45f6SSimon J. Gerraty 	    (void)Var_Parse(&p, VAR_CMDLINE, eflags, &nestedVal,
250956e45f6SSimon J. Gerraty 			    &nestedVal_freeIt);
251956e45f6SSimon J. Gerraty 	    /* TODO: handle errors */
252956e45f6SSimon J. Gerraty 	    Buf_AddStr(&argBuf, nestedVal);
253956e45f6SSimon J. Gerraty 	    free(nestedVal_freeIt);
2543955d011SMarcel Moolenaar 	    continue;
2553955d011SMarcel Moolenaar 	}
2563955d011SMarcel Moolenaar 	if (ch == '(')
2573955d011SMarcel Moolenaar 	    paren_depth++;
2582c3632d1SSimon J. Gerraty 	else if (ch == ')' && --paren_depth < 0)
2593955d011SMarcel Moolenaar 	    break;
260956e45f6SSimon J. Gerraty 	Buf_AddByte(&argBuf, *p);
261956e45f6SSimon J. Gerraty 	p++;
2623955d011SMarcel Moolenaar     }
2633955d011SMarcel Moolenaar 
264956e45f6SSimon J. Gerraty     *out_arg = Buf_GetAll(&argBuf, &argLen);
265956e45f6SSimon J. Gerraty     Buf_Destroy(&argBuf, FALSE);
2663955d011SMarcel Moolenaar 
267*e2eeea75SSimon J. Gerraty     cpp_skip_hspace(&p);
2683955d011SMarcel Moolenaar 
269956e45f6SSimon J. Gerraty     if (func != NULL && *p++ != ')') {
2703955d011SMarcel Moolenaar 	Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
2713955d011SMarcel Moolenaar 		    func);
272956e45f6SSimon J. Gerraty 	/* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
2733841c287SSimon J. Gerraty 	return 0;
2743955d011SMarcel Moolenaar     }
2753955d011SMarcel Moolenaar 
276956e45f6SSimon J. Gerraty     *pp = p;
2773841c287SSimon J. Gerraty     return argLen;
2783955d011SMarcel Moolenaar }
2792c3632d1SSimon J. Gerraty 
2802c3632d1SSimon J. Gerraty /* Test whether the given variable is defined. */
2813955d011SMarcel Moolenaar static Boolean
282956e45f6SSimon J. Gerraty FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
2833955d011SMarcel Moolenaar {
284956e45f6SSimon J. Gerraty     void *freeIt;
285956e45f6SSimon J. Gerraty     Boolean result = Var_Value(arg, VAR_CMDLINE, &freeIt) != NULL;
2862c3632d1SSimon J. Gerraty     bmake_free(freeIt);
2873841c287SSimon J. Gerraty     return result;
2883955d011SMarcel Moolenaar }
2892c3632d1SSimon J. Gerraty 
2902c3632d1SSimon J. Gerraty /* See if the given target is being made. */
2913955d011SMarcel Moolenaar static Boolean
292956e45f6SSimon J. Gerraty FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
2933955d011SMarcel Moolenaar {
294956e45f6SSimon J. Gerraty     StringListNode *ln;
295956e45f6SSimon J. Gerraty 
296956e45f6SSimon J. Gerraty     for (ln = opts.create->first; ln != NULL; ln = ln->next)
297956e45f6SSimon J. Gerraty 	if (Str_Match(ln->datum, arg))
298956e45f6SSimon J. Gerraty 	    return TRUE;
299956e45f6SSimon J. Gerraty     return FALSE;
3003955d011SMarcel Moolenaar }
3012c3632d1SSimon J. Gerraty 
3022c3632d1SSimon J. Gerraty /* See if the given file exists. */
3033955d011SMarcel Moolenaar static Boolean
304956e45f6SSimon J. Gerraty FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
3053955d011SMarcel Moolenaar {
3063955d011SMarcel Moolenaar     Boolean result;
3073955d011SMarcel Moolenaar     char *path;
3083955d011SMarcel Moolenaar 
3093955d011SMarcel Moolenaar     path = Dir_FindFile(arg, dirSearchPath);
310*e2eeea75SSimon J. Gerraty     DEBUG2(COND, "exists(%s) result is \"%s\"\n",
311*e2eeea75SSimon J. Gerraty 	   arg, path != NULL ? path : "");
312*e2eeea75SSimon J. Gerraty     result = path != NULL;
3133955d011SMarcel Moolenaar     free(path);
3143841c287SSimon J. Gerraty     return result;
3153955d011SMarcel Moolenaar }
3162c3632d1SSimon J. Gerraty 
3172c3632d1SSimon J. Gerraty /* See if the given node exists and is an actual target. */
3183955d011SMarcel Moolenaar static Boolean
319956e45f6SSimon J. Gerraty FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
3203955d011SMarcel Moolenaar {
321956e45f6SSimon J. Gerraty     GNode *gn = Targ_FindNode(arg);
322956e45f6SSimon J. Gerraty     return gn != NULL && GNode_IsTarget(gn);
3233955d011SMarcel Moolenaar }
3243955d011SMarcel Moolenaar 
3252c3632d1SSimon J. Gerraty /* See if the given node exists and is an actual target with commands
3262c3632d1SSimon J. Gerraty  * associated with it. */
3273955d011SMarcel Moolenaar static Boolean
328956e45f6SSimon J. Gerraty FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
3293955d011SMarcel Moolenaar {
330956e45f6SSimon J. Gerraty     GNode *gn = Targ_FindNode(arg);
331956e45f6SSimon J. Gerraty     return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(gn->commands);
3323955d011SMarcel Moolenaar }
3332c3632d1SSimon J. Gerraty 
334*e2eeea75SSimon J. Gerraty /*
3353955d011SMarcel Moolenaar  * Convert the given number into a double.
3363955d011SMarcel Moolenaar  * We try a base 10 or 16 integer conversion first, if that fails
3373955d011SMarcel Moolenaar  * then we try a floating point conversion instead.
3383955d011SMarcel Moolenaar  *
3393955d011SMarcel Moolenaar  * Results:
3402c3632d1SSimon J. Gerraty  *	Returns TRUE if the conversion succeeded.
341*e2eeea75SSimon J. Gerraty  *	Sets 'out_value' to the converted number.
3423955d011SMarcel Moolenaar  */
3433955d011SMarcel Moolenaar static Boolean
344*e2eeea75SSimon J. Gerraty TryParseNumber(const char *str, double *out_value)
3453955d011SMarcel Moolenaar {
346*e2eeea75SSimon J. Gerraty     char *end;
347*e2eeea75SSimon J. Gerraty     unsigned long ul_val;
348*e2eeea75SSimon J. Gerraty     double dbl_val;
3493955d011SMarcel Moolenaar 
3503955d011SMarcel Moolenaar     errno = 0;
351*e2eeea75SSimon J. Gerraty     if (str[0] == '\0') {	/* XXX: why is an empty string a number? */
352*e2eeea75SSimon J. Gerraty 	*out_value = 0.0;
353ac3446e9SSimon J. Gerraty 	return TRUE;
354ac3446e9SSimon J. Gerraty     }
355*e2eeea75SSimon J. Gerraty 
356*e2eeea75SSimon J. Gerraty     ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10);
357*e2eeea75SSimon J. Gerraty     if (*end == '\0' && errno != ERANGE) {
358*e2eeea75SSimon J. Gerraty 	*out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val;
359*e2eeea75SSimon J. Gerraty 	return TRUE;
3603955d011SMarcel Moolenaar     }
3613955d011SMarcel Moolenaar 
362*e2eeea75SSimon J. Gerraty     if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E')
363*e2eeea75SSimon J. Gerraty 	return FALSE;		/* skip the expensive strtod call */
364*e2eeea75SSimon J. Gerraty     dbl_val = strtod(str, &end);
365*e2eeea75SSimon J. Gerraty     if (*end != '\0')
366*e2eeea75SSimon J. Gerraty 	return FALSE;
367*e2eeea75SSimon J. Gerraty 
368*e2eeea75SSimon J. Gerraty     *out_value = dbl_val;
3693955d011SMarcel Moolenaar     return TRUE;
3703955d011SMarcel Moolenaar }
3713955d011SMarcel Moolenaar 
372956e45f6SSimon J. Gerraty static Boolean
373956e45f6SSimon J. Gerraty is_separator(char ch)
374956e45f6SSimon J. Gerraty {
375956e45f6SSimon J. Gerraty     return ch == '\0' || ch_isspace(ch) || strchr("!=><)", ch) != NULL;
376956e45f6SSimon J. Gerraty }
377956e45f6SSimon J. Gerraty 
3783955d011SMarcel Moolenaar /*-
379956e45f6SSimon J. Gerraty  * Parse a string from a variable reference or an optionally quoted
380956e45f6SSimon J. Gerraty  * string.  This is called for the lhs and rhs of string comparisons.
3813955d011SMarcel Moolenaar  *
3823955d011SMarcel Moolenaar  * Results:
3832c3632d1SSimon J. Gerraty  *	Returns the string, absent any quotes, or NULL on error.
384*e2eeea75SSimon J. Gerraty  *	Sets out_quoted if the string was quoted.
385*e2eeea75SSimon J. Gerraty  *	Sets out_freeIt.
3863955d011SMarcel Moolenaar  */
387956e45f6SSimon J. Gerraty /* coverity:[+alloc : arg-*4] */
3882c3632d1SSimon J. Gerraty static const char *
389956e45f6SSimon J. Gerraty CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
390*e2eeea75SSimon J. Gerraty 		  Boolean *out_quoted, void **out_freeIt)
3913955d011SMarcel Moolenaar {
3923955d011SMarcel Moolenaar     Buffer buf;
3932c3632d1SSimon J. Gerraty     const char *str;
394956e45f6SSimon J. Gerraty     Boolean atStart;
395956e45f6SSimon J. Gerraty     const char *nested_p;
396*e2eeea75SSimon J. Gerraty     Boolean quoted;
3972c3632d1SSimon J. Gerraty     const char *start;
3982c3632d1SSimon J. Gerraty     VarEvalFlags eflags;
399956e45f6SSimon J. Gerraty     VarParseResult parseResult;
4003955d011SMarcel Moolenaar 
401*e2eeea75SSimon J. Gerraty     Buf_Init(&buf);
4023955d011SMarcel Moolenaar     str = NULL;
403*e2eeea75SSimon J. Gerraty     *out_freeIt = NULL;
404*e2eeea75SSimon J. Gerraty     *out_quoted = quoted = par->p[0] == '"';
405956e45f6SSimon J. Gerraty     start = par->p;
406*e2eeea75SSimon J. Gerraty     if (quoted)
407956e45f6SSimon J. Gerraty 	par->p++;
408*e2eeea75SSimon J. Gerraty     while (par->p[0] != '\0' && str == NULL) {
409956e45f6SSimon J. Gerraty 	switch (par->p[0]) {
4103955d011SMarcel Moolenaar 	case '\\':
411956e45f6SSimon J. Gerraty 	    par->p++;
412956e45f6SSimon J. Gerraty 	    if (par->p[0] != '\0') {
413956e45f6SSimon J. Gerraty 		Buf_AddByte(&buf, par->p[0]);
414956e45f6SSimon J. Gerraty 		par->p++;
4153955d011SMarcel Moolenaar 	    }
416956e45f6SSimon J. Gerraty 	    continue;
4173955d011SMarcel Moolenaar 	case '"':
418*e2eeea75SSimon J. Gerraty 	    if (quoted) {
419*e2eeea75SSimon J. Gerraty 		par->p++;	/* skip the closing quote */
4203955d011SMarcel Moolenaar 		goto got_str;
421956e45f6SSimon J. Gerraty 	    }
422956e45f6SSimon J. Gerraty 	    Buf_AddByte(&buf, par->p[0]); /* likely? */
423956e45f6SSimon J. Gerraty 	    par->p++;
424956e45f6SSimon J. Gerraty 	    continue;
425*e2eeea75SSimon J. Gerraty 	case ')':		/* see is_separator */
4263955d011SMarcel Moolenaar 	case '!':
4273955d011SMarcel Moolenaar 	case '=':
4283955d011SMarcel Moolenaar 	case '>':
4293955d011SMarcel Moolenaar 	case '<':
4303955d011SMarcel Moolenaar 	case ' ':
4313955d011SMarcel Moolenaar 	case '\t':
432*e2eeea75SSimon J. Gerraty 	    if (!quoted)
4333955d011SMarcel Moolenaar 		goto got_str;
434956e45f6SSimon J. Gerraty 	    Buf_AddByte(&buf, par->p[0]);
435956e45f6SSimon J. Gerraty 	    par->p++;
436956e45f6SSimon J. Gerraty 	    continue;
4373955d011SMarcel Moolenaar 	case '$':
438956e45f6SSimon J. Gerraty 	    /* if we are in quotes, an undefined variable is ok */
439*e2eeea75SSimon J. Gerraty 	    eflags = doEval && !quoted ? VARE_WANTRES | VARE_UNDEFERR :
440*e2eeea75SSimon J. Gerraty 		     doEval ? VARE_WANTRES :
441*e2eeea75SSimon J. Gerraty 		     VARE_NONE;
442*e2eeea75SSimon J. Gerraty 
443956e45f6SSimon J. Gerraty 	    nested_p = par->p;
444956e45f6SSimon J. Gerraty 	    atStart = nested_p == start;
445956e45f6SSimon J. Gerraty 	    parseResult = Var_Parse(&nested_p, VAR_CMDLINE, eflags, &str,
446*e2eeea75SSimon J. Gerraty 				    out_freeIt);
447956e45f6SSimon J. Gerraty 	    /* TODO: handle errors */
4483955d011SMarcel Moolenaar 	    if (str == var_Error) {
449956e45f6SSimon J. Gerraty 		if (parseResult & VPR_ANY_MSG)
450956e45f6SSimon J. Gerraty 		    par->printedError = TRUE;
451*e2eeea75SSimon J. Gerraty 		if (*out_freeIt != NULL) {
452*e2eeea75SSimon J. Gerraty 		    /* XXX: Can there be any situation in which a returned
453*e2eeea75SSimon J. Gerraty 		     * var_Error requires freeIt? */
454*e2eeea75SSimon J. Gerraty 		    free(*out_freeIt);
455*e2eeea75SSimon J. Gerraty 		    *out_freeIt = NULL;
4563955d011SMarcel Moolenaar 		}
4573955d011SMarcel Moolenaar 		/*
4583955d011SMarcel Moolenaar 		 * Even if !doEval, we still report syntax errors, which
4593955d011SMarcel Moolenaar 		 * is what getting var_Error back with !doEval means.
4603955d011SMarcel Moolenaar 		 */
4613955d011SMarcel Moolenaar 		str = NULL;
4623955d011SMarcel Moolenaar 		goto cleanup;
4633955d011SMarcel Moolenaar 	    }
464956e45f6SSimon J. Gerraty 	    par->p = nested_p;
465956e45f6SSimon J. Gerraty 
4663955d011SMarcel Moolenaar 	    /*
467956e45f6SSimon J. Gerraty 	     * If the '$' started the string literal (which means no quotes),
468956e45f6SSimon J. Gerraty 	     * and the variable expression is followed by a space, looks like
469956e45f6SSimon J. Gerraty 	     * a comparison operator or is the end of the expression, we are
470956e45f6SSimon J. Gerraty 	     * done.
4713955d011SMarcel Moolenaar 	     */
472956e45f6SSimon J. Gerraty 	    if (atStart && is_separator(par->p[0]))
4733955d011SMarcel Moolenaar 		goto cleanup;
474956e45f6SSimon J. Gerraty 
475956e45f6SSimon J. Gerraty 	    Buf_AddStr(&buf, str);
476*e2eeea75SSimon J. Gerraty 	    if (*out_freeIt) {
477*e2eeea75SSimon J. Gerraty 		free(*out_freeIt);
478*e2eeea75SSimon J. Gerraty 		*out_freeIt = NULL;
4793955d011SMarcel Moolenaar 	    }
4803955d011SMarcel Moolenaar 	    str = NULL;		/* not finished yet */
481956e45f6SSimon J. Gerraty 	    continue;
4823955d011SMarcel Moolenaar 	default:
483*e2eeea75SSimon J. Gerraty 	    if (strictLHS && !quoted && *start != '$' && !ch_isdigit(*start)) {
48428a6bc81SSimon J. Gerraty 		/* lhs must be quoted, a variable reference or number */
48528a6bc81SSimon J. Gerraty 		str = NULL;
48628a6bc81SSimon J. Gerraty 		goto cleanup;
48728a6bc81SSimon J. Gerraty 	    }
488956e45f6SSimon J. Gerraty 	    Buf_AddByte(&buf, par->p[0]);
489956e45f6SSimon J. Gerraty 	    par->p++;
490956e45f6SSimon J. Gerraty 	    continue;
4913955d011SMarcel Moolenaar 	}
4923955d011SMarcel Moolenaar     }
4933955d011SMarcel Moolenaar got_str:
494*e2eeea75SSimon J. Gerraty     *out_freeIt = Buf_GetAll(&buf, NULL);
495*e2eeea75SSimon J. Gerraty     str = *out_freeIt;
4963955d011SMarcel Moolenaar cleanup:
4973955d011SMarcel Moolenaar     Buf_Destroy(&buf, FALSE);
4983955d011SMarcel Moolenaar     return str;
4993955d011SMarcel Moolenaar }
5002c3632d1SSimon J. Gerraty 
501*e2eeea75SSimon J. Gerraty struct If {
5022c3632d1SSimon J. Gerraty     const char *form;		/* Form of if */
5032c3632d1SSimon J. Gerraty     size_t formlen;		/* Length of form */
5042c3632d1SSimon J. Gerraty     Boolean doNot;		/* TRUE if default function should be negated */
505956e45f6SSimon J. Gerraty     Boolean (*defProc)(size_t, const char *); /* Default function to apply */
506*e2eeea75SSimon J. Gerraty };
507*e2eeea75SSimon J. Gerraty 
508*e2eeea75SSimon J. Gerraty /* The different forms of .if directives. */
509*e2eeea75SSimon J. Gerraty static const struct If ifs[] = {
510956e45f6SSimon J. Gerraty     { "def",   3, FALSE, FuncDefined },
511956e45f6SSimon J. Gerraty     { "ndef",  4, TRUE,  FuncDefined },
512956e45f6SSimon J. Gerraty     { "make",  4, FALSE, FuncMake },
513956e45f6SSimon J. Gerraty     { "nmake", 5, TRUE,  FuncMake },
514956e45f6SSimon J. Gerraty     { "",      0, FALSE, FuncDefined },
5152c3632d1SSimon J. Gerraty     { NULL,    0, FALSE, NULL }
5162c3632d1SSimon J. Gerraty };
517*e2eeea75SSimon J. Gerraty enum { PLAIN_IF_INDEX = 4 };
518*e2eeea75SSimon J. Gerraty 
519*e2eeea75SSimon J. Gerraty static Boolean
520*e2eeea75SSimon J. Gerraty If_Eval(const struct If *if_info, const char *arg, size_t arglen)
521*e2eeea75SSimon J. Gerraty {
522*e2eeea75SSimon J. Gerraty     Boolean res = if_info->defProc(arglen, arg);
523*e2eeea75SSimon J. Gerraty     return if_info->doNot ? !res : res;
524*e2eeea75SSimon J. Gerraty }
5252c3632d1SSimon J. Gerraty 
526956e45f6SSimon J. Gerraty /* Evaluate a "comparison without operator", such as in ".if ${VAR}" or
527956e45f6SSimon J. Gerraty  * ".if 0". */
528*e2eeea75SSimon J. Gerraty static Boolean
529*e2eeea75SSimon J. Gerraty EvalNotEmpty(CondParser *par, const char *value, Boolean quoted)
5303955d011SMarcel Moolenaar {
531*e2eeea75SSimon J. Gerraty     double num;
532956e45f6SSimon J. Gerraty 
533*e2eeea75SSimon J. Gerraty     /* For .ifxxx "...", check for non-empty string. */
534*e2eeea75SSimon J. Gerraty     if (quoted)
535*e2eeea75SSimon J. Gerraty 	return value[0] != '\0';
536956e45f6SSimon J. Gerraty 
537*e2eeea75SSimon J. Gerraty     /* For .ifxxx <number>, compare against zero */
538*e2eeea75SSimon J. Gerraty     if (TryParseNumber(value, &num))
539*e2eeea75SSimon J. Gerraty 	return num != 0.0;
540956e45f6SSimon J. Gerraty 
541*e2eeea75SSimon J. Gerraty     /* For .if ${...}, check for non-empty string.  This is different from
542*e2eeea75SSimon J. Gerraty      * the evaluation function from that .if variant, which would test
543*e2eeea75SSimon J. Gerraty      * whether a variable of the given name were defined. */
544*e2eeea75SSimon J. Gerraty     /* XXX: Whitespace should count as empty, just as in ParseEmptyArg. */
545956e45f6SSimon J. Gerraty     if (par->if_info->form[0] == '\0')
546*e2eeea75SSimon J. Gerraty 	return value[0] != '\0';
547956e45f6SSimon J. Gerraty 
548*e2eeea75SSimon J. Gerraty     /* For the other variants of .ifxxx ${...}, use its default function. */
549*e2eeea75SSimon J. Gerraty     return If_Eval(par->if_info, value, strlen(value));
550956e45f6SSimon J. Gerraty }
551956e45f6SSimon J. Gerraty 
552956e45f6SSimon J. Gerraty /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
553956e45f6SSimon J. Gerraty static Token
554956e45f6SSimon J. Gerraty EvalCompareNum(double lhs, const char *op, double rhs)
555956e45f6SSimon J. Gerraty {
556956e45f6SSimon J. Gerraty     DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, op);
557956e45f6SSimon J. Gerraty 
558956e45f6SSimon J. Gerraty     switch (op[0]) {
559956e45f6SSimon J. Gerraty     case '!':
560956e45f6SSimon J. Gerraty 	if (op[1] != '=') {
561956e45f6SSimon J. Gerraty 	    Parse_Error(PARSE_WARNING, "Unknown operator");
562956e45f6SSimon J. Gerraty 	    /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
563956e45f6SSimon J. Gerraty 	    return TOK_ERROR;
564956e45f6SSimon J. Gerraty 	}
565*e2eeea75SSimon J. Gerraty 	return ToToken(lhs != rhs);
566956e45f6SSimon J. Gerraty     case '=':
567956e45f6SSimon J. Gerraty 	if (op[1] != '=') {
568956e45f6SSimon J. Gerraty 	    Parse_Error(PARSE_WARNING, "Unknown operator");
569956e45f6SSimon J. Gerraty 	    /* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
570956e45f6SSimon J. Gerraty 	    return TOK_ERROR;
571956e45f6SSimon J. Gerraty 	}
572*e2eeea75SSimon J. Gerraty 	return ToToken(lhs == rhs);
573956e45f6SSimon J. Gerraty     case '<':
574*e2eeea75SSimon J. Gerraty 	return ToToken(op[1] == '=' ? lhs <= rhs : lhs < rhs);
575956e45f6SSimon J. Gerraty     case '>':
576*e2eeea75SSimon J. Gerraty 	return ToToken(op[1] == '=' ? lhs >= rhs : lhs > rhs);
577956e45f6SSimon J. Gerraty     }
578956e45f6SSimon J. Gerraty     return TOK_ERROR;
579956e45f6SSimon J. Gerraty }
580956e45f6SSimon J. Gerraty 
581956e45f6SSimon J. Gerraty static Token
582956e45f6SSimon J. Gerraty EvalCompareStr(const char *lhs, const char *op, const char *rhs)
583956e45f6SSimon J. Gerraty {
584956e45f6SSimon J. Gerraty     if (!((op[0] == '!' || op[0] == '=') && op[1] == '=')) {
585956e45f6SSimon J. Gerraty 	Parse_Error(PARSE_WARNING,
586956e45f6SSimon J. Gerraty 		    "String comparison operator must be either == or !=");
587956e45f6SSimon J. Gerraty 	/* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
588956e45f6SSimon J. Gerraty 	return TOK_ERROR;
589956e45f6SSimon J. Gerraty     }
590956e45f6SSimon J. Gerraty 
591956e45f6SSimon J. Gerraty     DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", lhs, rhs, op);
592*e2eeea75SSimon J. Gerraty     return ToToken((*op == '=') == (strcmp(lhs, rhs) == 0));
593956e45f6SSimon J. Gerraty }
594956e45f6SSimon J. Gerraty 
595956e45f6SSimon J. Gerraty /* Evaluate a comparison, such as "${VAR} == 12345". */
596956e45f6SSimon J. Gerraty static Token
597956e45f6SSimon J. Gerraty EvalCompare(const char *lhs, Boolean lhsQuoted, const char *op,
598956e45f6SSimon J. Gerraty 	    const char *rhs, Boolean rhsQuoted)
599956e45f6SSimon J. Gerraty {
6003955d011SMarcel Moolenaar     double left, right;
6013955d011SMarcel Moolenaar 
602956e45f6SSimon J. Gerraty     if (!rhsQuoted && !lhsQuoted)
603956e45f6SSimon J. Gerraty 	if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
604956e45f6SSimon J. Gerraty 	    return EvalCompareNum(left, op, right);
605956e45f6SSimon J. Gerraty 
606956e45f6SSimon J. Gerraty     return EvalCompareStr(lhs, op, rhs);
607956e45f6SSimon J. Gerraty }
608956e45f6SSimon J. Gerraty 
609956e45f6SSimon J. Gerraty /* Parse a comparison condition such as:
610956e45f6SSimon J. Gerraty  *
611956e45f6SSimon J. Gerraty  *	0
612956e45f6SSimon J. Gerraty  *	${VAR:Mpattern}
613956e45f6SSimon J. Gerraty  *	${VAR} == value
614956e45f6SSimon J. Gerraty  *	${VAR:U0} < 12345
615956e45f6SSimon J. Gerraty  */
616956e45f6SSimon J. Gerraty static Token
617956e45f6SSimon J. Gerraty CondParser_Comparison(CondParser *par, Boolean doEval)
618956e45f6SSimon J. Gerraty {
619956e45f6SSimon J. Gerraty     Token t = TOK_ERROR;
620956e45f6SSimon J. Gerraty     const char *lhs, *op, *rhs;
621*e2eeea75SSimon J. Gerraty     void *lhs_freeIt, *rhs_freeIt;
622956e45f6SSimon J. Gerraty     Boolean lhsQuoted, rhsQuoted;
623956e45f6SSimon J. Gerraty 
6243955d011SMarcel Moolenaar     /*
6253955d011SMarcel Moolenaar      * Parse the variable spec and skip over it, saving its
6263955d011SMarcel Moolenaar      * value in lhs.
6273955d011SMarcel Moolenaar      */
628*e2eeea75SSimon J. Gerraty     lhs = CondParser_String(par, doEval, lhsStrict, &lhsQuoted, &lhs_freeIt);
629*e2eeea75SSimon J. Gerraty     if (lhs == NULL)
630*e2eeea75SSimon J. Gerraty 	goto done_lhs;
6313955d011SMarcel Moolenaar 
632956e45f6SSimon J. Gerraty     CondParser_SkipWhitespace(par);
6333955d011SMarcel Moolenaar 
634956e45f6SSimon J. Gerraty     op = par->p;
635956e45f6SSimon J. Gerraty     switch (par->p[0]) {
6363955d011SMarcel Moolenaar     case '!':
6373955d011SMarcel Moolenaar     case '=':
6383955d011SMarcel Moolenaar     case '<':
6393955d011SMarcel Moolenaar     case '>':
640*e2eeea75SSimon J. Gerraty 	if (par->p[1] == '=')
641956e45f6SSimon J. Gerraty 	    par->p += 2;
642*e2eeea75SSimon J. Gerraty 	else
643956e45f6SSimon J. Gerraty 	    par->p++;
6443955d011SMarcel Moolenaar 	break;
6453955d011SMarcel Moolenaar     default:
646*e2eeea75SSimon J. Gerraty 	/* Unknown operator, compare against an empty string or 0. */
647*e2eeea75SSimon J. Gerraty 	t = ToToken(doEval && EvalNotEmpty(par, lhs, lhsQuoted));
648*e2eeea75SSimon J. Gerraty 	goto done_lhs;
6493955d011SMarcel Moolenaar     }
6503955d011SMarcel Moolenaar 
651956e45f6SSimon J. Gerraty     CondParser_SkipWhitespace(par);
6523955d011SMarcel Moolenaar 
653956e45f6SSimon J. Gerraty     if (par->p[0] == '\0') {
654956e45f6SSimon J. Gerraty 	Parse_Error(PARSE_WARNING, "Missing right-hand-side of operator");
655956e45f6SSimon J. Gerraty 	/* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
656*e2eeea75SSimon J. Gerraty 	goto done_lhs;
6573955d011SMarcel Moolenaar     }
6583955d011SMarcel Moolenaar 
659*e2eeea75SSimon J. Gerraty     rhs = CondParser_String(par, doEval, FALSE, &rhsQuoted, &rhs_freeIt);
660956e45f6SSimon J. Gerraty     if (rhs == NULL)
661*e2eeea75SSimon J. Gerraty 	goto done_rhs;
6623955d011SMarcel Moolenaar 
6633841c287SSimon J. Gerraty     if (!doEval) {
6643841c287SSimon J. Gerraty 	t = TOK_FALSE;
665*e2eeea75SSimon J. Gerraty 	goto done_rhs;
6663841c287SSimon J. Gerraty     }
6673841c287SSimon J. Gerraty 
668956e45f6SSimon J. Gerraty     t = EvalCompare(lhs, lhsQuoted, op, rhs, rhsQuoted);
6693955d011SMarcel Moolenaar 
670*e2eeea75SSimon J. Gerraty done_rhs:
671*e2eeea75SSimon J. Gerraty     free(rhs_freeIt);
672*e2eeea75SSimon J. Gerraty done_lhs:
673*e2eeea75SSimon J. Gerraty     free(lhs_freeIt);
6743955d011SMarcel Moolenaar     return t;
6753955d011SMarcel Moolenaar }
6763955d011SMarcel Moolenaar 
677*e2eeea75SSimon J. Gerraty /* The argument to empty() is a variable name, optionally followed by
678*e2eeea75SSimon J. Gerraty  * variable modifiers. */
679956e45f6SSimon J. Gerraty static size_t
680*e2eeea75SSimon J. Gerraty ParseEmptyArg(const char **pp, Boolean doEval,
681*e2eeea75SSimon J. Gerraty 	      const char *func MAKE_ATTR_UNUSED, char **out_arg)
6823955d011SMarcel Moolenaar {
6832c3632d1SSimon J. Gerraty     void *val_freeIt;
6842c3632d1SSimon J. Gerraty     const char *val;
685956e45f6SSimon J. Gerraty     size_t magic_res;
6863955d011SMarcel Moolenaar 
6873955d011SMarcel Moolenaar     /* We do all the work here and return the result as the length */
688*e2eeea75SSimon J. Gerraty     *out_arg = NULL;
6893955d011SMarcel Moolenaar 
690*e2eeea75SSimon J. Gerraty     (*pp)--;			/* Make (*pp)[1] point to the '('. */
691*e2eeea75SSimon J. Gerraty     (void)Var_Parse(pp, VAR_CMDLINE, doEval ? VARE_WANTRES : VARE_NONE,
692956e45f6SSimon J. Gerraty 		    &val, &val_freeIt);
693956e45f6SSimon J. Gerraty     /* TODO: handle errors */
694*e2eeea75SSimon J. Gerraty     /* If successful, *pp points beyond the closing ')' now. */
6953955d011SMarcel Moolenaar 
6963955d011SMarcel Moolenaar     if (val == var_Error) {
6972c3632d1SSimon J. Gerraty 	free(val_freeIt);
698956e45f6SSimon J. Gerraty 	return (size_t)-1;
6993955d011SMarcel Moolenaar     }
7003955d011SMarcel Moolenaar 
7013955d011SMarcel Moolenaar     /* A variable is empty when it just contains spaces... 4/15/92, christos */
702956e45f6SSimon J. Gerraty     cpp_skip_whitespace(&val);
7033955d011SMarcel Moolenaar 
7043955d011SMarcel Moolenaar     /*
7053955d011SMarcel Moolenaar      * For consistency with the other functions we can't generate the
7063955d011SMarcel Moolenaar      * true/false here.
7073955d011SMarcel Moolenaar      */
708956e45f6SSimon J. Gerraty     magic_res = *val != '\0' ? 2 : 1;
7092c3632d1SSimon J. Gerraty     free(val_freeIt);
710956e45f6SSimon J. Gerraty     return magic_res;
7113955d011SMarcel Moolenaar }
7123955d011SMarcel Moolenaar 
7133955d011SMarcel Moolenaar static Boolean
714956e45f6SSimon J. Gerraty FuncEmpty(size_t arglen, const char *arg MAKE_ATTR_UNUSED)
7153955d011SMarcel Moolenaar {
716956e45f6SSimon J. Gerraty     /* Magic values ahead, see ParseEmptyArg. */
7173955d011SMarcel Moolenaar     return arglen == 1;
7183955d011SMarcel Moolenaar }
7193955d011SMarcel Moolenaar 
720*e2eeea75SSimon J. Gerraty static Boolean
721*e2eeea75SSimon J. Gerraty CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
7223955d011SMarcel Moolenaar {
7233955d011SMarcel Moolenaar     static const struct fn_def {
7243955d011SMarcel Moolenaar 	const char *fn_name;
7252c3632d1SSimon J. Gerraty 	size_t fn_name_len;
726956e45f6SSimon J. Gerraty 	size_t (*fn_parse)(const char **, Boolean, const char *, char **);
727956e45f6SSimon J. Gerraty 	Boolean (*fn_eval)(size_t, const char *);
728*e2eeea75SSimon J. Gerraty     } fns[] = {
729956e45f6SSimon J. Gerraty 	{ "defined",  7, ParseFuncArg,  FuncDefined },
730956e45f6SSimon J. Gerraty 	{ "make",     4, ParseFuncArg,  FuncMake },
731956e45f6SSimon J. Gerraty 	{ "exists",   6, ParseFuncArg,  FuncExists },
732956e45f6SSimon J. Gerraty 	{ "empty",    5, ParseEmptyArg, FuncEmpty },
733956e45f6SSimon J. Gerraty 	{ "target",   6, ParseFuncArg,  FuncTarget },
734*e2eeea75SSimon J. Gerraty 	{ "commands", 8, ParseFuncArg,  FuncCommands }
7353955d011SMarcel Moolenaar     };
736*e2eeea75SSimon J. Gerraty     const struct fn_def *fn;
737*e2eeea75SSimon J. Gerraty     char *arg = NULL;
738*e2eeea75SSimon J. Gerraty     size_t arglen;
739*e2eeea75SSimon J. Gerraty     const char *cp = par->p;
740*e2eeea75SSimon J. Gerraty     const struct fn_def *fns_end = fns + sizeof fns / sizeof fns[0];
741*e2eeea75SSimon J. Gerraty 
742*e2eeea75SSimon J. Gerraty     for (fn = fns; fn != fns_end; fn++) {
743*e2eeea75SSimon J. Gerraty 	if (!is_token(cp, fn->fn_name, fn->fn_name_len))
744*e2eeea75SSimon J. Gerraty 	    continue;
745*e2eeea75SSimon J. Gerraty 
746*e2eeea75SSimon J. Gerraty 	cp += fn->fn_name_len;
747*e2eeea75SSimon J. Gerraty 	cpp_skip_whitespace(&cp);
748*e2eeea75SSimon J. Gerraty 	if (*cp != '(')
749*e2eeea75SSimon J. Gerraty 	    break;
750*e2eeea75SSimon J. Gerraty 
751*e2eeea75SSimon J. Gerraty 	arglen = fn->fn_parse(&cp, doEval, fn->fn_name, &arg);
752*e2eeea75SSimon J. Gerraty 	if (arglen == 0 || arglen == (size_t)-1) {
753*e2eeea75SSimon J. Gerraty 	    par->p = cp;
754*e2eeea75SSimon J. Gerraty 	    *out_token = arglen == 0 ? TOK_FALSE : TOK_ERROR;
755*e2eeea75SSimon J. Gerraty 	    return TRUE;
756*e2eeea75SSimon J. Gerraty 	}
757*e2eeea75SSimon J. Gerraty 
758*e2eeea75SSimon J. Gerraty 	/* Evaluate the argument using the required function. */
759*e2eeea75SSimon J. Gerraty 	*out_token = ToToken(!doEval || fn->fn_eval(arglen, arg));
760*e2eeea75SSimon J. Gerraty 	free(arg);
761*e2eeea75SSimon J. Gerraty 	par->p = cp;
762*e2eeea75SSimon J. Gerraty 	return TRUE;
763*e2eeea75SSimon J. Gerraty     }
764*e2eeea75SSimon J. Gerraty 
765*e2eeea75SSimon J. Gerraty     return FALSE;
766*e2eeea75SSimon J. Gerraty }
767*e2eeea75SSimon J. Gerraty 
768*e2eeea75SSimon J. Gerraty /* Parse a function call, a number, a variable expression or a string
769*e2eeea75SSimon J. Gerraty  * literal. */
770*e2eeea75SSimon J. Gerraty static Token
771*e2eeea75SSimon J. Gerraty CondParser_LeafToken(CondParser *par, Boolean doEval)
772*e2eeea75SSimon J. Gerraty {
7733955d011SMarcel Moolenaar     Token t;
7743955d011SMarcel Moolenaar     char *arg = NULL;
775956e45f6SSimon J. Gerraty     size_t arglen;
776956e45f6SSimon J. Gerraty     const char *cp = par->p;
7772c3632d1SSimon J. Gerraty     const char *cp1;
7783955d011SMarcel Moolenaar 
779*e2eeea75SSimon J. Gerraty     if (CondParser_Func(par, doEval, &t))
7803955d011SMarcel Moolenaar 	return t;
7813955d011SMarcel Moolenaar 
7823955d011SMarcel Moolenaar     /* Push anything numeric through the compare expression */
783956e45f6SSimon J. Gerraty     cp = par->p;
784*e2eeea75SSimon J. Gerraty     if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
785956e45f6SSimon J. Gerraty 	return CondParser_Comparison(par, doEval);
7863955d011SMarcel Moolenaar 
7873955d011SMarcel Moolenaar     /*
7883955d011SMarcel Moolenaar      * Most likely we have a naked token to apply the default function to.
7893955d011SMarcel Moolenaar      * However ".if a == b" gets here when the "a" is unquoted and doesn't
7903955d011SMarcel Moolenaar      * start with a '$'. This surprises people.
7913955d011SMarcel Moolenaar      * If what follows the function argument is a '=' or '!' then the syntax
7923955d011SMarcel Moolenaar      * would be invalid if we did "defined(a)" - so instead treat as an
7933955d011SMarcel Moolenaar      * expression.
7943955d011SMarcel Moolenaar      */
795956e45f6SSimon J. Gerraty     arglen = ParseFuncArg(&cp, doEval, NULL, &arg);
796956e45f6SSimon J. Gerraty     cp1 = cp;
797956e45f6SSimon J. Gerraty     cpp_skip_whitespace(&cp1);
7983955d011SMarcel Moolenaar     if (*cp1 == '=' || *cp1 == '!')
799956e45f6SSimon J. Gerraty 	return CondParser_Comparison(par, doEval);
800956e45f6SSimon J. Gerraty     par->p = cp;
8013955d011SMarcel Moolenaar 
8023955d011SMarcel Moolenaar     /*
8033955d011SMarcel Moolenaar      * Evaluate the argument using the default function.
804956e45f6SSimon J. Gerraty      * This path always treats .if as .ifdef. To get here, the character
8053955d011SMarcel Moolenaar      * after .if must have been taken literally, so the argument cannot
8063955d011SMarcel Moolenaar      * be empty - even if it contained a variable expansion.
8073955d011SMarcel Moolenaar      */
808*e2eeea75SSimon J. Gerraty     t = ToToken(!doEval || If_Eval(par->if_info, arg, arglen));
8093955d011SMarcel Moolenaar     free(arg);
8103955d011SMarcel Moolenaar     return t;
8113955d011SMarcel Moolenaar }
8123955d011SMarcel Moolenaar 
813956e45f6SSimon J. Gerraty /* Return the next token or comparison result from the parser. */
8143955d011SMarcel Moolenaar static Token
815956e45f6SSimon J. Gerraty CondParser_Token(CondParser *par, Boolean doEval)
8163955d011SMarcel Moolenaar {
8173955d011SMarcel Moolenaar     Token t;
8183955d011SMarcel Moolenaar 
819956e45f6SSimon J. Gerraty     t = par->curr;
8203955d011SMarcel Moolenaar     if (t != TOK_NONE) {
821956e45f6SSimon J. Gerraty 	par->curr = TOK_NONE;
8223955d011SMarcel Moolenaar 	return t;
8233955d011SMarcel Moolenaar     }
8243955d011SMarcel Moolenaar 
825*e2eeea75SSimon J. Gerraty     cpp_skip_hspace(&par->p);
8263955d011SMarcel Moolenaar 
827956e45f6SSimon J. Gerraty     switch (par->p[0]) {
8283955d011SMarcel Moolenaar 
8293955d011SMarcel Moolenaar     case '(':
830956e45f6SSimon J. Gerraty 	par->p++;
8313955d011SMarcel Moolenaar 	return TOK_LPAREN;
8323955d011SMarcel Moolenaar 
8333955d011SMarcel Moolenaar     case ')':
834956e45f6SSimon J. Gerraty 	par->p++;
8353955d011SMarcel Moolenaar 	return TOK_RPAREN;
8363955d011SMarcel Moolenaar 
8373955d011SMarcel Moolenaar     case '|':
838956e45f6SSimon J. Gerraty 	par->p++;
839*e2eeea75SSimon J. Gerraty 	if (par->p[0] == '|')
840956e45f6SSimon J. Gerraty 	    par->p++;
841*e2eeea75SSimon J. Gerraty 	else if (opts.lint) {
842*e2eeea75SSimon J. Gerraty 	    Parse_Error(PARSE_FATAL, "Unknown operator '|'");
843*e2eeea75SSimon J. Gerraty 	    par->printedError = TRUE;
844*e2eeea75SSimon J. Gerraty 	    return TOK_ERROR;
8453955d011SMarcel Moolenaar 	}
8463955d011SMarcel Moolenaar 	return TOK_OR;
8473955d011SMarcel Moolenaar 
8483955d011SMarcel Moolenaar     case '&':
849956e45f6SSimon J. Gerraty 	par->p++;
850*e2eeea75SSimon J. Gerraty 	if (par->p[0] == '&')
851956e45f6SSimon J. Gerraty 	    par->p++;
852*e2eeea75SSimon J. Gerraty 	else if (opts.lint) {
853*e2eeea75SSimon J. Gerraty 	    Parse_Error(PARSE_FATAL, "Unknown operator '&'");
854*e2eeea75SSimon J. Gerraty 	    par->printedError = TRUE;
855*e2eeea75SSimon J. Gerraty 	    return TOK_ERROR;
8563955d011SMarcel Moolenaar 	}
8573955d011SMarcel Moolenaar 	return TOK_AND;
8583955d011SMarcel Moolenaar 
8593955d011SMarcel Moolenaar     case '!':
860956e45f6SSimon J. Gerraty 	par->p++;
8613955d011SMarcel Moolenaar 	return TOK_NOT;
8623955d011SMarcel Moolenaar 
863*e2eeea75SSimon J. Gerraty     case '#':			/* XXX: see unit-tests/cond-token-plain.mk */
864*e2eeea75SSimon J. Gerraty     case '\n':			/* XXX: why should this end the condition? */
865*e2eeea75SSimon J. Gerraty 				/* Probably obsolete now, from 1993-03-21. */
8663955d011SMarcel Moolenaar     case '\0':
8673955d011SMarcel Moolenaar 	return TOK_EOF;
8683955d011SMarcel Moolenaar 
8693955d011SMarcel Moolenaar     case '"':
8703955d011SMarcel Moolenaar     case '$':
871956e45f6SSimon J. Gerraty 	return CondParser_Comparison(par, doEval);
8723955d011SMarcel Moolenaar 
8733955d011SMarcel Moolenaar     default:
874*e2eeea75SSimon J. Gerraty 	return CondParser_LeafToken(par, doEval);
8753955d011SMarcel Moolenaar     }
8763955d011SMarcel Moolenaar }
8773955d011SMarcel Moolenaar 
878956e45f6SSimon J. Gerraty /* Parse a single term in the expression. This consists of a terminal symbol
879956e45f6SSimon J. Gerraty  * or TOK_NOT and a term (not including the binary operators):
880956e45f6SSimon J. Gerraty  *
8813955d011SMarcel Moolenaar  *	T -> defined(variable) | make(target) | exists(file) | symbol
8823955d011SMarcel Moolenaar  *	T -> ! T | ( E )
8833955d011SMarcel Moolenaar  *
8843955d011SMarcel Moolenaar  * Results:
8853955d011SMarcel Moolenaar  *	TOK_TRUE, TOK_FALSE or TOK_ERROR.
8863955d011SMarcel Moolenaar  */
8873955d011SMarcel Moolenaar static Token
888956e45f6SSimon J. Gerraty CondParser_Term(CondParser *par, Boolean doEval)
8893955d011SMarcel Moolenaar {
8903955d011SMarcel Moolenaar     Token t;
8913955d011SMarcel Moolenaar 
892956e45f6SSimon J. Gerraty     t = CondParser_Token(par, doEval);
8933955d011SMarcel Moolenaar 
8943955d011SMarcel Moolenaar     if (t == TOK_EOF) {
8953955d011SMarcel Moolenaar 	/*
8963955d011SMarcel Moolenaar 	 * If we reached the end of the expression, the expression
8973955d011SMarcel Moolenaar 	 * is malformed...
8983955d011SMarcel Moolenaar 	 */
8993955d011SMarcel Moolenaar 	t = TOK_ERROR;
9003955d011SMarcel Moolenaar     } else if (t == TOK_LPAREN) {
9013955d011SMarcel Moolenaar 	/*
9023955d011SMarcel Moolenaar 	 * T -> ( E )
9033955d011SMarcel Moolenaar 	 */
904956e45f6SSimon J. Gerraty 	t = CondParser_Expr(par, doEval);
9053955d011SMarcel Moolenaar 	if (t != TOK_ERROR) {
906956e45f6SSimon J. Gerraty 	    if (CondParser_Token(par, doEval) != TOK_RPAREN) {
9073955d011SMarcel Moolenaar 		t = TOK_ERROR;
9083955d011SMarcel Moolenaar 	    }
9093955d011SMarcel Moolenaar 	}
9103955d011SMarcel Moolenaar     } else if (t == TOK_NOT) {
911956e45f6SSimon J. Gerraty 	t = CondParser_Term(par, doEval);
9123955d011SMarcel Moolenaar 	if (t == TOK_TRUE) {
9133955d011SMarcel Moolenaar 	    t = TOK_FALSE;
9143955d011SMarcel Moolenaar 	} else if (t == TOK_FALSE) {
9153955d011SMarcel Moolenaar 	    t = TOK_TRUE;
9163955d011SMarcel Moolenaar 	}
9173955d011SMarcel Moolenaar     }
9183841c287SSimon J. Gerraty     return t;
9193955d011SMarcel Moolenaar }
9202c3632d1SSimon J. Gerraty 
921956e45f6SSimon J. Gerraty /* Parse a conjunctive factor (nice name, wot?)
922956e45f6SSimon J. Gerraty  *
9233955d011SMarcel Moolenaar  *	F -> T && F | T
9243955d011SMarcel Moolenaar  *
9253955d011SMarcel Moolenaar  * Results:
9263955d011SMarcel Moolenaar  *	TOK_TRUE, TOK_FALSE or TOK_ERROR
9273955d011SMarcel Moolenaar  */
9283955d011SMarcel Moolenaar static Token
929956e45f6SSimon J. Gerraty CondParser_Factor(CondParser *par, Boolean doEval)
9303955d011SMarcel Moolenaar {
9313955d011SMarcel Moolenaar     Token l, o;
9323955d011SMarcel Moolenaar 
933956e45f6SSimon J. Gerraty     l = CondParser_Term(par, doEval);
9343955d011SMarcel Moolenaar     if (l != TOK_ERROR) {
935956e45f6SSimon J. Gerraty 	o = CondParser_Token(par, doEval);
9363955d011SMarcel Moolenaar 
9373955d011SMarcel Moolenaar 	if (o == TOK_AND) {
9383955d011SMarcel Moolenaar 	    /*
9393955d011SMarcel Moolenaar 	     * F -> T && F
9403955d011SMarcel Moolenaar 	     *
941956e45f6SSimon J. Gerraty 	     * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we
942956e45f6SSimon J. Gerraty 	     * have to parse the r.h.s. anyway (to throw it away).
943956e45f6SSimon J. Gerraty 	     * If T is TOK_TRUE, the result is the r.h.s., be it a TOK_ERROR
944956e45f6SSimon J. Gerraty 	     * or not.
9453955d011SMarcel Moolenaar 	     */
9463955d011SMarcel Moolenaar 	    if (l == TOK_TRUE) {
947956e45f6SSimon J. Gerraty 		l = CondParser_Factor(par, doEval);
9483955d011SMarcel Moolenaar 	    } else {
949956e45f6SSimon J. Gerraty 		(void)CondParser_Factor(par, FALSE);
9503955d011SMarcel Moolenaar 	    }
9513955d011SMarcel Moolenaar 	} else {
9523955d011SMarcel Moolenaar 	    /*
9533955d011SMarcel Moolenaar 	     * F -> T
9543955d011SMarcel Moolenaar 	     */
955956e45f6SSimon J. Gerraty 	    CondParser_PushBack(par, o);
9563955d011SMarcel Moolenaar 	}
9573955d011SMarcel Moolenaar     }
9583841c287SSimon J. Gerraty     return l;
9593955d011SMarcel Moolenaar }
9602c3632d1SSimon J. Gerraty 
961956e45f6SSimon J. Gerraty /* Main expression production.
962956e45f6SSimon J. Gerraty  *
9633955d011SMarcel Moolenaar  *	E -> F || E | F
9643955d011SMarcel Moolenaar  *
9653955d011SMarcel Moolenaar  * Results:
9663955d011SMarcel Moolenaar  *	TOK_TRUE, TOK_FALSE or TOK_ERROR.
9673955d011SMarcel Moolenaar  */
9683955d011SMarcel Moolenaar static Token
969956e45f6SSimon J. Gerraty CondParser_Expr(CondParser *par, Boolean doEval)
9703955d011SMarcel Moolenaar {
9713955d011SMarcel Moolenaar     Token l, o;
9723955d011SMarcel Moolenaar 
973956e45f6SSimon J. Gerraty     l = CondParser_Factor(par, doEval);
9743955d011SMarcel Moolenaar     if (l != TOK_ERROR) {
975956e45f6SSimon J. Gerraty 	o = CondParser_Token(par, doEval);
9763955d011SMarcel Moolenaar 
9773955d011SMarcel Moolenaar 	if (o == TOK_OR) {
9783955d011SMarcel Moolenaar 	    /*
9793955d011SMarcel Moolenaar 	     * E -> F || E
9803955d011SMarcel Moolenaar 	     *
9813955d011SMarcel Moolenaar 	     * A similar thing occurs for ||, except that here we make sure
9823955d011SMarcel Moolenaar 	     * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s.
9833955d011SMarcel Moolenaar 	     * Once again, if l is TOK_FALSE, the result is the r.h.s. and once
9843955d011SMarcel Moolenaar 	     * again if l is TOK_TRUE, we parse the r.h.s. to throw it away.
9853955d011SMarcel Moolenaar 	     */
9863955d011SMarcel Moolenaar 	    if (l == TOK_FALSE) {
987956e45f6SSimon J. Gerraty 		l = CondParser_Expr(par, doEval);
9883955d011SMarcel Moolenaar 	    } else {
989956e45f6SSimon J. Gerraty 		(void)CondParser_Expr(par, FALSE);
9903955d011SMarcel Moolenaar 	    }
9913955d011SMarcel Moolenaar 	} else {
9923955d011SMarcel Moolenaar 	    /*
9933955d011SMarcel Moolenaar 	     * E -> F
9943955d011SMarcel Moolenaar 	     */
995956e45f6SSimon J. Gerraty 	    CondParser_PushBack(par, o);
9963955d011SMarcel Moolenaar 	}
9973955d011SMarcel Moolenaar     }
9983841c287SSimon J. Gerraty     return l;
9993955d011SMarcel Moolenaar }
10003955d011SMarcel Moolenaar 
10012c3632d1SSimon J. Gerraty static CondEvalResult
1002956e45f6SSimon J. Gerraty CondParser_Eval(CondParser *par, Boolean *value)
10032c3632d1SSimon J. Gerraty {
1004956e45f6SSimon J. Gerraty     Token res;
10052c3632d1SSimon J. Gerraty 
1006956e45f6SSimon J. Gerraty     DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
10072c3632d1SSimon J. Gerraty 
1008956e45f6SSimon J. Gerraty     res = CondParser_Expr(par, TRUE);
1009956e45f6SSimon J. Gerraty     if (res != TOK_FALSE && res != TOK_TRUE)
10102c3632d1SSimon J. Gerraty 	return COND_INVALID;
1011956e45f6SSimon J. Gerraty 
1012956e45f6SSimon J. Gerraty     if (CondParser_Token(par, TRUE /* XXX: Why TRUE? */) != TOK_EOF)
1013956e45f6SSimon J. Gerraty 	return COND_INVALID;
1014956e45f6SSimon J. Gerraty 
1015956e45f6SSimon J. Gerraty     *value = res == TOK_TRUE;
1016956e45f6SSimon J. Gerraty     return COND_PARSE;
10172c3632d1SSimon J. Gerraty }
10182c3632d1SSimon J. Gerraty 
1019956e45f6SSimon J. Gerraty /* Evaluate the condition, including any side effects from the variable
1020956e45f6SSimon J. Gerraty  * expressions in the condition. The condition consists of &&, ||, !,
1021956e45f6SSimon J. Gerraty  * function(arg), comparisons and parenthetical groupings thereof.
10223955d011SMarcel Moolenaar  *
10233955d011SMarcel Moolenaar  * Results:
10243955d011SMarcel Moolenaar  *	COND_PARSE	if the condition was valid grammatically
10253955d011SMarcel Moolenaar  *	COND_INVALID	if not a valid conditional.
10263955d011SMarcel Moolenaar  *
10273955d011SMarcel Moolenaar  *	(*value) is set to the boolean value of the condition
10283955d011SMarcel Moolenaar  */
1029956e45f6SSimon J. Gerraty static CondEvalResult
1030956e45f6SSimon J. Gerraty CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
1031956e45f6SSimon J. Gerraty 		    Boolean eprint, Boolean strictLHS)
10323955d011SMarcel Moolenaar {
1033956e45f6SSimon J. Gerraty     CondParser par;
1034*e2eeea75SSimon J. Gerraty     CondEvalResult rval;
10353955d011SMarcel Moolenaar 
103628a6bc81SSimon J. Gerraty     lhsStrict = strictLHS;
103728a6bc81SSimon J. Gerraty 
1038*e2eeea75SSimon J. Gerraty     cpp_skip_hspace(&cond);
10393955d011SMarcel Moolenaar 
1040*e2eeea75SSimon J. Gerraty     par.if_info = info != NULL ? info : ifs + PLAIN_IF_INDEX;
1041956e45f6SSimon J. Gerraty     par.p = cond;
1042956e45f6SSimon J. Gerraty     par.curr = TOK_NONE;
1043956e45f6SSimon J. Gerraty     par.printedError = FALSE;
10443955d011SMarcel Moolenaar 
1045956e45f6SSimon J. Gerraty     rval = CondParser_Eval(&par, value);
10463955d011SMarcel Moolenaar 
1047956e45f6SSimon J. Gerraty     if (rval == COND_INVALID && eprint && !par.printedError)
1048956e45f6SSimon J. Gerraty 	Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
10493955d011SMarcel Moolenaar 
10503955d011SMarcel Moolenaar     return rval;
10513955d011SMarcel Moolenaar }
10523955d011SMarcel Moolenaar 
1053*e2eeea75SSimon J. Gerraty /* Evaluate a condition in a :? modifier, such as
1054*e2eeea75SSimon J. Gerraty  * ${"${VAR}" == value:?yes:no}. */
1055956e45f6SSimon J. Gerraty CondEvalResult
1056956e45f6SSimon J. Gerraty Cond_EvalCondition(const char *cond, Boolean *out_value)
1057956e45f6SSimon J. Gerraty {
1058956e45f6SSimon J. Gerraty 	return CondEvalExpression(NULL, cond, out_value, FALSE, FALSE);
1059956e45f6SSimon J. Gerraty }
10603955d011SMarcel Moolenaar 
1061*e2eeea75SSimon J. Gerraty /* Evaluate the conditional directive in the line, which is one of:
10623955d011SMarcel Moolenaar  *
1063*e2eeea75SSimon J. Gerraty  *	.if <cond>
1064*e2eeea75SSimon J. Gerraty  *	.ifmake <cond>
1065*e2eeea75SSimon J. Gerraty  *	.ifnmake <cond>
1066*e2eeea75SSimon J. Gerraty  *	.ifdef <cond>
1067*e2eeea75SSimon J. Gerraty  *	.ifndef <cond>
1068*e2eeea75SSimon J. Gerraty  *	.elif <cond>
1069*e2eeea75SSimon J. Gerraty  *	.elifmake <cond>
1070*e2eeea75SSimon J. Gerraty  *	.elifnmake <cond>
1071*e2eeea75SSimon J. Gerraty  *	.elifdef <cond>
1072*e2eeea75SSimon J. Gerraty  *	.elifndef <cond>
1073*e2eeea75SSimon J. Gerraty  *	.else
1074*e2eeea75SSimon J. Gerraty  *	.endif
1075*e2eeea75SSimon J. Gerraty  *
1076*e2eeea75SSimon J. Gerraty  * In these directives, <cond> consists of &&, ||, !, function(arg),
1077*e2eeea75SSimon J. Gerraty  * comparisons, expressions, bare words, numbers and strings, and
1078*e2eeea75SSimon J. Gerraty  * parenthetical groupings thereof.
1079956e45f6SSimon J. Gerraty  *
1080956e45f6SSimon J. Gerraty  * Results:
1081*e2eeea75SSimon J. Gerraty  *	COND_PARSE	to continue parsing the lines that follow the
1082*e2eeea75SSimon J. Gerraty  *			conditional (when <cond> evaluates to TRUE)
1083956e45f6SSimon J. Gerraty  *	COND_SKIP	to skip the lines after the conditional
1084*e2eeea75SSimon J. Gerraty  *			(when <cond> evaluates to FALSE, or when a previous
1085956e45f6SSimon J. Gerraty  *			branch has already been taken)
1086956e45f6SSimon J. Gerraty  *	COND_INVALID	if the conditional was not valid, either because of
1087956e45f6SSimon J. Gerraty  *			a syntax error or because some variable was undefined
1088956e45f6SSimon J. Gerraty  *			or because the condition could not be evaluated
10893955d011SMarcel Moolenaar  */
10902c3632d1SSimon J. Gerraty CondEvalResult
1091*e2eeea75SSimon J. Gerraty Cond_EvalLine(const char *const line)
10923955d011SMarcel Moolenaar {
1093*e2eeea75SSimon J. Gerraty     typedef enum IfState {
1094*e2eeea75SSimon J. Gerraty 
1095*e2eeea75SSimon J. Gerraty 	/* None of the previous <cond> evaluated to TRUE. */
1096*e2eeea75SSimon J. Gerraty 	IFS_INITIAL	= 0,
1097*e2eeea75SSimon J. Gerraty 
1098*e2eeea75SSimon J. Gerraty 	/* The previous <cond> evaluated to TRUE.
1099*e2eeea75SSimon J. Gerraty 	 * The lines following this condition are interpreted. */
1100*e2eeea75SSimon J. Gerraty 	IFS_ACTIVE	= 1 << 0,
1101*e2eeea75SSimon J. Gerraty 
1102*e2eeea75SSimon J. Gerraty 	/* The previous directive was an '.else'. */
1103*e2eeea75SSimon J. Gerraty 	IFS_SEEN_ELSE	= 1 << 1,
1104*e2eeea75SSimon J. Gerraty 
1105*e2eeea75SSimon J. Gerraty 	/* One of the previous <cond> evaluated to TRUE. */
1106*e2eeea75SSimon J. Gerraty 	IFS_WAS_ACTIVE	= 1 << 2
1107*e2eeea75SSimon J. Gerraty 
1108*e2eeea75SSimon J. Gerraty     } IfState;
1109*e2eeea75SSimon J. Gerraty 
1110*e2eeea75SSimon J. Gerraty     static enum IfState *cond_states = NULL;
1111*e2eeea75SSimon J. Gerraty     static unsigned int cond_states_cap = 128;
11123955d011SMarcel Moolenaar 
11133955d011SMarcel Moolenaar     const struct If *ifp;
11143955d011SMarcel Moolenaar     Boolean isElif;
11153955d011SMarcel Moolenaar     Boolean value;
1116*e2eeea75SSimon J. Gerraty     IfState state;
1117*e2eeea75SSimon J. Gerraty     const char *p = line;
11183955d011SMarcel Moolenaar 
1119*e2eeea75SSimon J. Gerraty     if (cond_states == NULL) {
1120*e2eeea75SSimon J. Gerraty 	cond_states = bmake_malloc(cond_states_cap * sizeof *cond_states);
1121*e2eeea75SSimon J. Gerraty 	cond_states[0] = IFS_ACTIVE;
112259a02420SSimon J. Gerraty     }
11233955d011SMarcel Moolenaar 
1124*e2eeea75SSimon J. Gerraty     p++;		/* skip the leading '.' */
1125*e2eeea75SSimon J. Gerraty     cpp_skip_hspace(&p);
1126*e2eeea75SSimon J. Gerraty 
1127*e2eeea75SSimon J. Gerraty     /* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
1128*e2eeea75SSimon J. Gerraty     if (p[0] == 'e') {
1129*e2eeea75SSimon J. Gerraty 	if (p[1] != 'l') {
1130*e2eeea75SSimon J. Gerraty 	    if (!is_token(p + 1, "ndif", 4)) {
1131*e2eeea75SSimon J. Gerraty 		/* Unknown directive.  It might still be a transformation
1132*e2eeea75SSimon J. Gerraty 		 * rule like '.elisp.scm', therefore no error message here. */
11333955d011SMarcel Moolenaar 		return COND_INVALID;
1134*e2eeea75SSimon J. Gerraty 	    }
1135*e2eeea75SSimon J. Gerraty 
1136*e2eeea75SSimon J. Gerraty 	    /* It is an '.endif'. */
1137*e2eeea75SSimon J. Gerraty 	    /* TODO: check for extraneous <cond> */
1138*e2eeea75SSimon J. Gerraty 
11393955d011SMarcel Moolenaar 	    if (cond_depth == cond_min_depth) {
1140956e45f6SSimon J. Gerraty 		Parse_Error(PARSE_FATAL, "if-less endif");
11413955d011SMarcel Moolenaar 		return COND_PARSE;
11423955d011SMarcel Moolenaar 	    }
1143*e2eeea75SSimon J. Gerraty 
11443955d011SMarcel Moolenaar 	    /* Return state for previous conditional */
11453955d011SMarcel Moolenaar 	    cond_depth--;
1146*e2eeea75SSimon J. Gerraty 	    return cond_states[cond_depth] & IFS_ACTIVE
11472c3632d1SSimon J. Gerraty 		   ? COND_PARSE : COND_SKIP;
11483955d011SMarcel Moolenaar 	}
11493955d011SMarcel Moolenaar 
11503955d011SMarcel Moolenaar 	/* Quite likely this is 'else' or 'elif' */
1151*e2eeea75SSimon J. Gerraty 	p += 2;
1152*e2eeea75SSimon J. Gerraty 	if (is_token(p, "se", 2)) {	/* It is an 'else'. */
1153*e2eeea75SSimon J. Gerraty 
1154*e2eeea75SSimon J. Gerraty 	    if (opts.lint && p[2] != '\0')
1155*e2eeea75SSimon J. Gerraty 		Parse_Error(PARSE_FATAL,
1156*e2eeea75SSimon J. Gerraty 			    "The .else directive does not take arguments.");
1157*e2eeea75SSimon J. Gerraty 
11583955d011SMarcel Moolenaar 	    if (cond_depth == cond_min_depth) {
1159956e45f6SSimon J. Gerraty 		Parse_Error(PARSE_FATAL, "if-less else");
11603955d011SMarcel Moolenaar 		return COND_PARSE;
11613955d011SMarcel Moolenaar 	    }
11623955d011SMarcel Moolenaar 
1163*e2eeea75SSimon J. Gerraty 	    state = cond_states[cond_depth];
1164*e2eeea75SSimon J. Gerraty 	    if (state == IFS_INITIAL) {
1165*e2eeea75SSimon J. Gerraty 		state = IFS_ACTIVE | IFS_SEEN_ELSE;
1166*e2eeea75SSimon J. Gerraty 	    } else {
1167*e2eeea75SSimon J. Gerraty 		if (state & IFS_SEEN_ELSE)
11683955d011SMarcel Moolenaar 		    Parse_Error(PARSE_WARNING, "extra else");
1169*e2eeea75SSimon J. Gerraty 		state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
11703955d011SMarcel Moolenaar 	    }
1171*e2eeea75SSimon J. Gerraty 	    cond_states[cond_depth] = state;
1172*e2eeea75SSimon J. Gerraty 
1173*e2eeea75SSimon J. Gerraty 	    return state & IFS_ACTIVE ? COND_PARSE : COND_SKIP;
11743955d011SMarcel Moolenaar 	}
11753955d011SMarcel Moolenaar 	/* Assume for now it is an elif */
11763955d011SMarcel Moolenaar 	isElif = TRUE;
11773955d011SMarcel Moolenaar     } else
11783955d011SMarcel Moolenaar 	isElif = FALSE;
11793955d011SMarcel Moolenaar 
1180*e2eeea75SSimon J. Gerraty     if (p[0] != 'i' || p[1] != 'f') {
1181*e2eeea75SSimon J. Gerraty 	/* Unknown directive.  It might still be a transformation rule like
1182*e2eeea75SSimon J. Gerraty 	 * '.elisp.scm', therefore no error message here. */
1183*e2eeea75SSimon J. Gerraty 	return COND_INVALID;	/* Not an ifxxx or elifxxx line */
1184*e2eeea75SSimon J. Gerraty     }
11853955d011SMarcel Moolenaar 
11863955d011SMarcel Moolenaar     /*
11873955d011SMarcel Moolenaar      * Figure out what sort of conditional it is -- what its default
11883955d011SMarcel Moolenaar      * function is, etc. -- by looking in the table of valid "ifs"
11893955d011SMarcel Moolenaar      */
1190*e2eeea75SSimon J. Gerraty     p += 2;
11913955d011SMarcel Moolenaar     for (ifp = ifs;; ifp++) {
1192*e2eeea75SSimon J. Gerraty 	if (ifp->form == NULL) {
1193*e2eeea75SSimon J. Gerraty 	    /* TODO: Add error message about unknown directive,
1194*e2eeea75SSimon J. Gerraty 	     * since there is no other known directive that starts with 'el'
1195*e2eeea75SSimon J. Gerraty 	     * or 'if'.
1196*e2eeea75SSimon J. Gerraty 	     * Example: .elifx 123 */
11973955d011SMarcel Moolenaar 	    return COND_INVALID;
1198*e2eeea75SSimon J. Gerraty 	}
1199*e2eeea75SSimon J. Gerraty 	if (is_token(p, ifp->form, ifp->formlen)) {
1200*e2eeea75SSimon J. Gerraty 	    p += ifp->formlen;
12013955d011SMarcel Moolenaar 	    break;
12023955d011SMarcel Moolenaar 	}
12033955d011SMarcel Moolenaar     }
12043955d011SMarcel Moolenaar 
12053955d011SMarcel Moolenaar     /* Now we know what sort of 'if' it is... */
12063955d011SMarcel Moolenaar 
12073955d011SMarcel Moolenaar     if (isElif) {
12083955d011SMarcel Moolenaar 	if (cond_depth == cond_min_depth) {
1209956e45f6SSimon J. Gerraty 	    Parse_Error(PARSE_FATAL, "if-less elif");
12103955d011SMarcel Moolenaar 	    return COND_PARSE;
12113955d011SMarcel Moolenaar 	}
1212*e2eeea75SSimon J. Gerraty 	state = cond_states[cond_depth];
1213*e2eeea75SSimon J. Gerraty 	if (state & IFS_SEEN_ELSE) {
12143955d011SMarcel Moolenaar 	    Parse_Error(PARSE_WARNING, "extra elif");
1215*e2eeea75SSimon J. Gerraty 	    cond_states[cond_depth] = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
12163955d011SMarcel Moolenaar 	    return COND_SKIP;
12173955d011SMarcel Moolenaar 	}
1218*e2eeea75SSimon J. Gerraty 	if (state != IFS_INITIAL) {
1219*e2eeea75SSimon J. Gerraty 	    cond_states[cond_depth] = IFS_WAS_ACTIVE;
12203955d011SMarcel Moolenaar 	    return COND_SKIP;
12213955d011SMarcel Moolenaar 	}
12223955d011SMarcel Moolenaar     } else {
12233955d011SMarcel Moolenaar 	/* Normal .if */
1224*e2eeea75SSimon J. Gerraty 	if (cond_depth + 1 >= cond_states_cap) {
122559a02420SSimon J. Gerraty 	    /*
122659a02420SSimon J. Gerraty 	     * This is rare, but not impossible.
122759a02420SSimon J. Gerraty 	     * In meta mode, dirdeps.mk (only runs at level 0)
122859a02420SSimon J. Gerraty 	     * can need more than the default.
122959a02420SSimon J. Gerraty 	     */
1230*e2eeea75SSimon J. Gerraty 	    cond_states_cap += 32;
1231*e2eeea75SSimon J. Gerraty 	    cond_states = bmake_realloc(cond_states,
1232*e2eeea75SSimon J. Gerraty 					cond_states_cap * sizeof *cond_states);
12333955d011SMarcel Moolenaar 	}
1234*e2eeea75SSimon J. Gerraty 	state = cond_states[cond_depth];
12353955d011SMarcel Moolenaar 	cond_depth++;
1236*e2eeea75SSimon J. Gerraty 	if (!(state & IFS_ACTIVE)) {
12373955d011SMarcel Moolenaar 	    /* If we aren't parsing the data, treat as always false */
1238*e2eeea75SSimon J. Gerraty 	    cond_states[cond_depth] = IFS_WAS_ACTIVE;
12393955d011SMarcel Moolenaar 	    return COND_SKIP;
12403955d011SMarcel Moolenaar 	}
12413955d011SMarcel Moolenaar     }
12423955d011SMarcel Moolenaar 
1243956e45f6SSimon J. Gerraty     /* And evaluate the conditional expression */
1244*e2eeea75SSimon J. Gerraty     if (CondEvalExpression(ifp, p, &value, TRUE, TRUE) == COND_INVALID) {
12453955d011SMarcel Moolenaar 	/* Syntax error in conditional, error message already output. */
12463955d011SMarcel Moolenaar 	/* Skip everything to matching .endif */
1247*e2eeea75SSimon J. Gerraty 	/* XXX: An extra '.else' is not detected in this case. */
1248*e2eeea75SSimon J. Gerraty 	cond_states[cond_depth] = IFS_WAS_ACTIVE;
12493955d011SMarcel Moolenaar 	return COND_SKIP;
12503955d011SMarcel Moolenaar     }
12513955d011SMarcel Moolenaar 
12523955d011SMarcel Moolenaar     if (!value) {
1253*e2eeea75SSimon J. Gerraty 	cond_states[cond_depth] = IFS_INITIAL;
12543955d011SMarcel Moolenaar 	return COND_SKIP;
12553955d011SMarcel Moolenaar     }
1256*e2eeea75SSimon J. Gerraty     cond_states[cond_depth] = IFS_ACTIVE;
12573955d011SMarcel Moolenaar     return COND_PARSE;
12583955d011SMarcel Moolenaar }
12593955d011SMarcel Moolenaar 
12603955d011SMarcel Moolenaar void
12613955d011SMarcel Moolenaar Cond_restore_depth(unsigned int saved_depth)
12623955d011SMarcel Moolenaar {
1263956e45f6SSimon J. Gerraty     unsigned int open_conds = cond_depth - cond_min_depth;
12643955d011SMarcel Moolenaar 
12653955d011SMarcel Moolenaar     if (open_conds != 0 || saved_depth > cond_depth) {
1266956e45f6SSimon J. Gerraty 	Parse_Error(PARSE_FATAL, "%u open conditional%s", open_conds,
12673955d011SMarcel Moolenaar 		    open_conds == 1 ? "" : "s");
12683955d011SMarcel Moolenaar 	cond_depth = cond_min_depth;
12693955d011SMarcel Moolenaar     }
12703955d011SMarcel Moolenaar 
12713955d011SMarcel Moolenaar     cond_min_depth = saved_depth;
12723955d011SMarcel Moolenaar }
12733955d011SMarcel Moolenaar 
12743955d011SMarcel Moolenaar unsigned int
12753955d011SMarcel Moolenaar Cond_save_depth(void)
12763955d011SMarcel Moolenaar {
1277956e45f6SSimon J. Gerraty     unsigned int depth = cond_min_depth;
12783955d011SMarcel Moolenaar 
12793955d011SMarcel Moolenaar     cond_min_depth = cond_depth;
12803955d011SMarcel Moolenaar     return depth;
12813955d011SMarcel Moolenaar }
1282