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