1319884Ssjg/*	$NetBSD: cond.c,v 1.75 2017/04/16 20:59:04 riastradh Exp $	*/
2236769Sobrien
3236769Sobrien/*
4236769Sobrien * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
5236769Sobrien * All rights reserved.
6236769Sobrien *
7236769Sobrien * This code is derived from software contributed to Berkeley by
8236769Sobrien * Adam de Boor.
9236769Sobrien *
10236769Sobrien * Redistribution and use in source and binary forms, with or without
11236769Sobrien * modification, are permitted provided that the following conditions
12236769Sobrien * are met:
13236769Sobrien * 1. Redistributions of source code must retain the above copyright
14236769Sobrien *    notice, this list of conditions and the following disclaimer.
15236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright
16236769Sobrien *    notice, this list of conditions and the following disclaimer in the
17236769Sobrien *    documentation and/or other materials provided with the distribution.
18236769Sobrien * 3. Neither the name of the University nor the names of its contributors
19236769Sobrien *    may be used to endorse or promote products derived from this software
20236769Sobrien *    without specific prior written permission.
21236769Sobrien *
22236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23236769Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24236769Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25236769Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26236769Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27236769Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28236769Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29236769Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30236769Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31236769Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32236769Sobrien * SUCH DAMAGE.
33236769Sobrien */
34236769Sobrien
35236769Sobrien/*
36236769Sobrien * Copyright (c) 1988, 1989 by Adam de Boor
37236769Sobrien * Copyright (c) 1989 by Berkeley Softworks
38236769Sobrien * All rights reserved.
39236769Sobrien *
40236769Sobrien * This code is derived from software contributed to Berkeley by
41236769Sobrien * Adam de Boor.
42236769Sobrien *
43236769Sobrien * Redistribution and use in source and binary forms, with or without
44236769Sobrien * modification, are permitted provided that the following conditions
45236769Sobrien * are met:
46236769Sobrien * 1. Redistributions of source code must retain the above copyright
47236769Sobrien *    notice, this list of conditions and the following disclaimer.
48236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright
49236769Sobrien *    notice, this list of conditions and the following disclaimer in the
50236769Sobrien *    documentation and/or other materials provided with the distribution.
51236769Sobrien * 3. All advertising materials mentioning features or use of this software
52236769Sobrien *    must display the following acknowledgement:
53236769Sobrien *	This product includes software developed by the University of
54236769Sobrien *	California, Berkeley and its contributors.
55236769Sobrien * 4. Neither the name of the University nor the names of its contributors
56236769Sobrien *    may be used to endorse or promote products derived from this software
57236769Sobrien *    without specific prior written permission.
58236769Sobrien *
59236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60236769Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61236769Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62236769Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63236769Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64236769Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65236769Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66236769Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67236769Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68236769Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69236769Sobrien * SUCH DAMAGE.
70236769Sobrien */
71236769Sobrien
72236769Sobrien#ifndef MAKE_NATIVE
73319884Ssjgstatic char rcsid[] = "$NetBSD: cond.c,v 1.75 2017/04/16 20:59:04 riastradh Exp $";
74236769Sobrien#else
75236769Sobrien#include <sys/cdefs.h>
76236769Sobrien#ifndef lint
77236769Sobrien#if 0
78236769Sobrienstatic char sccsid[] = "@(#)cond.c	8.2 (Berkeley) 1/2/94";
79236769Sobrien#else
80319884Ssjg__RCSID("$NetBSD: cond.c,v 1.75 2017/04/16 20:59:04 riastradh Exp $");
81236769Sobrien#endif
82236769Sobrien#endif /* not lint */
83236769Sobrien#endif
84236769Sobrien
85236769Sobrien/*-
86236769Sobrien * cond.c --
87236769Sobrien *	Functions to handle conditionals in a makefile.
88236769Sobrien *
89236769Sobrien * Interface:
90236769Sobrien *	Cond_Eval 	Evaluate the conditional in the passed line.
91236769Sobrien *
92236769Sobrien */
93236769Sobrien
94319884Ssjg#include    <assert.h>
95236769Sobrien#include    <ctype.h>
96236769Sobrien#include    <errno.h>    /* For strtoul() error checking */
97236769Sobrien
98236769Sobrien#include    "make.h"
99236769Sobrien#include    "hash.h"
100236769Sobrien#include    "dir.h"
101236769Sobrien#include    "buf.h"
102236769Sobrien
103236769Sobrien/*
104236769Sobrien * The parsing of conditional expressions is based on this grammar:
105236769Sobrien *	E -> F || E
106236769Sobrien *	E -> F
107236769Sobrien *	F -> T && F
108236769Sobrien *	F -> T
109236769Sobrien *	T -> defined(variable)
110236769Sobrien *	T -> make(target)
111236769Sobrien *	T -> exists(file)
112236769Sobrien *	T -> empty(varspec)
113236769Sobrien *	T -> target(name)
114236769Sobrien *	T -> commands(name)
115236769Sobrien *	T -> symbol
116236769Sobrien *	T -> $(varspec) op value
117236769Sobrien *	T -> $(varspec) == "string"
118236769Sobrien *	T -> $(varspec) != "string"
119236769Sobrien *	T -> "string"
120236769Sobrien *	T -> ( E )
121236769Sobrien *	T -> ! T
122236769Sobrien *	op -> == | != | > | < | >= | <=
123236769Sobrien *
124236769Sobrien * 'symbol' is some other symbol to which the default function (condDefProc)
125236769Sobrien * is applied.
126236769Sobrien *
127236769Sobrien * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
128236769Sobrien * will return TOK_AND for '&' and '&&', TOK_OR for '|' and '||',
129236769Sobrien * TOK_NOT for '!', TOK_LPAREN for '(', TOK_RPAREN for ')' and will evaluate
130236769Sobrien * the other terminal symbols, using either the default function or the
131236769Sobrien * function given in the terminal, and return the result as either TOK_TRUE
132236769Sobrien * or TOK_FALSE.
133236769Sobrien *
134236769Sobrien * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons.
135236769Sobrien *
136236769Sobrien * All Non-Terminal functions (CondE, CondF and CondT) return TOK_ERROR on
137236769Sobrien * error.
138236769Sobrien */
139236769Sobrientypedef enum {
140236769Sobrien    TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT,
141236769Sobrien    TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
142236769Sobrien} Token;
143236769Sobrien
144236769Sobrien/*-
145236769Sobrien * Structures to handle elegantly the different forms of #if's. The
146236769Sobrien * last two fields are stored in condInvert and condDefProc, respectively.
147236769Sobrien */
148236769Sobrienstatic void CondPushBack(Token);
149236769Sobrienstatic int CondGetArg(char **, char **, const char *);
150236769Sobrienstatic Boolean CondDoDefined(int, const char *);
151236769Sobrienstatic int CondStrMatch(const void *, const void *);
152236769Sobrienstatic Boolean CondDoMake(int, const char *);
153236769Sobrienstatic Boolean CondDoExists(int, const char *);
154236769Sobrienstatic Boolean CondDoTarget(int, const char *);
155236769Sobrienstatic Boolean CondDoCommands(int, const char *);
156236769Sobrienstatic Boolean CondCvtArg(char *, double *);
157236769Sobrienstatic Token CondToken(Boolean);
158236769Sobrienstatic Token CondT(Boolean);
159236769Sobrienstatic Token CondF(Boolean);
160236769Sobrienstatic Token CondE(Boolean);
161236769Sobrienstatic int do_Cond_EvalExpression(Boolean *);
162236769Sobrien
163236769Sobrienstatic const struct If {
164236769Sobrien    const char	*form;	      /* Form of if */
165236769Sobrien    int		formlen;      /* Length of form */
166236769Sobrien    Boolean	doNot;	      /* TRUE if default function should be negated */
167236769Sobrien    Boolean	(*defProc)(int, const char *); /* Default function to apply */
168236769Sobrien} ifs[] = {
169236769Sobrien    { "def",	  3,	  FALSE,  CondDoDefined },
170236769Sobrien    { "ndef",	  4,	  TRUE,	  CondDoDefined },
171236769Sobrien    { "make",	  4,	  FALSE,  CondDoMake },
172236769Sobrien    { "nmake",	  5,	  TRUE,	  CondDoMake },
173236769Sobrien    { "",	  0,	  FALSE,  CondDoDefined },
174236769Sobrien    { NULL,	  0,	  FALSE,  NULL }
175236769Sobrien};
176236769Sobrien
177236769Sobrienstatic const struct If *if_info;        /* Info for current statement */
178236769Sobrienstatic char 	  *condExpr;	    	/* The expression to parse */
179236769Sobrienstatic Token	  condPushBack=TOK_NONE;	/* Single push-back token used in
180236769Sobrien					 * parsing */
181236769Sobrien
182236769Sobrienstatic unsigned int	cond_depth = 0;  	/* current .if nesting level */
183236769Sobrienstatic unsigned int	cond_min_depth = 0;  	/* depth at makefile open */
184236769Sobrien
185282740Ssjg/*
186282740Ssjg * Indicate when we should be strict about lhs of comparisons.
187282740Ssjg * TRUE when Cond_EvalExpression is called from Cond_Eval (.if etc)
188282740Ssjg * FALSE when Cond_EvalExpression is called from var.c:ApplyModifiers
189282740Ssjg * since lhs is already expanded and we cannot tell if
190282740Ssjg * it was a variable reference or not.
191282740Ssjg */
192282740Ssjgstatic Boolean lhsStrict;
193282740Ssjg
194236769Sobrienstatic int
195236769Sobrienistoken(const char *str, const char *tok, size_t len)
196236769Sobrien{
197236769Sobrien	return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]);
198236769Sobrien}
199236769Sobrien
200236769Sobrien/*-
201236769Sobrien *-----------------------------------------------------------------------
202236769Sobrien * CondPushBack --
203236769Sobrien *	Push back the most recent token read. We only need one level of
204236769Sobrien *	this, so the thing is just stored in 'condPushback'.
205236769Sobrien *
206236769Sobrien * Input:
207236769Sobrien *	t		Token to push back into the "stream"
208236769Sobrien *
209236769Sobrien * Results:
210236769Sobrien *	None.
211236769Sobrien *
212236769Sobrien * Side Effects:
213236769Sobrien *	condPushback is overwritten.
214236769Sobrien *
215236769Sobrien *-----------------------------------------------------------------------
216236769Sobrien */
217236769Sobrienstatic void
218236769SobrienCondPushBack(Token t)
219236769Sobrien{
220236769Sobrien    condPushBack = t;
221236769Sobrien}
222236769Sobrien
223236769Sobrien/*-
224236769Sobrien *-----------------------------------------------------------------------
225236769Sobrien * CondGetArg --
226236769Sobrien *	Find the argument of a built-in function.
227236769Sobrien *
228236769Sobrien * Input:
229236769Sobrien *	parens		TRUE if arg should be bounded by parens
230236769Sobrien *
231236769Sobrien * Results:
232236769Sobrien *	The length of the argument and the address of the argument.
233236769Sobrien *
234236769Sobrien * Side Effects:
235236769Sobrien *	The pointer is set to point to the closing parenthesis of the
236236769Sobrien *	function call.
237236769Sobrien *
238236769Sobrien *-----------------------------------------------------------------------
239236769Sobrien */
240236769Sobrienstatic int
241236769SobrienCondGetArg(char **linePtr, char **argPtr, const char *func)
242236769Sobrien{
243236769Sobrien    char	  *cp;
244236769Sobrien    int	    	  argLen;
245236769Sobrien    Buffer	  buf;
246236769Sobrien    int           paren_depth;
247236769Sobrien    char          ch;
248236769Sobrien
249236769Sobrien    cp = *linePtr;
250236769Sobrien    if (func != NULL)
251236769Sobrien	/* Skip opening '(' - verfied by caller */
252236769Sobrien	cp++;
253236769Sobrien
254236769Sobrien    if (*cp == '\0') {
255236769Sobrien	/*
256236769Sobrien	 * No arguments whatsoever. Because 'make' and 'defined' aren't really
257236769Sobrien	 * "reserved words", we don't print a message. I think this is better
258236769Sobrien	 * than hitting the user with a warning message every time s/he uses
259236769Sobrien	 * the word 'make' or 'defined' at the beginning of a symbol...
260236769Sobrien	 */
261236769Sobrien	*argPtr = NULL;
262236769Sobrien	return (0);
263236769Sobrien    }
264236769Sobrien
265236769Sobrien    while (*cp == ' ' || *cp == '\t') {
266236769Sobrien	cp++;
267236769Sobrien    }
268236769Sobrien
269236769Sobrien    /*
270236769Sobrien     * Create a buffer for the argument and start it out at 16 characters
271236769Sobrien     * long. Why 16? Why not?
272236769Sobrien     */
273236769Sobrien    Buf_Init(&buf, 16);
274236769Sobrien
275236769Sobrien    paren_depth = 0;
276236769Sobrien    for (;;) {
277236769Sobrien	ch = *cp;
278236769Sobrien	if (ch == 0 || ch == ' ' || ch == '\t')
279236769Sobrien	    break;
280236769Sobrien	if ((ch == '&' || ch == '|') && paren_depth == 0)
281236769Sobrien	    break;
282236769Sobrien	if (*cp == '$') {
283236769Sobrien	    /*
284236769Sobrien	     * Parse the variable spec and install it as part of the argument
285236769Sobrien	     * if it's valid. We tell Var_Parse to complain on an undefined
286236769Sobrien	     * variable, so we don't do it too. Nor do we return an error,
287236769Sobrien	     * though perhaps we should...
288236769Sobrien	     */
289236769Sobrien	    char  	*cp2;
290236769Sobrien	    int		len;
291236769Sobrien	    void	*freeIt;
292236769Sobrien
293296637Ssjg	    cp2 = Var_Parse(cp, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES,
294296637Ssjg			    &len, &freeIt);
295236769Sobrien	    Buf_AddBytes(&buf, strlen(cp2), cp2);
296296637Ssjg	    free(freeIt);
297236769Sobrien	    cp += len;
298236769Sobrien	    continue;
299236769Sobrien	}
300236769Sobrien	if (ch == '(')
301236769Sobrien	    paren_depth++;
302236769Sobrien	else
303236769Sobrien	    if (ch == ')' && --paren_depth < 0)
304236769Sobrien		break;
305236769Sobrien	Buf_AddByte(&buf, *cp);
306236769Sobrien	cp++;
307236769Sobrien    }
308236769Sobrien
309236769Sobrien    *argPtr = Buf_GetAll(&buf, &argLen);
310236769Sobrien    Buf_Destroy(&buf, FALSE);
311236769Sobrien
312236769Sobrien    while (*cp == ' ' || *cp == '\t') {
313236769Sobrien	cp++;
314236769Sobrien    }
315236769Sobrien
316236769Sobrien    if (func != NULL && *cp++ != ')') {
317236769Sobrien	Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
318236769Sobrien		     func);
319236769Sobrien	return (0);
320236769Sobrien    }
321236769Sobrien
322236769Sobrien    *linePtr = cp;
323236769Sobrien    return (argLen);
324236769Sobrien}
325236769Sobrien
326236769Sobrien/*-
327236769Sobrien *-----------------------------------------------------------------------
328236769Sobrien * CondDoDefined --
329236769Sobrien *	Handle the 'defined' function for conditionals.
330236769Sobrien *
331236769Sobrien * Results:
332236769Sobrien *	TRUE if the given variable is defined.
333236769Sobrien *
334236769Sobrien * Side Effects:
335236769Sobrien *	None.
336236769Sobrien *
337236769Sobrien *-----------------------------------------------------------------------
338236769Sobrien */
339236769Sobrienstatic Boolean
340237578SobrienCondDoDefined(int argLen MAKE_ATTR_UNUSED, const char *arg)
341236769Sobrien{
342236769Sobrien    char    *p1;
343236769Sobrien    Boolean result;
344236769Sobrien
345236769Sobrien    if (Var_Value(arg, VAR_CMD, &p1) != NULL) {
346236769Sobrien	result = TRUE;
347236769Sobrien    } else {
348236769Sobrien	result = FALSE;
349236769Sobrien    }
350296637Ssjg
351296637Ssjg    free(p1);
352236769Sobrien    return (result);
353236769Sobrien}
354236769Sobrien
355236769Sobrien/*-
356236769Sobrien *-----------------------------------------------------------------------
357236769Sobrien * CondStrMatch --
358236769Sobrien *	Front-end for Str_Match so it returns 0 on match and non-zero
359236769Sobrien *	on mismatch. Callback function for CondDoMake via Lst_Find
360236769Sobrien *
361236769Sobrien * Results:
362236769Sobrien *	0 if string matches pattern
363236769Sobrien *
364236769Sobrien * Side Effects:
365236769Sobrien *	None
366236769Sobrien *
367236769Sobrien *-----------------------------------------------------------------------
368236769Sobrien */
369236769Sobrienstatic int
370236769SobrienCondStrMatch(const void *string, const void *pattern)
371236769Sobrien{
372236769Sobrien    return(!Str_Match(string, pattern));
373236769Sobrien}
374236769Sobrien
375236769Sobrien/*-
376236769Sobrien *-----------------------------------------------------------------------
377236769Sobrien * CondDoMake --
378236769Sobrien *	Handle the 'make' function for conditionals.
379236769Sobrien *
380236769Sobrien * Results:
381236769Sobrien *	TRUE if the given target is being made.
382236769Sobrien *
383236769Sobrien * Side Effects:
384236769Sobrien *	None.
385236769Sobrien *
386236769Sobrien *-----------------------------------------------------------------------
387236769Sobrien */
388236769Sobrienstatic Boolean
389237578SobrienCondDoMake(int argLen MAKE_ATTR_UNUSED, const char *arg)
390236769Sobrien{
391236769Sobrien    return Lst_Find(create, arg, CondStrMatch) != NULL;
392236769Sobrien}
393236769Sobrien
394236769Sobrien/*-
395236769Sobrien *-----------------------------------------------------------------------
396236769Sobrien * CondDoExists --
397236769Sobrien *	See if the given file exists.
398236769Sobrien *
399236769Sobrien * Results:
400236769Sobrien *	TRUE if the file exists and FALSE if it does not.
401236769Sobrien *
402236769Sobrien * Side Effects:
403236769Sobrien *	None.
404236769Sobrien *
405236769Sobrien *-----------------------------------------------------------------------
406236769Sobrien */
407236769Sobrienstatic Boolean
408237578SobrienCondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg)
409236769Sobrien{
410236769Sobrien    Boolean result;
411236769Sobrien    char    *path;
412236769Sobrien
413236769Sobrien    path = Dir_FindFile(arg, dirSearchPath);
414236769Sobrien    if (DEBUG(COND)) {
415236769Sobrien	fprintf(debug_file, "exists(%s) result is \"%s\"\n",
416236769Sobrien	       arg, path ? path : "");
417236769Sobrien    }
418236769Sobrien    if (path != NULL) {
419236769Sobrien	result = TRUE;
420236769Sobrien	free(path);
421236769Sobrien    } else {
422236769Sobrien	result = FALSE;
423236769Sobrien    }
424236769Sobrien    return (result);
425236769Sobrien}
426236769Sobrien
427236769Sobrien/*-
428236769Sobrien *-----------------------------------------------------------------------
429236769Sobrien * CondDoTarget --
430236769Sobrien *	See if the given node exists and is an actual target.
431236769Sobrien *
432236769Sobrien * Results:
433236769Sobrien *	TRUE if the node exists as a target and FALSE if it does not.
434236769Sobrien *
435236769Sobrien * Side Effects:
436236769Sobrien *	None.
437236769Sobrien *
438236769Sobrien *-----------------------------------------------------------------------
439236769Sobrien */
440236769Sobrienstatic Boolean
441237578SobrienCondDoTarget(int argLen MAKE_ATTR_UNUSED, const char *arg)
442236769Sobrien{
443236769Sobrien    GNode   *gn;
444236769Sobrien
445236769Sobrien    gn = Targ_FindNode(arg, TARG_NOCREATE);
446236769Sobrien    return (gn != NULL) && !OP_NOP(gn->type);
447236769Sobrien}
448236769Sobrien
449236769Sobrien/*-
450236769Sobrien *-----------------------------------------------------------------------
451236769Sobrien * CondDoCommands --
452236769Sobrien *	See if the given node exists and is an actual target with commands
453236769Sobrien *	associated with it.
454236769Sobrien *
455236769Sobrien * Results:
456236769Sobrien *	TRUE if the node exists as a target and has commands associated with
457236769Sobrien *	it and FALSE if it does not.
458236769Sobrien *
459236769Sobrien * Side Effects:
460236769Sobrien *	None.
461236769Sobrien *
462236769Sobrien *-----------------------------------------------------------------------
463236769Sobrien */
464236769Sobrienstatic Boolean
465237578SobrienCondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg)
466236769Sobrien{
467236769Sobrien    GNode   *gn;
468236769Sobrien
469236769Sobrien    gn = Targ_FindNode(arg, TARG_NOCREATE);
470236769Sobrien    return (gn != NULL) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands);
471236769Sobrien}
472236769Sobrien
473236769Sobrien/*-
474236769Sobrien *-----------------------------------------------------------------------
475236769Sobrien * CondCvtArg --
476236769Sobrien *	Convert the given number into a double.
477236769Sobrien *	We try a base 10 or 16 integer conversion first, if that fails
478236769Sobrien *	then we try a floating point conversion instead.
479236769Sobrien *
480236769Sobrien * Results:
481236769Sobrien *	Sets 'value' to double value of string.
482236769Sobrien *	Returns 'true' if the convertion suceeded
483236769Sobrien *
484236769Sobrien *-----------------------------------------------------------------------
485236769Sobrien */
486236769Sobrienstatic Boolean
487236769SobrienCondCvtArg(char *str, double *value)
488236769Sobrien{
489236769Sobrien    char *eptr, ech;
490236769Sobrien    unsigned long l_val;
491236769Sobrien    double d_val;
492236769Sobrien
493236769Sobrien    errno = 0;
494291978Ssjg    if (!*str) {
495291978Ssjg	*value = (double)0;
496291978Ssjg	return TRUE;
497291978Ssjg    }
498236769Sobrien    l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10);
499236769Sobrien    ech = *eptr;
500236769Sobrien    if (ech == 0 && errno != ERANGE) {
501236769Sobrien	d_val = str[0] == '-' ? -(double)-l_val : (double)l_val;
502236769Sobrien    } else {
503236769Sobrien	if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E')
504236769Sobrien	    return FALSE;
505236769Sobrien	d_val = strtod(str, &eptr);
506236769Sobrien	if (*eptr)
507236769Sobrien	    return FALSE;
508236769Sobrien    }
509236769Sobrien
510236769Sobrien    *value = d_val;
511236769Sobrien    return TRUE;
512236769Sobrien}
513236769Sobrien
514236769Sobrien/*-
515236769Sobrien *-----------------------------------------------------------------------
516236769Sobrien * CondGetString --
517236769Sobrien *	Get a string from a variable reference or an optionally quoted
518236769Sobrien *	string.  This is called for the lhs and rhs of string compares.
519236769Sobrien *
520236769Sobrien * Results:
521236769Sobrien *	Sets freeIt if needed,
522236769Sobrien *	Sets quoted if string was quoted,
523236769Sobrien *	Returns NULL on error,
524236769Sobrien *	else returns string - absent any quotes.
525236769Sobrien *
526236769Sobrien * Side Effects:
527236769Sobrien *	Moves condExpr to end of this token.
528236769Sobrien *
529236769Sobrien *
530236769Sobrien *-----------------------------------------------------------------------
531236769Sobrien */
532236769Sobrien/* coverity:[+alloc : arg-*2] */
533236769Sobrienstatic char *
534282740SsjgCondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS)
535236769Sobrien{
536236769Sobrien    Buffer buf;
537236769Sobrien    char *cp;
538236769Sobrien    char *str;
539236769Sobrien    int	len;
540236769Sobrien    int qt;
541236769Sobrien    char *start;
542236769Sobrien
543236769Sobrien    Buf_Init(&buf, 0);
544236769Sobrien    str = NULL;
545236769Sobrien    *freeIt = NULL;
546236769Sobrien    *quoted = qt = *condExpr == '"' ? 1 : 0;
547236769Sobrien    if (qt)
548236769Sobrien	condExpr++;
549236769Sobrien    for (start = condExpr; *condExpr && str == NULL; condExpr++) {
550236769Sobrien	switch (*condExpr) {
551236769Sobrien	case '\\':
552236769Sobrien	    if (condExpr[1] != '\0') {
553236769Sobrien		condExpr++;
554236769Sobrien		Buf_AddByte(&buf, *condExpr);
555236769Sobrien	    }
556236769Sobrien	    break;
557236769Sobrien	case '"':
558236769Sobrien	    if (qt) {
559236769Sobrien		condExpr++;		/* we don't want the quotes */
560236769Sobrien		goto got_str;
561236769Sobrien	    } else
562236769Sobrien		Buf_AddByte(&buf, *condExpr); /* likely? */
563236769Sobrien	    break;
564236769Sobrien	case ')':
565236769Sobrien	case '!':
566236769Sobrien	case '=':
567236769Sobrien	case '>':
568236769Sobrien	case '<':
569236769Sobrien	case ' ':
570236769Sobrien	case '\t':
571236769Sobrien	    if (!qt)
572236769Sobrien		goto got_str;
573236769Sobrien	    else
574236769Sobrien		Buf_AddByte(&buf, *condExpr);
575236769Sobrien	    break;
576236769Sobrien	case '$':
577236769Sobrien	    /* if we are in quotes, then an undefined variable is ok */
578296637Ssjg	    str = Var_Parse(condExpr, VAR_CMD,
579296637Ssjg			    ((!qt && doEval) ? VARF_UNDEFERR : 0) |
580296637Ssjg			    VARF_WANTRES, &len, freeIt);
581236769Sobrien	    if (str == var_Error) {
582236769Sobrien		if (*freeIt) {
583236769Sobrien		    free(*freeIt);
584236769Sobrien		    *freeIt = NULL;
585236769Sobrien		}
586236769Sobrien		/*
587236769Sobrien		 * Even if !doEval, we still report syntax errors, which
588236769Sobrien		 * is what getting var_Error back with !doEval means.
589236769Sobrien		 */
590236769Sobrien		str = NULL;
591236769Sobrien		goto cleanup;
592236769Sobrien	    }
593236769Sobrien	    condExpr += len;
594236769Sobrien	    /*
595236769Sobrien	     * If the '$' was first char (no quotes), and we are
596236769Sobrien	     * followed by space, the operator or end of expression,
597236769Sobrien	     * we are done.
598236769Sobrien	     */
599236769Sobrien	    if ((condExpr == start + len) &&
600236769Sobrien		(*condExpr == '\0' ||
601236769Sobrien		 isspace((unsigned char) *condExpr) ||
602236769Sobrien		 strchr("!=><)", *condExpr))) {
603236769Sobrien		goto cleanup;
604236769Sobrien	    }
605236769Sobrien	    /*
606236769Sobrien	     * Nope, we better copy str to buf
607236769Sobrien	     */
608236769Sobrien	    for (cp = str; *cp; cp++) {
609236769Sobrien		Buf_AddByte(&buf, *cp);
610236769Sobrien	    }
611236769Sobrien	    if (*freeIt) {
612236769Sobrien		free(*freeIt);
613236769Sobrien		*freeIt = NULL;
614236769Sobrien	    }
615236769Sobrien	    str = NULL;			/* not finished yet */
616236769Sobrien	    condExpr--;			/* don't skip over next char */
617236769Sobrien	    break;
618236769Sobrien	default:
619282740Ssjg	    if (strictLHS && !qt && *start != '$' &&
620282740Ssjg		!isdigit((unsigned char) *start)) {
621282740Ssjg		/* lhs must be quoted, a variable reference or number */
622282740Ssjg		if (*freeIt) {
623282740Ssjg		    free(*freeIt);
624282740Ssjg		    *freeIt = NULL;
625282740Ssjg		}
626282740Ssjg		str = NULL;
627282740Ssjg		goto cleanup;
628282740Ssjg	    }
629236769Sobrien	    Buf_AddByte(&buf, *condExpr);
630236769Sobrien	    break;
631236769Sobrien	}
632236769Sobrien    }
633236769Sobrien got_str:
634236769Sobrien    str = Buf_GetAll(&buf, NULL);
635236769Sobrien    *freeIt = str;
636236769Sobrien cleanup:
637236769Sobrien    Buf_Destroy(&buf, FALSE);
638236769Sobrien    return str;
639236769Sobrien}
640236769Sobrien
641236769Sobrien/*-
642236769Sobrien *-----------------------------------------------------------------------
643236769Sobrien * CondToken --
644236769Sobrien *	Return the next token from the input.
645236769Sobrien *
646236769Sobrien * Results:
647236769Sobrien *	A Token for the next lexical token in the stream.
648236769Sobrien *
649236769Sobrien * Side Effects:
650236769Sobrien *	condPushback will be set back to TOK_NONE if it is used.
651236769Sobrien *
652236769Sobrien *-----------------------------------------------------------------------
653236769Sobrien */
654236769Sobrienstatic Token
655236769Sobriencompare_expression(Boolean doEval)
656236769Sobrien{
657236769Sobrien    Token	t;
658236769Sobrien    char	*lhs;
659236769Sobrien    char	*rhs;
660236769Sobrien    char	*op;
661236769Sobrien    void	*lhsFree;
662236769Sobrien    void	*rhsFree;
663236769Sobrien    Boolean lhsQuoted;
664236769Sobrien    Boolean rhsQuoted;
665236769Sobrien    double  	left, right;
666236769Sobrien
667236769Sobrien    t = TOK_ERROR;
668236769Sobrien    rhs = NULL;
669236769Sobrien    lhsFree = rhsFree = FALSE;
670236769Sobrien    lhsQuoted = rhsQuoted = FALSE;
671236769Sobrien
672236769Sobrien    /*
673236769Sobrien     * Parse the variable spec and skip over it, saving its
674236769Sobrien     * value in lhs.
675236769Sobrien     */
676282740Ssjg    lhs = CondGetString(doEval, &lhsQuoted, &lhsFree, lhsStrict);
677236769Sobrien    if (!lhs)
678236769Sobrien	goto done;
679236769Sobrien
680236769Sobrien    /*
681236769Sobrien     * Skip whitespace to get to the operator
682236769Sobrien     */
683236769Sobrien    while (isspace((unsigned char) *condExpr))
684236769Sobrien	condExpr++;
685236769Sobrien
686236769Sobrien    /*
687236769Sobrien     * Make sure the operator is a valid one. If it isn't a
688236769Sobrien     * known relational operator, pretend we got a
689236769Sobrien     * != 0 comparison.
690236769Sobrien     */
691236769Sobrien    op = condExpr;
692236769Sobrien    switch (*condExpr) {
693236769Sobrien	case '!':
694236769Sobrien	case '=':
695236769Sobrien	case '<':
696236769Sobrien	case '>':
697236769Sobrien	    if (condExpr[1] == '=') {
698236769Sobrien		condExpr += 2;
699236769Sobrien	    } else {
700236769Sobrien		condExpr += 1;
701236769Sobrien	    }
702236769Sobrien	    break;
703236769Sobrien	default:
704236769Sobrien	    if (!doEval) {
705236769Sobrien		t = TOK_FALSE;
706236769Sobrien		goto done;
707236769Sobrien	    }
708236769Sobrien	    /* For .ifxxx "..." check for non-empty string. */
709236769Sobrien	    if (lhsQuoted) {
710236769Sobrien		t = lhs[0] != 0;
711236769Sobrien		goto done;
712236769Sobrien	    }
713236769Sobrien	    /* For .ifxxx <number> compare against zero */
714236769Sobrien	    if (CondCvtArg(lhs, &left)) {
715236769Sobrien		t = left != 0.0;
716236769Sobrien		goto done;
717236769Sobrien	    }
718236769Sobrien	    /* For .if ${...} check for non-empty string (defProc is ifdef). */
719236769Sobrien	    if (if_info->form[0] == 0) {
720236769Sobrien		t = lhs[0] != 0;
721236769Sobrien		goto done;
722236769Sobrien	    }
723236769Sobrien	    /* Otherwise action default test ... */
724236769Sobrien	    t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot;
725236769Sobrien	    goto done;
726236769Sobrien    }
727236769Sobrien
728236769Sobrien    while (isspace((unsigned char)*condExpr))
729236769Sobrien	condExpr++;
730236769Sobrien
731236769Sobrien    if (*condExpr == '\0') {
732236769Sobrien	Parse_Error(PARSE_WARNING,
733236769Sobrien		    "Missing right-hand-side of operator");
734236769Sobrien	goto done;
735236769Sobrien    }
736236769Sobrien
737282740Ssjg    rhs = CondGetString(doEval, &rhsQuoted, &rhsFree, FALSE);
738236769Sobrien    if (!rhs)
739236769Sobrien	goto done;
740236769Sobrien
741236769Sobrien    if (rhsQuoted || lhsQuoted) {
742236769Sobriendo_string_compare:
743236769Sobrien	if (((*op != '!') && (*op != '=')) || (op[1] != '=')) {
744236769Sobrien	    Parse_Error(PARSE_WARNING,
745236769Sobrien    "String comparison operator should be either == or !=");
746236769Sobrien	    goto done;
747236769Sobrien	}
748236769Sobrien
749236769Sobrien	if (DEBUG(COND)) {
750236769Sobrien	    fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
751236769Sobrien		   lhs, rhs, op);
752236769Sobrien	}
753236769Sobrien	/*
754236769Sobrien	 * Null-terminate rhs and perform the comparison.
755236769Sobrien	 * t is set to the result.
756236769Sobrien	 */
757236769Sobrien	if (*op == '=') {
758236769Sobrien	    t = strcmp(lhs, rhs) == 0;
759236769Sobrien	} else {
760236769Sobrien	    t = strcmp(lhs, rhs) != 0;
761236769Sobrien	}
762236769Sobrien    } else {
763236769Sobrien	/*
764236769Sobrien	 * rhs is either a float or an integer. Convert both the
765236769Sobrien	 * lhs and the rhs to a double and compare the two.
766236769Sobrien	 */
767236769Sobrien
768236769Sobrien	if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right))
769236769Sobrien	    goto do_string_compare;
770236769Sobrien
771236769Sobrien	if (DEBUG(COND)) {
772236769Sobrien	    fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left,
773236769Sobrien		   right, op);
774236769Sobrien	}
775236769Sobrien	switch(op[0]) {
776236769Sobrien	case '!':
777236769Sobrien	    if (op[1] != '=') {
778236769Sobrien		Parse_Error(PARSE_WARNING,
779236769Sobrien			    "Unknown operator");
780236769Sobrien		goto done;
781236769Sobrien	    }
782236769Sobrien	    t = (left != right);
783236769Sobrien	    break;
784236769Sobrien	case '=':
785236769Sobrien	    if (op[1] != '=') {
786236769Sobrien		Parse_Error(PARSE_WARNING,
787236769Sobrien			    "Unknown operator");
788236769Sobrien		goto done;
789236769Sobrien	    }
790236769Sobrien	    t = (left == right);
791236769Sobrien	    break;
792236769Sobrien	case '<':
793236769Sobrien	    if (op[1] == '=') {
794236769Sobrien		t = (left <= right);
795236769Sobrien	    } else {
796236769Sobrien		t = (left < right);
797236769Sobrien	    }
798236769Sobrien	    break;
799236769Sobrien	case '>':
800236769Sobrien	    if (op[1] == '=') {
801236769Sobrien		t = (left >= right);
802236769Sobrien	    } else {
803236769Sobrien		t = (left > right);
804236769Sobrien	    }
805236769Sobrien	    break;
806236769Sobrien	}
807236769Sobrien    }
808236769Sobrien
809236769Sobriendone:
810296637Ssjg    free(lhsFree);
811296637Ssjg    free(rhsFree);
812236769Sobrien    return t;
813236769Sobrien}
814236769Sobrien
815236769Sobrienstatic int
816237578Sobrienget_mpt_arg(char **linePtr, char **argPtr, const char *func MAKE_ATTR_UNUSED)
817236769Sobrien{
818236769Sobrien    /*
819236769Sobrien     * Use Var_Parse to parse the spec in parens and return
820236769Sobrien     * TOK_TRUE if the resulting string is empty.
821236769Sobrien     */
822236769Sobrien    int	    length;
823236769Sobrien    void    *freeIt;
824236769Sobrien    char    *val;
825236769Sobrien    char    *cp = *linePtr;
826236769Sobrien
827236769Sobrien    /* We do all the work here and return the result as the length */
828236769Sobrien    *argPtr = NULL;
829236769Sobrien
830296637Ssjg    val = Var_Parse(cp - 1, VAR_CMD, VARF_WANTRES, &length, &freeIt);
831236769Sobrien    /*
832236769Sobrien     * Advance *linePtr to beyond the closing ). Note that
833236769Sobrien     * we subtract one because 'length' is calculated from 'cp - 1'.
834236769Sobrien     */
835236769Sobrien    *linePtr = cp - 1 + length;
836236769Sobrien
837236769Sobrien    if (val == var_Error) {
838236769Sobrien	free(freeIt);
839236769Sobrien	return -1;
840236769Sobrien    }
841236769Sobrien
842236769Sobrien    /* A variable is empty when it just contains spaces... 4/15/92, christos */
843236769Sobrien    while (isspace(*(unsigned char *)val))
844236769Sobrien	val++;
845236769Sobrien
846236769Sobrien    /*
847236769Sobrien     * For consistency with the other functions we can't generate the
848236769Sobrien     * true/false here.
849236769Sobrien     */
850236769Sobrien    length = *val ? 2 : 1;
851296637Ssjg    free(freeIt);
852236769Sobrien    return length;
853236769Sobrien}
854236769Sobrien
855236769Sobrienstatic Boolean
856237578SobrienCondDoEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED)
857236769Sobrien{
858236769Sobrien    return arglen == 1;
859236769Sobrien}
860236769Sobrien
861236769Sobrienstatic Token
862236769Sobriencompare_function(Boolean doEval)
863236769Sobrien{
864236769Sobrien    static const struct fn_def {
865236769Sobrien	const char  *fn_name;
866236769Sobrien	int         fn_name_len;
867236769Sobrien        int         (*fn_getarg)(char **, char **, const char *);
868236769Sobrien	Boolean     (*fn_proc)(int, const char *);
869236769Sobrien    } fn_defs[] = {
870236769Sobrien	{ "defined",   7, CondGetArg, CondDoDefined },
871236769Sobrien	{ "make",      4, CondGetArg, CondDoMake },
872236769Sobrien	{ "exists",    6, CondGetArg, CondDoExists },
873236769Sobrien	{ "empty",     5, get_mpt_arg, CondDoEmpty },
874236769Sobrien	{ "target",    6, CondGetArg, CondDoTarget },
875236769Sobrien	{ "commands",  8, CondGetArg, CondDoCommands },
876236769Sobrien	{ NULL,        0, NULL, NULL },
877236769Sobrien    };
878236769Sobrien    const struct fn_def *fn_def;
879236769Sobrien    Token	t;
880236769Sobrien    char	*arg = NULL;
881236769Sobrien    int	arglen;
882236769Sobrien    char *cp = condExpr;
883236769Sobrien    char *cp1;
884236769Sobrien
885236769Sobrien    for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) {
886236769Sobrien	if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len))
887236769Sobrien	    continue;
888236769Sobrien	cp += fn_def->fn_name_len;
889236769Sobrien	/* There can only be whitespace before the '(' */
890236769Sobrien	while (isspace(*(unsigned char *)cp))
891236769Sobrien	    cp++;
892236769Sobrien	if (*cp != '(')
893236769Sobrien	    break;
894236769Sobrien
895236769Sobrien	arglen = fn_def->fn_getarg(&cp, &arg, fn_def->fn_name);
896236769Sobrien	if (arglen <= 0) {
897236769Sobrien	    condExpr = cp;
898236769Sobrien	    return arglen < 0 ? TOK_ERROR : TOK_FALSE;
899236769Sobrien	}
900236769Sobrien	/* Evaluate the argument using the required function. */
901236769Sobrien	t = !doEval || fn_def->fn_proc(arglen, arg);
902296637Ssjg	free(arg);
903236769Sobrien	condExpr = cp;
904236769Sobrien	return t;
905236769Sobrien    }
906236769Sobrien
907236769Sobrien    /* Push anything numeric through the compare expression */
908236769Sobrien    cp = condExpr;
909236769Sobrien    if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0]))
910236769Sobrien	return compare_expression(doEval);
911236769Sobrien
912236769Sobrien    /*
913236769Sobrien     * Most likely we have a naked token to apply the default function to.
914236769Sobrien     * However ".if a == b" gets here when the "a" is unquoted and doesn't
915236769Sobrien     * start with a '$'. This surprises people.
916236769Sobrien     * If what follows the function argument is a '=' or '!' then the syntax
917236769Sobrien     * would be invalid if we did "defined(a)" - so instead treat as an
918236769Sobrien     * expression.
919236769Sobrien     */
920236769Sobrien    arglen = CondGetArg(&cp, &arg, NULL);
921236769Sobrien    for (cp1 = cp; isspace(*(unsigned char *)cp1); cp1++)
922236769Sobrien	continue;
923236769Sobrien    if (*cp1 == '=' || *cp1 == '!')
924236769Sobrien	return compare_expression(doEval);
925236769Sobrien    condExpr = cp;
926236769Sobrien
927236769Sobrien    /*
928236769Sobrien     * Evaluate the argument using the default function.
929236769Sobrien     * This path always treats .if as .ifdef. To get here the character
930236769Sobrien     * after .if must have been taken literally, so the argument cannot
931236769Sobrien     * be empty - even if it contained a variable expansion.
932236769Sobrien     */
933236769Sobrien    t = !doEval || if_info->defProc(arglen, arg) != if_info->doNot;
934296637Ssjg    free(arg);
935236769Sobrien    return t;
936236769Sobrien}
937236769Sobrien
938236769Sobrienstatic Token
939236769SobrienCondToken(Boolean doEval)
940236769Sobrien{
941236769Sobrien    Token t;
942236769Sobrien
943236769Sobrien    t = condPushBack;
944236769Sobrien    if (t != TOK_NONE) {
945236769Sobrien	condPushBack = TOK_NONE;
946236769Sobrien	return t;
947236769Sobrien    }
948236769Sobrien
949236769Sobrien    while (*condExpr == ' ' || *condExpr == '\t') {
950236769Sobrien	condExpr++;
951236769Sobrien    }
952236769Sobrien
953236769Sobrien    switch (*condExpr) {
954236769Sobrien
955236769Sobrien    case '(':
956236769Sobrien	condExpr++;
957236769Sobrien	return TOK_LPAREN;
958236769Sobrien
959236769Sobrien    case ')':
960236769Sobrien	condExpr++;
961236769Sobrien	return TOK_RPAREN;
962236769Sobrien
963236769Sobrien    case '|':
964236769Sobrien	if (condExpr[1] == '|') {
965236769Sobrien	    condExpr++;
966236769Sobrien	}
967236769Sobrien	condExpr++;
968236769Sobrien	return TOK_OR;
969236769Sobrien
970236769Sobrien    case '&':
971236769Sobrien	if (condExpr[1] == '&') {
972236769Sobrien	    condExpr++;
973236769Sobrien	}
974236769Sobrien	condExpr++;
975236769Sobrien	return TOK_AND;
976236769Sobrien
977236769Sobrien    case '!':
978236769Sobrien	condExpr++;
979236769Sobrien	return TOK_NOT;
980236769Sobrien
981236769Sobrien    case '#':
982236769Sobrien    case '\n':
983236769Sobrien    case '\0':
984236769Sobrien	return TOK_EOF;
985236769Sobrien
986236769Sobrien    case '"':
987236769Sobrien    case '$':
988236769Sobrien	return compare_expression(doEval);
989236769Sobrien
990236769Sobrien    default:
991236769Sobrien	return compare_function(doEval);
992236769Sobrien    }
993236769Sobrien}
994236769Sobrien
995236769Sobrien/*-
996236769Sobrien *-----------------------------------------------------------------------
997236769Sobrien * CondT --
998236769Sobrien *	Parse a single term in the expression. This consists of a terminal
999236769Sobrien *	symbol or TOK_NOT and a terminal symbol (not including the binary
1000236769Sobrien *	operators):
1001236769Sobrien *	    T -> defined(variable) | make(target) | exists(file) | symbol
1002236769Sobrien *	    T -> ! T | ( E )
1003236769Sobrien *
1004236769Sobrien * Results:
1005236769Sobrien *	TOK_TRUE, TOK_FALSE or TOK_ERROR.
1006236769Sobrien *
1007236769Sobrien * Side Effects:
1008236769Sobrien *	Tokens are consumed.
1009236769Sobrien *
1010236769Sobrien *-----------------------------------------------------------------------
1011236769Sobrien */
1012236769Sobrienstatic Token
1013236769SobrienCondT(Boolean doEval)
1014236769Sobrien{
1015236769Sobrien    Token   t;
1016236769Sobrien
1017236769Sobrien    t = CondToken(doEval);
1018236769Sobrien
1019236769Sobrien    if (t == TOK_EOF) {
1020236769Sobrien	/*
1021236769Sobrien	 * If we reached the end of the expression, the expression
1022236769Sobrien	 * is malformed...
1023236769Sobrien	 */
1024236769Sobrien	t = TOK_ERROR;
1025236769Sobrien    } else if (t == TOK_LPAREN) {
1026236769Sobrien	/*
1027236769Sobrien	 * T -> ( E )
1028236769Sobrien	 */
1029236769Sobrien	t = CondE(doEval);
1030236769Sobrien	if (t != TOK_ERROR) {
1031236769Sobrien	    if (CondToken(doEval) != TOK_RPAREN) {
1032236769Sobrien		t = TOK_ERROR;
1033236769Sobrien	    }
1034236769Sobrien	}
1035236769Sobrien    } else if (t == TOK_NOT) {
1036236769Sobrien	t = CondT(doEval);
1037236769Sobrien	if (t == TOK_TRUE) {
1038236769Sobrien	    t = TOK_FALSE;
1039236769Sobrien	} else if (t == TOK_FALSE) {
1040236769Sobrien	    t = TOK_TRUE;
1041236769Sobrien	}
1042236769Sobrien    }
1043236769Sobrien    return (t);
1044236769Sobrien}
1045236769Sobrien
1046236769Sobrien/*-
1047236769Sobrien *-----------------------------------------------------------------------
1048236769Sobrien * CondF --
1049236769Sobrien *	Parse a conjunctive factor (nice name, wot?)
1050236769Sobrien *	    F -> T && F | T
1051236769Sobrien *
1052236769Sobrien * Results:
1053236769Sobrien *	TOK_TRUE, TOK_FALSE or TOK_ERROR
1054236769Sobrien *
1055236769Sobrien * Side Effects:
1056236769Sobrien *	Tokens are consumed.
1057236769Sobrien *
1058236769Sobrien *-----------------------------------------------------------------------
1059236769Sobrien */
1060236769Sobrienstatic Token
1061236769SobrienCondF(Boolean doEval)
1062236769Sobrien{
1063236769Sobrien    Token   l, o;
1064236769Sobrien
1065236769Sobrien    l = CondT(doEval);
1066236769Sobrien    if (l != TOK_ERROR) {
1067236769Sobrien	o = CondToken(doEval);
1068236769Sobrien
1069236769Sobrien	if (o == TOK_AND) {
1070236769Sobrien	    /*
1071236769Sobrien	     * F -> T && F
1072236769Sobrien	     *
1073236769Sobrien	     * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we have to
1074236769Sobrien	     * parse the r.h.s. anyway (to throw it away).
1075236769Sobrien	     * If T is TOK_TRUE, the result is the r.h.s., be it an TOK_ERROR or no.
1076236769Sobrien	     */
1077236769Sobrien	    if (l == TOK_TRUE) {
1078236769Sobrien		l = CondF(doEval);
1079236769Sobrien	    } else {
1080236769Sobrien		(void)CondF(FALSE);
1081236769Sobrien	    }
1082236769Sobrien	} else {
1083236769Sobrien	    /*
1084236769Sobrien	     * F -> T
1085236769Sobrien	     */
1086236769Sobrien	    CondPushBack(o);
1087236769Sobrien	}
1088236769Sobrien    }
1089236769Sobrien    return (l);
1090236769Sobrien}
1091236769Sobrien
1092236769Sobrien/*-
1093236769Sobrien *-----------------------------------------------------------------------
1094236769Sobrien * CondE --
1095236769Sobrien *	Main expression production.
1096236769Sobrien *	    E -> F || E | F
1097236769Sobrien *
1098236769Sobrien * Results:
1099236769Sobrien *	TOK_TRUE, TOK_FALSE or TOK_ERROR.
1100236769Sobrien *
1101236769Sobrien * Side Effects:
1102236769Sobrien *	Tokens are, of course, consumed.
1103236769Sobrien *
1104236769Sobrien *-----------------------------------------------------------------------
1105236769Sobrien */
1106236769Sobrienstatic Token
1107236769SobrienCondE(Boolean doEval)
1108236769Sobrien{
1109236769Sobrien    Token   l, o;
1110236769Sobrien
1111236769Sobrien    l = CondF(doEval);
1112236769Sobrien    if (l != TOK_ERROR) {
1113236769Sobrien	o = CondToken(doEval);
1114236769Sobrien
1115236769Sobrien	if (o == TOK_OR) {
1116236769Sobrien	    /*
1117236769Sobrien	     * E -> F || E
1118236769Sobrien	     *
1119236769Sobrien	     * A similar thing occurs for ||, except that here we make sure
1120236769Sobrien	     * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s.
1121236769Sobrien	     * Once again, if l is TOK_FALSE, the result is the r.h.s. and once
1122236769Sobrien	     * again if l is TOK_TRUE, we parse the r.h.s. to throw it away.
1123236769Sobrien	     */
1124236769Sobrien	    if (l == TOK_FALSE) {
1125236769Sobrien		l = CondE(doEval);
1126236769Sobrien	    } else {
1127236769Sobrien		(void)CondE(FALSE);
1128236769Sobrien	    }
1129236769Sobrien	} else {
1130236769Sobrien	    /*
1131236769Sobrien	     * E -> F
1132236769Sobrien	     */
1133236769Sobrien	    CondPushBack(o);
1134236769Sobrien	}
1135236769Sobrien    }
1136236769Sobrien    return (l);
1137236769Sobrien}
1138236769Sobrien
1139236769Sobrien/*-
1140236769Sobrien *-----------------------------------------------------------------------
1141236769Sobrien * Cond_EvalExpression --
1142236769Sobrien *	Evaluate an expression in the passed line. The expression
1143236769Sobrien *	consists of &&, ||, !, make(target), defined(variable)
1144236769Sobrien *	and parenthetical groupings thereof.
1145236769Sobrien *
1146236769Sobrien * Results:
1147236769Sobrien *	COND_PARSE	if the condition was valid grammatically
1148236769Sobrien *	COND_INVALID  	if not a valid conditional.
1149236769Sobrien *
1150236769Sobrien *	(*value) is set to the boolean value of the condition
1151236769Sobrien *
1152236769Sobrien * Side Effects:
1153236769Sobrien *	None.
1154236769Sobrien *
1155236769Sobrien *-----------------------------------------------------------------------
1156236769Sobrien */
1157236769Sobrienint
1158282740SsjgCond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprint, Boolean strictLHS)
1159236769Sobrien{
1160236769Sobrien    static const struct If *dflt_info;
1161236769Sobrien    const struct If *sv_if_info = if_info;
1162236769Sobrien    char *sv_condExpr = condExpr;
1163236769Sobrien    Token sv_condPushBack = condPushBack;
1164236769Sobrien    int rval;
1165236769Sobrien
1166282740Ssjg    lhsStrict = strictLHS;
1167282740Ssjg
1168236769Sobrien    while (*line == ' ' || *line == '\t')
1169236769Sobrien	line++;
1170236769Sobrien
1171236769Sobrien    if (info == NULL && (info = dflt_info) == NULL) {
1172236769Sobrien	/* Scan for the entry for .if - it can't be first */
1173236769Sobrien	for (info = ifs; ; info++)
1174236769Sobrien	    if (info->form[0] == 0)
1175236769Sobrien		break;
1176236769Sobrien	dflt_info = info;
1177236769Sobrien    }
1178319884Ssjg    assert(info != NULL);
1179236769Sobrien
1180319884Ssjg    if_info = info;
1181236769Sobrien    condExpr = line;
1182236769Sobrien    condPushBack = TOK_NONE;
1183236769Sobrien
1184236769Sobrien    rval = do_Cond_EvalExpression(value);
1185236769Sobrien
1186236769Sobrien    if (rval == COND_INVALID && eprint)
1187236769Sobrien	Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line);
1188236769Sobrien
1189236769Sobrien    if_info = sv_if_info;
1190236769Sobrien    condExpr = sv_condExpr;
1191236769Sobrien    condPushBack = sv_condPushBack;
1192236769Sobrien
1193236769Sobrien    return rval;
1194236769Sobrien}
1195236769Sobrien
1196236769Sobrienstatic int
1197236769Sobriendo_Cond_EvalExpression(Boolean *value)
1198236769Sobrien{
1199236769Sobrien
1200236769Sobrien    switch (CondE(TRUE)) {
1201236769Sobrien    case TOK_TRUE:
1202236769Sobrien	if (CondToken(TRUE) == TOK_EOF) {
1203236769Sobrien	    *value = TRUE;
1204236769Sobrien	    return COND_PARSE;
1205236769Sobrien	}
1206236769Sobrien	break;
1207236769Sobrien    case TOK_FALSE:
1208236769Sobrien	if (CondToken(TRUE) == TOK_EOF) {
1209236769Sobrien	    *value = FALSE;
1210236769Sobrien	    return COND_PARSE;
1211236769Sobrien	}
1212236769Sobrien	break;
1213236769Sobrien    default:
1214236769Sobrien    case TOK_ERROR:
1215236769Sobrien	break;
1216236769Sobrien    }
1217236769Sobrien
1218236769Sobrien    return COND_INVALID;
1219236769Sobrien}
1220236769Sobrien
1221236769Sobrien
1222236769Sobrien/*-
1223236769Sobrien *-----------------------------------------------------------------------
1224236769Sobrien * Cond_Eval --
1225236769Sobrien *	Evaluate the conditional in the passed line. The line
1226236769Sobrien *	looks like this:
1227236769Sobrien *	    .<cond-type> <expr>
1228236769Sobrien *	where <cond-type> is any of if, ifmake, ifnmake, ifdef,
1229236769Sobrien *	ifndef, elif, elifmake, elifnmake, elifdef, elifndef
1230236769Sobrien *	and <expr> consists of &&, ||, !, make(target), defined(variable)
1231236769Sobrien *	and parenthetical groupings thereof.
1232236769Sobrien *
1233236769Sobrien * Input:
1234236769Sobrien *	line		Line to parse
1235236769Sobrien *
1236236769Sobrien * Results:
1237236769Sobrien *	COND_PARSE	if should parse lines after the conditional
1238236769Sobrien *	COND_SKIP	if should skip lines after the conditional
1239236769Sobrien *	COND_INVALID  	if not a valid conditional.
1240236769Sobrien *
1241236769Sobrien * Side Effects:
1242236769Sobrien *	None.
1243236769Sobrien *
1244236769Sobrien * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order
1245236769Sobrien * to detect splurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF)
1246236769Sobrien * otherwise .else could be treated as '.elif 1'.
1247236769Sobrien *
1248236769Sobrien *-----------------------------------------------------------------------
1249236769Sobrien */
1250236769Sobrienint
1251236769SobrienCond_Eval(char *line)
1252236769Sobrien{
1253243115Ssjg#define	    MAXIF      128	/* maximum depth of .if'ing */
1254243115Ssjg#define	    MAXIF_BUMP  32	/* how much to grow by */
1255236769Sobrien    enum if_states {
1256236769Sobrien	IF_ACTIVE,		/* .if or .elif part active */
1257236769Sobrien	ELSE_ACTIVE,		/* .else part active */
1258236769Sobrien	SEARCH_FOR_ELIF,	/* searching for .elif/else to execute */
1259236769Sobrien	SKIP_TO_ELSE,           /* has been true, but not seen '.else' */
1260236769Sobrien	SKIP_TO_ENDIF		/* nothing else to execute */
1261236769Sobrien    };
1262243115Ssjg    static enum if_states *cond_state = NULL;
1263243115Ssjg    static unsigned int max_if_depth = MAXIF;
1264236769Sobrien
1265236769Sobrien    const struct If *ifp;
1266236769Sobrien    Boolean 	    isElif;
1267236769Sobrien    Boolean 	    value;
1268236769Sobrien    int	    	    level;  	/* Level at which to report errors. */
1269236769Sobrien    enum if_states  state;
1270236769Sobrien
1271236769Sobrien    level = PARSE_FATAL;
1272243115Ssjg    if (!cond_state) {
1273243115Ssjg	cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state));
1274243115Ssjg	cond_state[0] = IF_ACTIVE;
1275243115Ssjg    }
1276236769Sobrien    /* skip leading character (the '.') and any whitespace */
1277236769Sobrien    for (line++; *line == ' ' || *line == '\t'; line++)
1278236769Sobrien	continue;
1279236769Sobrien
1280236769Sobrien    /* Find what type of if we're dealing with.  */
1281236769Sobrien    if (line[0] == 'e') {
1282236769Sobrien	if (line[1] != 'l') {
1283236769Sobrien	    if (!istoken(line + 1, "ndif", 4))
1284236769Sobrien		return COND_INVALID;
1285236769Sobrien	    /* End of conditional section */
1286236769Sobrien	    if (cond_depth == cond_min_depth) {
1287236769Sobrien		Parse_Error(level, "if-less endif");
1288236769Sobrien		return COND_PARSE;
1289236769Sobrien	    }
1290236769Sobrien	    /* Return state for previous conditional */
1291236769Sobrien	    cond_depth--;
1292236769Sobrien	    return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
1293236769Sobrien	}
1294236769Sobrien
1295236769Sobrien	/* Quite likely this is 'else' or 'elif' */
1296236769Sobrien	line += 2;
1297236769Sobrien	if (istoken(line, "se", 2)) {
1298236769Sobrien	    /* It is else... */
1299236769Sobrien	    if (cond_depth == cond_min_depth) {
1300236769Sobrien		Parse_Error(level, "if-less else");
1301236769Sobrien		return COND_PARSE;
1302236769Sobrien	    }
1303236769Sobrien
1304236769Sobrien	    state = cond_state[cond_depth];
1305236769Sobrien	    switch (state) {
1306236769Sobrien	    case SEARCH_FOR_ELIF:
1307236769Sobrien		state = ELSE_ACTIVE;
1308236769Sobrien		break;
1309236769Sobrien	    case ELSE_ACTIVE:
1310236769Sobrien	    case SKIP_TO_ENDIF:
1311236769Sobrien		Parse_Error(PARSE_WARNING, "extra else");
1312236769Sobrien		/* FALLTHROUGH */
1313236769Sobrien	    default:
1314236769Sobrien	    case IF_ACTIVE:
1315236769Sobrien	    case SKIP_TO_ELSE:
1316236769Sobrien		state = SKIP_TO_ENDIF;
1317236769Sobrien		break;
1318236769Sobrien	    }
1319236769Sobrien	    cond_state[cond_depth] = state;
1320236769Sobrien	    return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
1321236769Sobrien	}
1322236769Sobrien	/* Assume for now it is an elif */
1323236769Sobrien	isElif = TRUE;
1324236769Sobrien    } else
1325236769Sobrien	isElif = FALSE;
1326236769Sobrien
1327236769Sobrien    if (line[0] != 'i' || line[1] != 'f')
1328236769Sobrien	/* Not an ifxxx or elifxxx line */
1329236769Sobrien	return COND_INVALID;
1330236769Sobrien
1331236769Sobrien    /*
1332236769Sobrien     * Figure out what sort of conditional it is -- what its default
1333236769Sobrien     * function is, etc. -- by looking in the table of valid "ifs"
1334236769Sobrien     */
1335236769Sobrien    line += 2;
1336236769Sobrien    for (ifp = ifs; ; ifp++) {
1337236769Sobrien	if (ifp->form == NULL)
1338236769Sobrien	    return COND_INVALID;
1339236769Sobrien	if (istoken(ifp->form, line, ifp->formlen)) {
1340236769Sobrien	    line += ifp->formlen;
1341236769Sobrien	    break;
1342236769Sobrien	}
1343236769Sobrien    }
1344236769Sobrien
1345236769Sobrien    /* Now we know what sort of 'if' it is... */
1346236769Sobrien
1347236769Sobrien    if (isElif) {
1348236769Sobrien	if (cond_depth == cond_min_depth) {
1349236769Sobrien	    Parse_Error(level, "if-less elif");
1350236769Sobrien	    return COND_PARSE;
1351236769Sobrien	}
1352236769Sobrien	state = cond_state[cond_depth];
1353236769Sobrien	if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) {
1354236769Sobrien	    Parse_Error(PARSE_WARNING, "extra elif");
1355236769Sobrien	    cond_state[cond_depth] = SKIP_TO_ENDIF;
1356236769Sobrien	    return COND_SKIP;
1357236769Sobrien	}
1358236769Sobrien	if (state != SEARCH_FOR_ELIF) {
1359236769Sobrien	    /* Either just finished the 'true' block, or already SKIP_TO_ELSE */
1360236769Sobrien	    cond_state[cond_depth] = SKIP_TO_ELSE;
1361236769Sobrien	    return COND_SKIP;
1362236769Sobrien	}
1363236769Sobrien    } else {
1364236769Sobrien	/* Normal .if */
1365243115Ssjg	if (cond_depth + 1 >= max_if_depth) {
1366243115Ssjg	    /*
1367243115Ssjg	     * This is rare, but not impossible.
1368243115Ssjg	     * In meta mode, dirdeps.mk (only runs at level 0)
1369243115Ssjg	     * can need more than the default.
1370243115Ssjg	     */
1371243115Ssjg	    max_if_depth += MAXIF_BUMP;
1372243115Ssjg	    cond_state = bmake_realloc(cond_state, max_if_depth *
1373243115Ssjg		sizeof(*cond_state));
1374236769Sobrien	}
1375236769Sobrien	state = cond_state[cond_depth];
1376236769Sobrien	cond_depth++;
1377236769Sobrien	if (state > ELSE_ACTIVE) {
1378236769Sobrien	    /* If we aren't parsing the data, treat as always false */
1379236769Sobrien	    cond_state[cond_depth] = SKIP_TO_ELSE;
1380236769Sobrien	    return COND_SKIP;
1381236769Sobrien	}
1382236769Sobrien    }
1383236769Sobrien
1384236769Sobrien    /* And evaluate the conditional expresssion */
1385282740Ssjg    if (Cond_EvalExpression(ifp, line, &value, 1, TRUE) == COND_INVALID) {
1386236769Sobrien	/* Syntax error in conditional, error message already output. */
1387236769Sobrien	/* Skip everything to matching .endif */
1388236769Sobrien	cond_state[cond_depth] = SKIP_TO_ELSE;
1389236769Sobrien	return COND_SKIP;
1390236769Sobrien    }
1391236769Sobrien
1392236769Sobrien    if (!value) {
1393236769Sobrien	cond_state[cond_depth] = SEARCH_FOR_ELIF;
1394236769Sobrien	return COND_SKIP;
1395236769Sobrien    }
1396236769Sobrien    cond_state[cond_depth] = IF_ACTIVE;
1397236769Sobrien    return COND_PARSE;
1398236769Sobrien}
1399236769Sobrien
1400236769Sobrien
1401236769Sobrien
1402236769Sobrien/*-
1403236769Sobrien *-----------------------------------------------------------------------
1404236769Sobrien * Cond_End --
1405236769Sobrien *	Make sure everything's clean at the end of a makefile.
1406236769Sobrien *
1407236769Sobrien * Results:
1408236769Sobrien *	None.
1409236769Sobrien *
1410236769Sobrien * Side Effects:
1411236769Sobrien *	Parse_Error will be called if open conditionals are around.
1412236769Sobrien *
1413236769Sobrien *-----------------------------------------------------------------------
1414236769Sobrien */
1415236769Sobrienvoid
1416236769SobrienCond_restore_depth(unsigned int saved_depth)
1417236769Sobrien{
1418236769Sobrien    int open_conds = cond_depth - cond_min_depth;
1419236769Sobrien
1420236769Sobrien    if (open_conds != 0 || saved_depth > cond_depth) {
1421236769Sobrien	Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds,
1422236769Sobrien		    open_conds == 1 ? "" : "s");
1423236769Sobrien	cond_depth = cond_min_depth;
1424236769Sobrien    }
1425236769Sobrien
1426236769Sobrien    cond_min_depth = saved_depth;
1427236769Sobrien}
1428236769Sobrien
1429236769Sobrienunsigned int
1430236769SobrienCond_save_depth(void)
1431236769Sobrien{
1432236769Sobrien    int depth = cond_min_depth;
1433236769Sobrien
1434236769Sobrien    cond_min_depth = cond_depth;
1435236769Sobrien    return depth;
1436236769Sobrien}
1437