cond.c revision 141104
1118611Snjl/*-
2118611Snjl * Copyright (c) 1988, 1989, 1990, 1993
3118611Snjl *	The Regents of the University of California.  All rights reserved.
4118611Snjl * Copyright (c) 1988, 1989 by Adam de Boor
5118611Snjl * Copyright (c) 1989 by Berkeley Softworks
6118611Snjl * All rights reserved.
7118611Snjl *
8118611Snjl * This code is derived from software contributed to Berkeley by
9118611Snjl * Adam de Boor.
10118611Snjl *
11118611Snjl * Redistribution and use in source and binary forms, with or without
12118611Snjl * modification, are permitted provided that the following conditions
13118611Snjl * are met:
14126372Snjl * 1. Redistributions of source code must retain the above copyright
15118611Snjl *    notice, this list of conditions and the following disclaimer.
16118611Snjl * 2. Redistributions in binary form must reproduce the above copyright
17118611Snjl *    notice, this list of conditions and the following disclaimer in the
18118611Snjl *    documentation and/or other materials provided with the distribution.
19118611Snjl * 3. All advertising materials mentioning features or use of this software
20118611Snjl *    must display the following acknowledgement:
21118611Snjl *	This product includes software developed by the University of
22118611Snjl *	California, Berkeley and its contributors.
23118611Snjl * 4. Neither the name of the University nor the names of its contributors
24118611Snjl *    may be used to endorse or promote products derived from this software
25118611Snjl *    without specific prior written permission.
26118611Snjl *
27118611Snjl * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28118611Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29118611Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30118611Snjl * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31118611Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32118611Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33118611Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34118611Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35118611Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36118611Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37118611Snjl * SUCH DAMAGE.
38118611Snjl *
39118611Snjl * @(#)cond.c	8.2 (Berkeley) 1/2/94
40118611Snjl */
41118611Snjl
42118611Snjl#include <sys/cdefs.h>
43118611Snjl__FBSDID("$FreeBSD: head/usr.bin/make/cond.c 141104 2005-02-01 10:50:37Z harti $");
44118611Snjl
45118611Snjl/*-
46118611Snjl * cond.c --
47118611Snjl *	Functions to handle conditionals in a makefile.
48118611Snjl *
49118611Snjl * Interface:
50118611Snjl *	Cond_Eval 	Evaluate the conditional in the passed line.
51118611Snjl *
52118611Snjl */
53118611Snjl
54118611Snjl#include <ctype.h>
55118611Snjl#include <string.h>
56118611Snjl#include <stdlib.h>
57118611Snjl
58118611Snjl#include "cond.h"
59118611Snjl#include "dir.h"
60118611Snjl#include "globals.h"
61118611Snjl#include "GNode.h"
62118611Snjl#include "make.h"
63118611Snjl#include "parse.h"
64118611Snjl#include "sprite.h"
65118611Snjl#include "str.h"
66118611Snjl#include "targ.h"
67118611Snjl#include "util.h"
68118611Snjl#include "var.h"
69118611Snjl
70118611Snjl/*
71118611Snjl * The parsing of conditional expressions is based on this grammar:
72118611Snjl *	E -> F || E
73118611Snjl *	E -> F
74118611Snjl *	F -> T && F
75118611Snjl *	F -> T
76118611Snjl *	T -> defined(variable)
77118611Snjl *	T -> make(target)
78118611Snjl *	T -> exists(file)
79118611Snjl *	T -> empty(varspec)
80118611Snjl *	T -> target(name)
81118611Snjl *	T -> symbol
82118611Snjl *	T -> $(varspec) op value
83118611Snjl *	T -> $(varspec) == "string"
84118611Snjl *	T -> $(varspec) != "string"
85118611Snjl *	T -> ( E )
86118611Snjl *	T -> ! T
87118611Snjl *	op -> == | != | > | < | >= | <=
88118611Snjl *
89118611Snjl * 'symbol' is some other symbol to which the default function (condDefProc)
90118611Snjl * is applied.
91118611Snjl *
92118611Snjl * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
93118611Snjl * will return And for '&' and '&&', Or for '|' and '||', Not for '!',
94118611Snjl * LParen for '(', RParen for ')' and will evaluate the other terminal
95118611Snjl * symbols, using either the default function or the function given in the
96118611Snjl * terminal, and return the result as either True or False.
97118611Snjl *
98118611Snjl * All Non-Terminal functions (CondE, CondF and CondT) return Err on error.
99118611Snjl */
100118611Snjltypedef enum {
101118611Snjl    And, Or, Not, True, False, LParen, RParen, EndOfFile, None, Err
102118611Snjl} Token;
103118611Snjl
104118611Snjl/*-
105118611Snjl * Structures to handle elegantly the different forms of #if's. The
106118611Snjl * last two fields are stored in condInvert and condDefProc, respectively.
107118611Snjl */
108118611Snjlstatic void CondPushBack(Token);
109118611Snjlstatic int CondGetArg(char **, char **, char *, Boolean);
110118611Snjlstatic Boolean CondDoDefined(int, char *);
111118611Snjlstatic Boolean CondDoMake(int, char *);
112118611Snjlstatic Boolean CondDoExists(int, char *);
113118611Snjlstatic Boolean CondDoTarget(int, char *);
114118611Snjlstatic char * CondCvtArg(char *, double *);
115118611Snjlstatic Token CondToken(Boolean);
116118611Snjlstatic Token CondT(Boolean);
117118611Snjlstatic Token CondF(Boolean);
118118611Snjlstatic Token CondE(Boolean);
119118611Snjl
120118611Snjlstatic struct If {
121118611Snjl    char	*form;	      /* Form of if */
122118611Snjl    int		formlen;      /* Length of form */
123118611Snjl    Boolean	doNot;	      /* TRUE if default function should be negated */
124118611Snjl    Boolean	(*defProc)(int, char *); /* Default function to apply */
125118611Snjl} ifs[] = {
126118611Snjl    { "ifdef",	  5,	  FALSE,  CondDoDefined },
127118611Snjl    { "ifndef",	  6,	  TRUE,	  CondDoDefined },
128118611Snjl    { "ifmake",	  6,	  FALSE,  CondDoMake },
129118611Snjl    { "ifnmake",  7,	  TRUE,	  CondDoMake },
130118611Snjl    { "if",	  2,	  FALSE,  CondDoDefined },
131118611Snjl    { NULL,	  0,	  FALSE,  NULL }
132118611Snjl};
133118611Snjl
134118611Snjlstatic Boolean	  condInvert;	    	/* Invert the default function */
135118611Snjlstatic Boolean	  (*condDefProc)	/* Default function to apply */
136118611Snjl(int, char *);
137118611Snjlstatic char 	  *condExpr;	    	/* The expression to parse */
138118611Snjlstatic Token	  condPushBack=None;	/* Single push-back token used in
139118611Snjl					 * parsing */
140118611Snjl
141118611Snjl#define	MAXIF		30	  /* greatest depth of #if'ing */
142118611Snjl
143118611Snjlstatic Boolean	  condStack[MAXIF]; 	/* Stack of conditionals's values */
144118611Snjlstatic int	  condLineno[MAXIF];	/* Line numbers of the opening .if */
145118611Snjlstatic int  	  condTop = MAXIF;  	/* Top-most conditional */
146118611Snjlstatic int  	  skipIfLevel=0;    	/* Depth of skipped conditionals */
147118611Snjlstatic int	  skipIfLineno[MAXIF];  /* Line numbers of skipped .ifs */
148118611Snjlstatic Boolean	  skipLine = FALSE; 	/* Whether the parse module is skipping
149118611Snjl					 * lines */
150118611Snjl
151118611Snjl/*-
152118611Snjl *-----------------------------------------------------------------------
153118611Snjl * CondPushBack --
154118611Snjl *	Push back the most recent token read. We only need one level of
155118611Snjl *	this, so the thing is just stored in 'condPushback'.
156118611Snjl *
157118611Snjl * Results:
158118611Snjl *	None.
159118611Snjl *
160118611Snjl * Side Effects:
161118611Snjl *	condPushback is overwritten.
162118611Snjl *
163118611Snjl *-----------------------------------------------------------------------
164118611Snjl */
165118611Snjlstatic void
166118611SnjlCondPushBack(Token t)
167118611Snjl{
168118611Snjl
169118611Snjl    condPushBack = t;
170118611Snjl}
171118611Snjl
172118611Snjl/*-
173118611Snjl *-----------------------------------------------------------------------
174118611Snjl * CondGetArg --
175118611Snjl *	Find the argument of a built-in function.  parens is set to TRUE
176118611Snjl *	if the arguments are bounded by parens.
177118611Snjl *
178118611Snjl * Results:
179118611Snjl *	The length of the argument and the address of the argument.
180118611Snjl *
181118611Snjl * Side Effects:
182118611Snjl *	The pointer is set to point to the closing parenthesis of the
183118611Snjl *	function call.
184118611Snjl *
185118611Snjl *-----------------------------------------------------------------------
186118611Snjl */
187118611Snjlstatic int
188118611SnjlCondGetArg(char **linePtr, char **argPtr, char *func, Boolean parens)
189118611Snjl{
190118611Snjl    char	  *cp;
191118611Snjl    size_t    	  argLen;
192118611Snjl    Buffer	  buf;
193118611Snjl
194118611Snjl    cp = *linePtr;
195118611Snjl    if (parens) {
196118611Snjl	while (*cp != '(' && *cp != '\0') {
197118611Snjl	    cp++;
198118611Snjl	}
199118611Snjl	if (*cp == '(') {
200118611Snjl	    cp++;
201118611Snjl	}
202118611Snjl    }
203118611Snjl
204118611Snjl    if (*cp == '\0') {
205118611Snjl	/*
206118611Snjl	 * No arguments whatsoever. Because 'make' and 'defined' aren't really
207118611Snjl	 * "reserved words", we don't print a message. I think this is better
208118611Snjl	 * than hitting the user with a warning message every time s/he uses
209118611Snjl	 * the word 'make' or 'defined' at the beginning of a symbol...
210138287Smarks	 */
211118611Snjl	*argPtr = cp;
212118611Snjl	return (0);
213118611Snjl    }
214118611Snjl
215118611Snjl    while (*cp == ' ' || *cp == '\t') {
216118611Snjl	cp++;
217118611Snjl    }
218118611Snjl
219118611Snjl    /*
220118611Snjl     * Create a buffer for the argument and start it out at 16 characters
221118611Snjl     * long. Why 16? Why not?
222118611Snjl     */
223118611Snjl    buf = Buf_Init(16);
224118611Snjl
225118611Snjl    while ((strchr(" \t)&|", *cp) == NULL) && (*cp != '\0')) {
226118611Snjl	if (*cp == '$') {
227118611Snjl	    /*
228118611Snjl	     * Parse the variable spec and install it as part of the argument
229118611Snjl	     * if it's valid. We tell Var_Parse to complain on an undefined
230118611Snjl	     * variable, so we don't do it too. Nor do we return an error,
231118611Snjl	     * though perhaps we should...
232118611Snjl	     */
233118611Snjl	    char  	*cp2;
234118611Snjl	    size_t	len;
235118611Snjl	    Boolean	doFree;
236118611Snjl
237118611Snjl	    cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree);
238118611Snjl
239118611Snjl	    Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
240118611Snjl	    if (doFree) {
241118611Snjl		free(cp2);
242118611Snjl	    }
243118611Snjl	    cp += len;
244118611Snjl	} else {
245118611Snjl	    Buf_AddByte(buf, (Byte)*cp);
246118611Snjl	    cp++;
247118611Snjl	}
248118611Snjl    }
249118611Snjl
250118611Snjl    Buf_AddByte(buf, (Byte)'\0');
251118611Snjl    *argPtr = (char *)Buf_GetAll(buf, &argLen);
252118611Snjl    Buf_Destroy(buf, FALSE);
253118611Snjl
254118611Snjl    while (*cp == ' ' || *cp == '\t') {
255118611Snjl	cp++;
256118611Snjl    }
257118611Snjl    if (parens && *cp != ')') {
258118611Snjl	Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
259118611Snjl		     func);
260118611Snjl	return (0);
261118611Snjl    } else if (parens) {
262118611Snjl	/*
263118611Snjl	 * Advance pointer past close parenthesis.
264118611Snjl	 */
265118611Snjl	cp++;
266118611Snjl    }
267118611Snjl
268118611Snjl    *linePtr = cp;
269118611Snjl    return (argLen);
270118611Snjl}
271118611Snjl
272118611Snjl/*-
273118611Snjl *-----------------------------------------------------------------------
274118611Snjl * CondDoDefined --
275118611Snjl *	Handle the 'defined' function for conditionals.
276118611Snjl *
277118611Snjl * Results:
278118611Snjl *	TRUE if the given variable is defined.
279118611Snjl *
280118611Snjl * Side Effects:
281118611Snjl *	None.
282118611Snjl *
283138287Smarks *-----------------------------------------------------------------------
284118611Snjl */
285118611Snjlstatic Boolean
286118611SnjlCondDoDefined(int argLen, char *arg)
287118611Snjl{
288118611Snjl    char    savec = arg[argLen];
289118611Snjl    char    *p1;
290118611Snjl    Boolean result;
291118611Snjl
292118611Snjl    arg[argLen] = '\0';
293118611Snjl    if (Var_Value(arg, VAR_CMD, &p1) != NULL) {
294118611Snjl	result = TRUE;
295118611Snjl    } else {
296118611Snjl	result = FALSE;
297118611Snjl    }
298118611Snjl    free(p1);
299118611Snjl    arg[argLen] = savec;
300118611Snjl    return (result);
301118611Snjl}
302118611Snjl
303118611Snjl/*-
304118611Snjl *-----------------------------------------------------------------------
305118611Snjl * CondStrMatch --
306118611Snjl *	Front-end for Str_Match so it returns 0 on match and non-zero
307118611Snjl *	on mismatch. Callback function for CondDoMake via Lst_Find
308118611Snjl *
309118611Snjl * Results:
310118611Snjl *	0 if string matches pattern
311118611Snjl *
312118611Snjl * Side Effects:
313118611Snjl *	None
314118611Snjl *
315118611Snjl *-----------------------------------------------------------------------
316118611Snjl */
317118611Snjlstatic int
318138287SmarksCondStrMatch(const void *string, const void *pattern)
319118611Snjl{
320118611Snjl
321118611Snjl    return (!Str_Match(string, pattern));
322118611Snjl}
323138287Smarks
324118611Snjl/*-
325138287Smarks *-----------------------------------------------------------------------
326138287Smarks * CondDoMake --
327138287Smarks *	Handle the 'make' function for conditionals.
328118611Snjl *
329118611Snjl * Results:
330118611Snjl *	TRUE if the given target is being made.
331118611Snjl *
332118611Snjl * Side Effects:
333118611Snjl *	None.
334118611Snjl *
335118611Snjl *-----------------------------------------------------------------------
336118611Snjl */
337118611Snjlstatic Boolean
338138287SmarksCondDoMake(int argLen, char *arg)
339118611Snjl{
340118611Snjl    char    savec = arg[argLen];
341118611Snjl    Boolean result;
342118611Snjl
343118611Snjl    arg[argLen] = '\0';
344118611Snjl    if (Lst_Find(&create, arg, CondStrMatch) == NULL) {
345118611Snjl	result = FALSE;
346138287Smarks    } else {
347118611Snjl	result = TRUE;
348118611Snjl    }
349118611Snjl    arg[argLen] = savec;
350118611Snjl    return (result);
351118611Snjl}
352118611Snjl
353118611Snjl/*-
354118611Snjl *-----------------------------------------------------------------------
355118611Snjl * CondDoExists --
356118611Snjl *	See if the given file exists.
357118611Snjl *
358118611Snjl * Results:
359118611Snjl *	TRUE if the file exists and FALSE if it does not.
360118611Snjl *
361118611Snjl * Side Effects:
362118611Snjl *	None.
363118611Snjl *
364118611Snjl *-----------------------------------------------------------------------
365118611Snjl */
366118611Snjlstatic Boolean
367118611SnjlCondDoExists(int argLen, char *arg)
368118611Snjl{
369118611Snjl    char    savec = arg[argLen];
370118611Snjl    Boolean result;
371118611Snjl    char    *path;
372118611Snjl
373118611Snjl    arg[argLen] = '\0';
374118611Snjl    path = Dir_FindFile(arg, &dirSearchPath);
375118611Snjl    if (path != NULL) {
376118611Snjl	result = TRUE;
377118611Snjl	free(path);
378118611Snjl    } else {
379118611Snjl	result = FALSE;
380118611Snjl    }
381118611Snjl    arg[argLen] = savec;
382118611Snjl    return (result);
383118611Snjl}
384118611Snjl
385118611Snjl/*-
386118611Snjl *-----------------------------------------------------------------------
387118611Snjl * CondDoTarget --
388118611Snjl *	See if the given node exists and is an actual target.
389118611Snjl *
390118611Snjl * Results:
391118611Snjl *	TRUE if the node exists as a target and FALSE if it does not.
392118611Snjl *
393118611Snjl * Side Effects:
394118611Snjl *	None.
395118611Snjl *
396118611Snjl *-----------------------------------------------------------------------
397118611Snjl */
398118611Snjlstatic Boolean
399118611SnjlCondDoTarget(int argLen, char *arg)
400118611Snjl{
401118611Snjl    char    savec = arg[argLen];
402118611Snjl    Boolean result;
403118611Snjl    GNode   *gn;
404118611Snjl
405118611Snjl    arg[argLen] = '\0';
406118611Snjl    gn = Targ_FindNode(arg, TARG_NOCREATE);
407118611Snjl    if ((gn != NULL) && !OP_NOP(gn->type)) {
408118611Snjl	result = TRUE;
409118611Snjl    } else {
410118611Snjl	result = FALSE;
411118611Snjl    }
412118611Snjl    arg[argLen] = savec;
413118611Snjl    return (result);
414118611Snjl}
415118611Snjl
416118611Snjl/*-
417118611Snjl *-----------------------------------------------------------------------
418118611Snjl * CondCvtArg --
419118611Snjl *	Convert the given number into a double. If the number begins
420118611Snjl *	with 0x, it is interpreted as a hexadecimal integer
421118611Snjl *	and converted to a double from there. All other strings just have
422118611Snjl *	strtod called on them.
423118611Snjl *
424118611Snjl * Results:
425118611Snjl *	Sets 'value' to double value of string.
426118611Snjl *	Returns address of the first character after the last valid
427118611Snjl *	character of the converted number.
428118611Snjl *
429118611Snjl * Side Effects:
430118611Snjl *	Can change 'value' even if string is not a valid number.
431118611Snjl *
432118611Snjl *
433118611Snjl *-----------------------------------------------------------------------
434118611Snjl */
435118611Snjlstatic char *
436118611SnjlCondCvtArg(char *str, double *value)
437118611Snjl{
438118611Snjl    if ((*str == '0') && (str[1] == 'x')) {
439118611Snjl	long i;
440118611Snjl
441118611Snjl	for (str += 2, i = 0; ; str++) {
442118611Snjl	    int x;
443118611Snjl	    if (isdigit((unsigned char)*str))
444118611Snjl		x  = *str - '0';
445118611Snjl	    else if (isxdigit((unsigned char)*str))
446118611Snjl		x = 10 + *str - isupper((unsigned char)*str) ? 'A' : 'a';
447118611Snjl	    else {
448118611Snjl		*value = (double)i;
449118611Snjl		return (str);
450118611Snjl	    }
451118611Snjl	    i = (i << 4) + x;
452118611Snjl	}
453118611Snjl    }
454118611Snjl    else {
455118611Snjl	char *eptr;
456118611Snjl	*value = strtod(str, &eptr);
457118611Snjl	return (eptr);
458118611Snjl    }
459118611Snjl}
460118611Snjl
461118611Snjl/*-
462118611Snjl *-----------------------------------------------------------------------
463118611Snjl * CondToken --
464118611Snjl *	Return the next token from the input.
465118611Snjl *
466118611Snjl * Results:
467118611Snjl *	A Token for the next lexical token in the stream.
468118611Snjl *
469118611Snjl * Side Effects:
470118611Snjl *	condPushback will be set back to None if it is used.
471118611Snjl *
472118611Snjl *-----------------------------------------------------------------------
473118611Snjl */
474118611Snjlstatic Token
475118611SnjlCondToken(Boolean doEval)
476118611Snjl{
477118611Snjl    Token	  t;
478118611Snjl
479118611Snjl    if (condPushBack == None) {
480118611Snjl	while (*condExpr == ' ' || *condExpr == '\t') {
481118611Snjl	    condExpr++;
482118611Snjl	}
483118611Snjl	switch (*condExpr) {
484118611Snjl	    case '(':
485118611Snjl		t = LParen;
486118611Snjl		condExpr++;
487118611Snjl		break;
488118611Snjl	    case ')':
489118611Snjl		t = RParen;
490118611Snjl		condExpr++;
491118611Snjl		break;
492118611Snjl	    case '|':
493118611Snjl		if (condExpr[1] == '|') {
494118611Snjl		    condExpr++;
495118611Snjl		}
496118611Snjl		condExpr++;
497118611Snjl		t = Or;
498118611Snjl		break;
499118611Snjl	    case '&':
500118611Snjl		if (condExpr[1] == '&') {
501118611Snjl		    condExpr++;
502118611Snjl		}
503118611Snjl		condExpr++;
504118611Snjl		t = And;
505118611Snjl		break;
506118611Snjl	    case '!':
507118611Snjl		t = Not;
508118611Snjl		condExpr++;
509118611Snjl		break;
510118611Snjl	    case '\n':
511118611Snjl	    case '\0':
512118611Snjl		t = EndOfFile;
513118611Snjl		break;
514118611Snjl	    case '$': {
515118611Snjl		char	*lhs;
516118611Snjl		char	*rhs;
517118611Snjl		char	*op;
518118611Snjl		size_t	varSpecLen;
519118611Snjl		Boolean	doFree;
520118611Snjl
521118611Snjl		/*
522118611Snjl		 * Parse the variable spec and skip over it, saving its
523118611Snjl		 * value in lhs.
524118611Snjl		 */
525118611Snjl		t = Err;
526118611Snjl		lhs = Var_Parse(condExpr, VAR_CMD, doEval,
527118611Snjl		    &varSpecLen, &doFree);
528118611Snjl		if (lhs == var_Error) {
529118611Snjl		    /*
530118611Snjl		     * Even if !doEval, we still report syntax errors, which
531118611Snjl		     * is what getting var_Error back with !doEval means.
532118611Snjl		     */
533118611Snjl		    return (Err);
534118611Snjl		}
535118611Snjl		condExpr += varSpecLen;
536118611Snjl
537118611Snjl		if (!isspace((unsigned char)*condExpr) &&
538118611Snjl		    strchr("!=><", *condExpr) == NULL) {
539118611Snjl		    Buffer buf;
540118611Snjl		    char *cp;
541118611Snjl
542118611Snjl		    buf = Buf_Init(0);
543118611Snjl
544118611Snjl		    for (cp = lhs; *cp; cp++)
545118611Snjl			Buf_AddByte(buf, (Byte)*cp);
546118611Snjl
547118611Snjl		    if (doFree)
548118611Snjl			free(lhs);
549118611Snjl
550118611Snjl		    for (;*condExpr && !isspace((unsigned char) *condExpr);
551118611Snjl			 condExpr++)
552118611Snjl			Buf_AddByte(buf, (Byte)*condExpr);
553118611Snjl
554118611Snjl		    Buf_AddByte(buf, (Byte)'\0');
555118611Snjl		    lhs = (char *)Buf_GetAll(buf, &varSpecLen);
556118611Snjl		    Buf_Destroy(buf, FALSE);
557118611Snjl
558118611Snjl		    doFree = TRUE;
559118611Snjl		}
560118611Snjl
561118611Snjl		/*
562118611Snjl		 * Skip whitespace to get to the operator
563118611Snjl		 */
564118611Snjl		while (isspace((unsigned char)*condExpr))
565118611Snjl		    condExpr++;
566118611Snjl
567118611Snjl		/*
568118611Snjl		 * Make sure the operator is a valid one. If it isn't a
569118611Snjl		 * known relational operator, pretend we got a
570118611Snjl		 * != 0 comparison.
571118611Snjl		 */
572118611Snjl		op = condExpr;
573118611Snjl		switch (*condExpr) {
574118611Snjl		    case '!':
575118611Snjl		    case '=':
576118611Snjl		    case '<':
577118611Snjl		    case '>':
578118611Snjl			if (condExpr[1] == '=') {
579118611Snjl			    condExpr += 2;
580118611Snjl			} else {
581118611Snjl			    condExpr += 1;
582118611Snjl			}
583118611Snjl			break;
584118611Snjl		    default:
585118611Snjl			op = "!=";
586118611Snjl			rhs = "0";
587118611Snjl
588118611Snjl			goto do_compare;
589118611Snjl		}
590118611Snjl		while (isspace((unsigned char)*condExpr)) {
591118611Snjl		    condExpr++;
592118611Snjl		}
593118611Snjl		if (*condExpr == '\0') {
594118611Snjl		    Parse_Error(PARSE_WARNING,
595118611Snjl				"Missing right-hand-side of operator");
596118611Snjl		    goto error;
597118611Snjl		}
598118611Snjl		rhs = condExpr;
599118611Snjldo_compare:
600118611Snjl		if (*rhs == '"') {
601118611Snjl		    /*
602118611Snjl		     * Doing a string comparison. Only allow == and != for
603118611Snjl		     * operators.
604118611Snjl		     */
605118611Snjl		    char    *string;
606118611Snjl		    char    *cp, *cp2;
607118611Snjl		    int	    qt;
608118611Snjl		    Buffer  buf;
609118611Snjl
610118611Snjldo_string_compare:
611118611Snjl		    if (((*op != '!') && (*op != '=')) || (op[1] != '=')) {
612118611Snjl			Parse_Error(PARSE_WARNING,
613118611Snjl		"String comparison operator should be either == or !=");
614118611Snjl			goto error;
615118611Snjl		    }
616118611Snjl
617118611Snjl		    buf = Buf_Init(0);
618118611Snjl		    qt = *rhs == '"' ? 1 : 0;
619118611Snjl
620118611Snjl		    for (cp = &rhs[qt];
621118611Snjl			 ((qt && (*cp != '"')) ||
622118611Snjl			  (!qt && strchr(" \t)", *cp) == NULL)) &&
623118611Snjl			 (*cp != '\0'); cp++) {
624118611Snjl			if ((*cp == '\\') && (cp[1] != '\0')) {
625118611Snjl			    /*
626118611Snjl			     * Backslash escapes things -- skip over next
627118611Snjl			     * character, if it exists.
628118611Snjl			     */
629118611Snjl			    cp++;
630118611Snjl			    Buf_AddByte(buf, (Byte)*cp);
631118611Snjl			} else if (*cp == '$') {
632118611Snjl			    size_t len;
633118611Snjl			    Boolean freeIt;
634118611Snjl
635118611Snjl			    cp2 = Var_Parse(cp, VAR_CMD, doEval, &len, &freeIt);
636118611Snjl			    if (cp2 != var_Error) {
637118611Snjl				Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
638118611Snjl				if (freeIt) {
639118611Snjl				    free(cp2);
640118611Snjl				}
641118611Snjl				cp += len - 1;
642118611Snjl			    } else {
643118611Snjl				Buf_AddByte(buf, (Byte)*cp);
644118611Snjl			    }
645118611Snjl			} else {
646118611Snjl			    Buf_AddByte(buf, (Byte)*cp);
647118611Snjl			}
648118611Snjl		    }
649118611Snjl
650118611Snjl		    Buf_AddByte(buf, (Byte)0);
651118611Snjl
652118611Snjl		    string = (char *)Buf_GetAll(buf, (size_t *)NULL);
653118611Snjl		    Buf_Destroy(buf, FALSE);
654118611Snjl
655118611Snjl		    DEBUGF(COND, ("lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
656118611Snjl			   lhs, string, op));
657118611Snjl		    /*
658118611Snjl		     * Null-terminate rhs and perform the comparison.
659118611Snjl		     * t is set to the result.
660118611Snjl		     */
661118611Snjl		    if (*op == '=') {
662118611Snjl			t = strcmp(lhs, string) ? False : True;
663118611Snjl		    } else {
664118611Snjl			t = strcmp(lhs, string) ? True : False;
665118611Snjl		    }
666118611Snjl		    free(string);
667118611Snjl		    if (rhs == condExpr) {
668118611Snjl		    	if (!qt && *cp == ')')
669118611Snjl			    condExpr = cp;
670118611Snjl			else
671118611Snjl			    condExpr = cp + 1;
672118611Snjl		    }
673118611Snjl		} else {
674118611Snjl		    /*
675118611Snjl		     * rhs is either a float or an integer. Convert both the
676118611Snjl		     * lhs and the rhs to a double and compare the two.
677118611Snjl		     */
678118611Snjl		    double  	left, right;
679118611Snjl		    char    	*string;
680118611Snjl
681118611Snjl		    if (*CondCvtArg(lhs, &left) != '\0')
682118611Snjl			goto do_string_compare;
683118611Snjl		    if (*rhs == '$') {
684118611Snjl			size_t len;
685118611Snjl			Boolean	freeIt;
686118611Snjl
687118611Snjl			string = Var_Parse(rhs, VAR_CMD, doEval, &len, &freeIt);
688118611Snjl			if (string == var_Error) {
689118611Snjl			    right = 0.0;
690118611Snjl			} else {
691118611Snjl			    if (*CondCvtArg(string, &right) != '\0') {
692118611Snjl				if (freeIt)
693118611Snjl				    free(string);
694118611Snjl				goto do_string_compare;
695118611Snjl			    }
696118611Snjl			    if (freeIt)
697118611Snjl				free(string);
698118611Snjl			    if (rhs == condExpr)
699118611Snjl				condExpr += len;
700118611Snjl			}
701118611Snjl		    } else {
702118611Snjl			char *c = CondCvtArg(rhs, &right);
703118611Snjl			if (c == rhs)
704118611Snjl			    goto do_string_compare;
705118611Snjl			if (rhs == condExpr) {
706118611Snjl			    /*
707118611Snjl			     * Skip over the right-hand side
708118611Snjl			     */
709118611Snjl			    condExpr = c;
710118611Snjl			}
711118611Snjl		    }
712118611Snjl
713118611Snjl		    DEBUGF(COND, ("left = %f, right = %f, op = %.2s\n", left,
714118611Snjl			   right, op));
715118611Snjl		    switch (op[0]) {
716118611Snjl		    case '!':
717118611Snjl			if (op[1] != '=') {
718118611Snjl			    Parse_Error(PARSE_WARNING,
719118611Snjl					"Unknown operator");
720118611Snjl			    goto error;
721118611Snjl			}
722118611Snjl			t = (left != right ? True : False);
723118611Snjl			break;
724118611Snjl		    case '=':
725118611Snjl			if (op[1] != '=') {
726118611Snjl			    Parse_Error(PARSE_WARNING,
727118611Snjl					"Unknown operator");
728118611Snjl			    goto error;
729118611Snjl			}
730118611Snjl			t = (left == right ? True : False);
731118611Snjl			break;
732118611Snjl		    case '<':
733118611Snjl			if (op[1] == '=') {
734118611Snjl			    t = (left <= right ? True : False);
735118611Snjl			} else {
736118611Snjl			    t = (left < right ? True : False);
737118611Snjl			}
738118611Snjl			break;
739118611Snjl		    case '>':
740118611Snjl			if (op[1] == '=') {
741118611Snjl			    t = (left >= right ? True : False);
742118611Snjl			} else {
743118611Snjl			    t = (left > right ? True : False);
744118611Snjl			}
745118611Snjl			break;
746118611Snjl		    default:
747118611Snjl			break;
748118611Snjl		    }
749118611Snjl		}
750118611Snjlerror:
751118611Snjl		if (doFree)
752118611Snjl		    free(lhs);
753118611Snjl		break;
754118611Snjl	    }
755118611Snjl	    default: {
756118611Snjl		Boolean (*evalProc)(int, char *);
757118611Snjl		Boolean invert = FALSE;
758118611Snjl		char	*arg;
759118611Snjl		int	arglen;
760118611Snjl
761118611Snjl		if (strncmp(condExpr, "defined", 7) == 0) {
762118611Snjl		    /*
763118611Snjl		     * Use CondDoDefined to evaluate the argument and
764118611Snjl		     * CondGetArg to extract the argument from the 'function
765118611Snjl		     * call'.
766118611Snjl		     */
767118611Snjl		    evalProc = CondDoDefined;
768118611Snjl		    condExpr += 7;
769118611Snjl		    arglen = CondGetArg(&condExpr, &arg, "defined", TRUE);
770118611Snjl		    if (arglen == 0) {
771118611Snjl			condExpr -= 7;
772118611Snjl			goto use_default;
773118611Snjl		    }
774118611Snjl		} else if (strncmp(condExpr, "make", 4) == 0) {
775118611Snjl		    /*
776118611Snjl		     * Use CondDoMake to evaluate the argument and
777118611Snjl		     * CondGetArg to extract the argument from the 'function
778118611Snjl		     * call'.
779118611Snjl		     */
780118611Snjl		    evalProc = CondDoMake;
781118611Snjl		    condExpr += 4;
782118611Snjl		    arglen = CondGetArg(&condExpr, &arg, "make", TRUE);
783118611Snjl		    if (arglen == 0) {
784118611Snjl			condExpr -= 4;
785118611Snjl			goto use_default;
786118611Snjl		    }
787118611Snjl		} else if (strncmp(condExpr, "exists", 6) == 0) {
788118611Snjl		    /*
789118611Snjl		     * Use CondDoExists to evaluate the argument and
790118611Snjl		     * CondGetArg to extract the argument from the
791118611Snjl		     * 'function call'.
792118611Snjl		     */
793118611Snjl		    evalProc = CondDoExists;
794118611Snjl		    condExpr += 6;
795118611Snjl		    arglen = CondGetArg(&condExpr, &arg, "exists", TRUE);
796118611Snjl		    if (arglen == 0) {
797118611Snjl			condExpr -= 6;
798118611Snjl			goto use_default;
799118611Snjl		    }
800118611Snjl		} else if (strncmp(condExpr, "empty", 5) == 0) {
801118611Snjl		    /*
802118611Snjl		     * Use Var_Parse to parse the spec in parens and return
803118611Snjl		     * True if the resulting string is empty.
804118611Snjl		     */
805118611Snjl		    size_t length;
806118611Snjl		    Boolean doFree;
807118611Snjl		    char    *val;
808118611Snjl
809118611Snjl		    condExpr += 5;
810118611Snjl
811118611Snjl		    for (arglen = 0;
812118611Snjl			 condExpr[arglen] != '(' && condExpr[arglen] != '\0';
813118611Snjl			 arglen += 1)
814118611Snjl			continue;
815118611Snjl
816118611Snjl		    if (condExpr[arglen] != '\0') {
817118611Snjl			val = Var_Parse(&condExpr[arglen - 1], VAR_CMD,
818118611Snjl					FALSE, &length, &doFree);
819118611Snjl			if (val == var_Error) {
820118611Snjl			    t = Err;
821118611Snjl			} else {
822118611Snjl			    /*
823118611Snjl			     * A variable is empty when it just contains
824118611Snjl			     * spaces... 4/15/92, christos
825118611Snjl			     */
826118611Snjl			    char *p;
827118611Snjl			    for (p = val; *p && isspace((unsigned char)*p); p++)
828118611Snjl				continue;
829118611Snjl			    t = (*p == '\0') ? True : False;
830118611Snjl			}
831118611Snjl			if (doFree) {
832118611Snjl			    free(val);
833118611Snjl			}
834118611Snjl			/*
835118611Snjl			 * Advance condExpr to beyond the closing ). Note that
836118611Snjl			 * we subtract one from arglen + length b/c length
837118611Snjl			 * is calculated from condExpr[arglen - 1].
838118611Snjl			 */
839118611Snjl			condExpr += arglen + length - 1;
840118611Snjl		    } else {
841118611Snjl			condExpr -= 5;
842118611Snjl			goto use_default;
843118611Snjl		    }
844118611Snjl		    break;
845118611Snjl		} else if (strncmp(condExpr, "target", 6) == 0) {
846118611Snjl		    /*
847118611Snjl		     * Use CondDoTarget to evaluate the argument and
848118611Snjl		     * CondGetArg to extract the argument from the
849118611Snjl		     * 'function call'.
850118611Snjl		     */
851118611Snjl		    evalProc = CondDoTarget;
852118611Snjl		    condExpr += 6;
853118611Snjl		    arglen = CondGetArg(&condExpr, &arg, "target", TRUE);
854118611Snjl		    if (arglen == 0) {
855118611Snjl			condExpr -= 6;
856118611Snjl			goto use_default;
857118611Snjl		    }
858118611Snjl		} else {
859118611Snjl		    /*
860118611Snjl		     * The symbol is itself the argument to the default
861118611Snjl		     * function. We advance condExpr to the end of the symbol
862118611Snjl		     * by hand (the next whitespace, closing paren or
863118611Snjl		     * binary operator) and set to invert the evaluation
864118611Snjl		     * function if condInvert is TRUE.
865118611Snjl		     */
866118611Snjl		use_default:
867118611Snjl		    invert = condInvert;
868118611Snjl		    evalProc = condDefProc;
869118611Snjl		    arglen = CondGetArg(&condExpr, &arg, "", FALSE);
870118611Snjl		}
871118611Snjl
872118611Snjl		/*
873118611Snjl		 * Evaluate the argument using the set function. If invert
874118611Snjl		 * is TRUE, we invert the sense of the function.
875118611Snjl		 */
876118611Snjl		t = (!doEval || (* evalProc) (arglen, arg) ?
877118611Snjl		     (invert ? False : True) :
878118611Snjl		     (invert ? True : False));
879118611Snjl		free(arg);
880118611Snjl		break;
881118611Snjl	    }
882118611Snjl	}
883118611Snjl    } else {
884118611Snjl	t = condPushBack;
885118611Snjl	condPushBack = None;
886118611Snjl    }
887118611Snjl    return (t);
888118611Snjl}
889118611Snjl
890118611Snjl/*-
891118611Snjl *-----------------------------------------------------------------------
892118611Snjl * CondT --
893118611Snjl *	Parse a single term in the expression. This consists of a terminal
894118611Snjl *	symbol or Not and a terminal symbol (not including the binary
895118611Snjl *	operators):
896118611Snjl *	    T -> defined(variable) | make(target) | exists(file) | symbol
897118611Snjl *	    T -> ! T | ( E )
898138287Smarks *
899138287Smarks * Results:
900138287Smarks *	True, False or Err.
901118611Snjl *
902118611Snjl * Side Effects:
903118611Snjl *	Tokens are consumed.
904118611Snjl *
905118611Snjl *-----------------------------------------------------------------------
906118611Snjl */
907118611Snjlstatic Token
908118611SnjlCondT(Boolean doEval)
909118611Snjl{
910118611Snjl    Token   t;
911118611Snjl
912118611Snjl    t = CondToken(doEval);
913118611Snjl
914118611Snjl    if (t == EndOfFile) {
915118611Snjl	/*
916118611Snjl	 * If we reached the end of the expression, the expression
917118611Snjl	 * is malformed...
918118611Snjl	 */
919118611Snjl	t = Err;
920118611Snjl    } else if (t == LParen) {
921118611Snjl	/*
922118611Snjl	 * T -> ( E )
923118611Snjl	 */
924118611Snjl	t = CondE(doEval);
925118611Snjl	if (t != Err) {
926118611Snjl	    if (CondToken(doEval) != RParen) {
927118611Snjl		t = Err;
928118611Snjl	    }
929118611Snjl	}
930118611Snjl    } else if (t == Not) {
931118611Snjl	t = CondT(doEval);
932118611Snjl	if (t == True) {
933118611Snjl	    t = False;
934118611Snjl	} else if (t == False) {
935118611Snjl	    t = True;
936118611Snjl	}
937118611Snjl    }
938118611Snjl    return (t);
939118611Snjl}
940118611Snjl
941118611Snjl/*-
942118611Snjl *-----------------------------------------------------------------------
943118611Snjl * CondF --
944118611Snjl *	Parse a conjunctive factor (nice name, wot?)
945118611Snjl *	    F -> T && F | T
946118611Snjl *
947118611Snjl * Results:
948118611Snjl *	True, False or Err
949118611Snjl *
950118611Snjl * Side Effects:
951118611Snjl *	Tokens are consumed.
952118611Snjl *
953118611Snjl *-----------------------------------------------------------------------
954118611Snjl */
955118611Snjlstatic Token
956118611SnjlCondF(Boolean doEval)
957118611Snjl{
958118611Snjl    Token   l, o;
959118611Snjl
960118611Snjl    l = CondT(doEval);
961118611Snjl    if (l != Err) {
962118611Snjl	o = CondToken(doEval);
963118611Snjl
964118611Snjl	if (o == And) {
965118611Snjl	    /*
966118611Snjl	     * F -> T && F
967118611Snjl	     *
968118611Snjl	     * If T is False, the whole thing will be False, but we have to
969118611Snjl	     * parse the r.h.s. anyway (to throw it away).
970118611Snjl	     * If T is True, the result is the r.h.s., be it an Err or no.
971118611Snjl	     */
972118611Snjl	    if (l == True) {
973118611Snjl		l = CondF(doEval);
974118611Snjl	    } else {
975118611Snjl		 CondF(FALSE);
976118611Snjl	    }
977118611Snjl	} else {
978118611Snjl	    /*
979118611Snjl	     * F -> T
980118611Snjl	     */
981118611Snjl	    CondPushBack(o);
982118611Snjl	}
983118611Snjl    }
984118611Snjl    return (l);
985118611Snjl}
986118611Snjl
987118611Snjl/*-
988118611Snjl *-----------------------------------------------------------------------
989118611Snjl * CondE --
990118611Snjl *	Main expression production.
991118611Snjl *	    E -> F || E | F
992118611Snjl *
993118611Snjl * Results:
994118611Snjl *	True, False or Err.
995118611Snjl *
996118611Snjl * Side Effects:
997118611Snjl *	Tokens are, of course, consumed.
998118611Snjl *
999118611Snjl *-----------------------------------------------------------------------
1000118611Snjl */
1001118611Snjlstatic Token
1002118611SnjlCondE(Boolean doEval)
1003118611Snjl{
1004118611Snjl    Token   l, o;
1005118611Snjl
1006118611Snjl    l = CondF(doEval);
1007118611Snjl    if (l != Err) {
1008118611Snjl	o = CondToken(doEval);
1009118611Snjl
1010118611Snjl	if (o == Or) {
1011118611Snjl	    /*
1012118611Snjl	     * E -> F || E
1013118611Snjl	     *
1014118611Snjl	     * A similar thing occurs for ||, except that here we make sure
1015118611Snjl	     * the l.h.s. is False before we bother to evaluate the r.h.s.
1016118611Snjl	     * Once again, if l is False, the result is the r.h.s. and once
1017118611Snjl	     * again if l is True, we parse the r.h.s. to throw it away.
1018118611Snjl	     */
1019118611Snjl	    if (l == False) {
1020118611Snjl		l = CondE(doEval);
1021118611Snjl	    } else {
1022118611Snjl		 CondE(FALSE);
1023118611Snjl	    }
1024118611Snjl	} else {
1025118611Snjl	    /*
1026118611Snjl	     * E -> F
1027118611Snjl	     */
1028118611Snjl	    CondPushBack(o);
1029118611Snjl	}
1030118611Snjl    }
1031118611Snjl    return (l);
1032118611Snjl}
1033118611Snjl
1034118611Snjl/*-
1035118611Snjl *-----------------------------------------------------------------------
1036118611Snjl * Cond_Eval --
1037118611Snjl *	Evaluate the conditional in the passed line. The line
1038118611Snjl *	looks like this:
1039118611Snjl *	    #<cond-type> <expr>
1040118611Snjl *	where <cond-type> is any of if, ifmake, ifnmake, ifdef,
1041118611Snjl *	ifndef, elif, elifmake, elifnmake, elifdef, elifndef
1042118611Snjl *	and <expr> consists of &&, ||, !, make(target), defined(variable)
1043118611Snjl *	and parenthetical groupings thereof.
1044118611Snjl *
1045118611Snjl * Results:
1046118611Snjl *	COND_PARSE	if should parse lines after the conditional
1047118611Snjl *	COND_SKIP	if should skip lines after the conditional
1048118611Snjl *	COND_INVALID  	if not a valid conditional.
1049118611Snjl *
1050118611Snjl * Side Effects:
1051118611Snjl *	None.
1052118611Snjl *
1053118611Snjl *-----------------------------------------------------------------------
1054118611Snjl */
1055118611Snjlint
1056118611SnjlCond_Eval(char *line)
1057118611Snjl{
1058118611Snjl    struct If	    *ifp;
1059118611Snjl    Boolean 	    isElse;
1060118611Snjl    Boolean 	    value = FALSE;
1061118611Snjl    int	    	    level;  	/* Level at which to report errors. */
1062118611Snjl    int		    lineno;
1063118611Snjl
1064118611Snjl    level = PARSE_FATAL;
1065118611Snjl    lineno = curFile.lineno;
1066118611Snjl
1067118611Snjl    for (line++; *line == ' ' || *line == '\t'; line++) {
1068118611Snjl	continue;
1069118611Snjl    }
1070118611Snjl
1071118611Snjl    /*
1072118611Snjl     * Find what type of if we're dealing with. The result is left
1073118611Snjl     * in ifp and isElse is set TRUE if it's an elif line.
1074118611Snjl     */
1075118611Snjl    if (line[0] == 'e' && line[1] == 'l') {
1076118611Snjl	line += 2;
1077118611Snjl	isElse = TRUE;
1078118611Snjl    } else if (strncmp(line, "endif", 5) == 0) {
1079118611Snjl	/*
1080118611Snjl	 * End of a conditional section. If skipIfLevel is non-zero, that
1081118611Snjl	 * conditional was skipped, so lines following it should also be
1082118611Snjl	 * skipped. Hence, we return COND_SKIP. Otherwise, the conditional
1083118611Snjl	 * was read so succeeding lines should be parsed (think about it...)
1084118611Snjl	 * so we return COND_PARSE, unless this endif isn't paired with
1085118611Snjl	 * a decent if.
1086118611Snjl	 */
1087118611Snjl	if (skipIfLevel != 0) {
1088118611Snjl	    skipIfLevel -= 1;
1089118611Snjl	    return (COND_SKIP);
1090118611Snjl	} else {
1091118611Snjl	    if (condTop == MAXIF) {
1092118611Snjl		Parse_Error(level, "if-less endif");
1093118611Snjl		return (COND_INVALID);
1094118611Snjl	    } else {
1095118611Snjl		skipLine = FALSE;
1096118611Snjl		condTop += 1;
1097118611Snjl		return (COND_PARSE);
1098118611Snjl	    }
1099118611Snjl	}
1100118611Snjl    } else {
1101118611Snjl	isElse = FALSE;
1102118611Snjl    }
1103118611Snjl
1104118611Snjl    /*
1105118611Snjl     * Figure out what sort of conditional it is -- what its default
1106118611Snjl     * function is, etc. -- by looking in the table of valid "ifs"
1107118611Snjl     */
1108118611Snjl    for (ifp = ifs; ifp->form != NULL; ifp++) {
1109118611Snjl	if (strncmp(ifp->form, line, ifp->formlen) == 0) {
1110118611Snjl	    break;
1111118611Snjl	}
1112118611Snjl    }
1113118611Snjl
1114118611Snjl    if (ifp->form == NULL) {
1115118611Snjl	/*
1116118611Snjl	 * Nothing fit. If the first word on the line is actually
1117118611Snjl	 * "else", it's a valid conditional whose value is the inverse
1118118611Snjl	 * of the previous if we parsed.
1119118611Snjl	 */
1120118611Snjl	if (isElse && (line[0] == 's') && (line[1] == 'e')) {
1121118611Snjl	    if (condTop == MAXIF) {
1122118611Snjl		Parse_Error(level, "if-less else");
1123118611Snjl		return (COND_INVALID);
1124118611Snjl	    } else if (skipIfLevel == 0) {
1125118611Snjl		value = !condStack[condTop];
1126118611Snjl		lineno = condLineno[condTop];
1127118611Snjl	    } else {
1128118611Snjl		return (COND_SKIP);
1129118611Snjl	    }
1130118611Snjl	} else {
1131118611Snjl	    /*
1132118611Snjl	     * Not a valid conditional type. No error...
1133118611Snjl	     */
1134118611Snjl	    return (COND_INVALID);
1135118611Snjl	}
1136118611Snjl    } else {
1137118611Snjl	if (isElse) {
1138118611Snjl	    if (condTop == MAXIF) {
1139118611Snjl		Parse_Error(level, "if-less elif");
1140118611Snjl		return (COND_INVALID);
1141118611Snjl	    } else if (skipIfLevel != 0) {
1142118611Snjl		/*
1143118611Snjl		 * If skipping this conditional, just ignore the whole thing.
1144118611Snjl		 * If we don't, the user might be employing a variable that's
1145118611Snjl		 * undefined, for which there's an enclosing ifdef that
1146118611Snjl		 * we're skipping...
1147118611Snjl		 */
1148118611Snjl	        skipIfLineno[skipIfLevel - 1] = lineno;
1149118611Snjl		return (COND_SKIP);
1150118611Snjl	    }
1151118611Snjl	} else if (skipLine) {
1152118611Snjl	    /*
1153118611Snjl	     * Don't even try to evaluate a conditional that's not an else if
1154118611Snjl	     * we're skipping things...
1155118611Snjl	     */
1156118611Snjl	    skipIfLineno[skipIfLevel] = lineno;
1157118611Snjl	    skipIfLevel += 1;
1158	    return (COND_SKIP);
1159	}
1160
1161	/*
1162	 * Initialize file-global variables for parsing
1163	 */
1164	condDefProc = ifp->defProc;
1165	condInvert = ifp->doNot;
1166
1167	line += ifp->formlen;
1168
1169	while (*line == ' ' || *line == '\t') {
1170	    line++;
1171	}
1172
1173	condExpr = line;
1174	condPushBack = None;
1175
1176	switch (CondE(TRUE)) {
1177	    case True:
1178		if (CondToken(TRUE) == EndOfFile) {
1179		    value = TRUE;
1180		    break;
1181		}
1182		goto err;
1183		/*FALLTHRU*/
1184	    case False:
1185		if (CondToken(TRUE) == EndOfFile) {
1186		    value = FALSE;
1187		    break;
1188		}
1189		/*FALLTHRU*/
1190	    case Err:
1191	    err:
1192		Parse_Error(level, "Malformed conditional (%s)",
1193			     line);
1194		return (COND_INVALID);
1195	    default:
1196		break;
1197	}
1198    }
1199    if (!isElse) {
1200	condTop -= 1;
1201    } else if ((skipIfLevel != 0) || condStack[condTop]) {
1202	/*
1203	 * If this is an else-type conditional, it should only take effect
1204	 * if its corresponding if was evaluated and FALSE. If its if was
1205	 * TRUE or skipped, we return COND_SKIP (and start skipping in case
1206	 * we weren't already), leaving the stack unmolested so later elif's
1207	 * don't screw up...
1208	 */
1209	skipLine = TRUE;
1210	return (COND_SKIP);
1211    }
1212
1213    if (condTop < 0) {
1214	/*
1215	 * This is the one case where we can definitely proclaim a fatal
1216	 * error. If we don't, we're hosed.
1217	 */
1218	Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF);
1219	return (COND_INVALID);
1220    } else {
1221	condStack[condTop] = value;
1222	condLineno[condTop] = lineno;
1223	skipLine = !value;
1224	return (value ? COND_PARSE : COND_SKIP);
1225    }
1226}
1227
1228/*-
1229 *-----------------------------------------------------------------------
1230 * Cond_End --
1231 *	Make sure everything's clean at the end of a makefile.
1232 *
1233 * Results:
1234 *	None.
1235 *
1236 * Side Effects:
1237 *	Parse_Error will be called if open conditionals are around.
1238 *
1239 *-----------------------------------------------------------------------
1240 */
1241void
1242Cond_End(void)
1243{
1244    int level;
1245
1246    if (condTop != MAXIF) {
1247	Parse_Error(PARSE_FATAL, "%d open conditional%s:",
1248	    MAXIF - condTop + skipIfLevel,
1249 	    MAXIF - condTop + skipIfLevel== 1 ? "" : "s");
1250
1251	for (level = skipIfLevel; level > 0; level--)
1252		Parse_Error(PARSE_FATAL, "\t%*sat line %d (skipped)",
1253		    MAXIF - condTop + level + 1, "", skipIfLineno[level - 1]);
1254	for (level = condTop; level < MAXIF; level++)
1255		Parse_Error(PARSE_FATAL, "\t%*sat line %d "
1256		    "(evaluated to %s)", MAXIF - level + skipIfLevel, "",
1257		    condLineno[level], condStack[level] ? "true" : "false");
1258    }
1259    condTop = MAXIF;
1260}
1261