1141104Sharti/*-
294589Sobrien * Copyright (c) 1988, 1989, 1990, 1993
394589Sobrien *	The Regents of the University of California.  All rights reserved.
45814Sjkh * Copyright (c) 1988, 1989 by Adam de Boor
51590Srgrimes * Copyright (c) 1989 by Berkeley Softworks
61590Srgrimes * All rights reserved.
71590Srgrimes *
81590Srgrimes * This code is derived from software contributed to Berkeley by
91590Srgrimes * Adam de Boor.
101590Srgrimes *
111590Srgrimes * Redistribution and use in source and binary forms, with or without
121590Srgrimes * modification, are permitted provided that the following conditions
131590Srgrimes * are met:
141590Srgrimes * 1. Redistributions of source code must retain the above copyright
151590Srgrimes *    notice, this list of conditions and the following disclaimer.
161590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
171590Srgrimes *    notice, this list of conditions and the following disclaimer in the
181590Srgrimes *    documentation and/or other materials provided with the distribution.
191590Srgrimes * 3. All advertising materials mentioning features or use of this software
201590Srgrimes *    must display the following acknowledgement:
211590Srgrimes *	This product includes software developed by the University of
221590Srgrimes *	California, Berkeley and its contributors.
231590Srgrimes * 4. Neither the name of the University nor the names of its contributors
241590Srgrimes *    may be used to endorse or promote products derived from this software
251590Srgrimes *    without specific prior written permission.
261590Srgrimes *
271590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
281590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
291590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
301590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
311590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
321590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
331590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
341590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
351590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
361590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
371590Srgrimes * SUCH DAMAGE.
3862833Swsanchez *
3962833Swsanchez * @(#)cond.c	8.2 (Berkeley) 1/2/94
401590Srgrimes */
411590Srgrimes
4262833Swsanchez#include <sys/cdefs.h>
4394587Sobrien__FBSDID("$FreeBSD$");
441590Srgrimes
45144473Sharti/*
46144473Sharti * Functions to handle conditionals in a makefile.
471590Srgrimes *
481590Srgrimes * Interface:
49144473Sharti *	Cond_Eval	Evaluate the conditional in the passed line.
501590Srgrimes */
511590Srgrimes
52141104Sharti#include <ctype.h>
53141104Sharti#include <string.h>
54141104Sharti#include <stdlib.h>
551590Srgrimes
56141133Sharti#include "buf.h"
57141104Sharti#include "cond.h"
58141104Sharti#include "dir.h"
59141104Sharti#include "globals.h"
60141104Sharti#include "GNode.h"
61141104Sharti#include "make.h"
62141104Sharti#include "parse.h"
63141104Sharti#include "str.h"
64141104Sharti#include "targ.h"
65141104Sharti#include "util.h"
66141104Sharti#include "var.h"
67141104Sharti
681590Srgrimes/*
691590Srgrimes * The parsing of conditional expressions is based on this grammar:
701590Srgrimes *	E -> F || E
711590Srgrimes *	E -> F
721590Srgrimes *	F -> T && F
731590Srgrimes *	F -> T
741590Srgrimes *	T -> defined(variable)
751590Srgrimes *	T -> make(target)
761590Srgrimes *	T -> exists(file)
771590Srgrimes *	T -> empty(varspec)
781590Srgrimes *	T -> target(name)
791590Srgrimes *	T -> symbol
801590Srgrimes *	T -> $(varspec) op value
811590Srgrimes *	T -> $(varspec) == "string"
821590Srgrimes *	T -> $(varspec) != "string"
831590Srgrimes *	T -> ( E )
841590Srgrimes *	T -> ! T
851590Srgrimes *	op -> == | != | > | < | >= | <=
861590Srgrimes *
871590Srgrimes * 'symbol' is some other symbol to which the default function (condDefProc)
881590Srgrimes * is applied.
891590Srgrimes *
901590Srgrimes * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
911590Srgrimes * will return And for '&' and '&&', Or for '|' and '||', Not for '!',
921590Srgrimes * LParen for '(', RParen for ')' and will evaluate the other terminal
931590Srgrimes * symbols, using either the default function or the function given in the
941590Srgrimes * terminal, and return the result as either True or False.
951590Srgrimes *
961590Srgrimes * All Non-Terminal functions (CondE, CondF and CondT) return Err on error.
971590Srgrimes */
981590Srgrimestypedef enum {
99144473Sharti	And,
100144473Sharti	Or,
101144473Sharti	Not,
102144473Sharti	True,
103144473Sharti	False,
104144473Sharti	LParen,
105144473Sharti	RParen,
106144473Sharti	EndOfFile,
107144473Sharti	None,
108144473Sharti	Err
1091590Srgrimes} Token;
1101590Srgrimes
111141254Shartitypedef Boolean CondProc(int, char *);
112141254Sharti
1131590Srgrimes/*-
1141590Srgrimes * Structures to handle elegantly the different forms of #if's. The
1151590Srgrimes * last two fields are stored in condInvert and condDefProc, respectively.
1161590Srgrimes */
11792921Simpstatic void CondPushBack(Token);
118141192Shartistatic int CondGetArg(char **, char **, const char *, Boolean);
119141254Shartistatic CondProc	CondDoDefined;
120141254Shartistatic CondProc	CondDoMake;
121141254Shartistatic CondProc	CondDoExists;
122141254Shartistatic CondProc	CondDoTarget;
123141254Shartistatic char *CondCvtArg(char *, double *);
12492921Simpstatic Token CondToken(Boolean);
12592921Simpstatic Token CondT(Boolean);
12692921Simpstatic Token CondF(Boolean);
12792921Simpstatic Token CondE(Boolean);
1281590Srgrimes
129144894Shartistatic const struct If {
130144473Sharti	Boolean	doNot;		/* TRUE if default function should be negated */
131144473Sharti	CondProc *defProc;	/* Default function to apply */
132144894Sharti	Boolean	isElse;		/* actually el<XXX> */
1331590Srgrimes} ifs[] = {
134144894Sharti	[COND_IF] =		{ FALSE,	CondDoDefined,	FALSE },
135144894Sharti	[COND_IFDEF] =		{ FALSE,	CondDoDefined,	FALSE },
136144894Sharti	[COND_IFNDEF] =		{ TRUE,		CondDoDefined,	FALSE },
137144894Sharti	[COND_IFMAKE] =		{ FALSE,	CondDoMake,	FALSE },
138144894Sharti	[COND_IFNMAKE] =	{ TRUE,		CondDoMake,	FALSE },
139144894Sharti	[COND_ELIF] =		{ FALSE,	CondDoDefined,	TRUE },
140144894Sharti	[COND_ELIFDEF] =	{ FALSE,	CondDoDefined,	TRUE },
141144894Sharti	[COND_ELIFNDEF] =	{ TRUE,		CondDoDefined,	TRUE },
142144894Sharti	[COND_ELIFMAKE] =	{ FALSE,	CondDoMake,	TRUE },
143144894Sharti	[COND_ELIFNMAKE] =	{ TRUE,		CondDoMake,	TRUE },
1441590Srgrimes};
1451590Srgrimes
146144473Shartistatic Boolean	condInvert;	/* Invert the default function */
147144473Shartistatic CondProc	*condDefProc;	/* default function to apply */
148144473Shartistatic char	*condExpr;	/* The expression to parse */
149144473Shartistatic Token	condPushBack = None; /* Single push-back token in parsing */
1501590Srgrimes
151144473Sharti#define MAXIF	30	/* greatest depth of #if'ing */
1521590Srgrimes
153144473Shartistatic Boolean	condStack[MAXIF];	/* Stack of conditionals's values */
154144473Shartistatic int	condLineno[MAXIF];	/* Line numbers of the opening .if */
155144473Shartistatic int	condTop = MAXIF;	/* Top-most conditional */
156144473Shartistatic int	skipIfLevel = 0;	/* Depth of skipped conditionals */
157144473Shartistatic int	skipIfLineno[MAXIF];	/* Line numbers of skipped .ifs */
158144894ShartiBoolean		skipLine = FALSE;	/* Whether the parse module is skipping
1591590Srgrimes					 * lines */
1601590Srgrimes
161144473Sharti/**
162144473Sharti * CondPushBack
1631590Srgrimes *	Push back the most recent token read. We only need one level of
1641590Srgrimes *	this, so the thing is just stored in 'condPushback'.
1651590Srgrimes *
1661590Srgrimes * Side Effects:
1671590Srgrimes *	condPushback is overwritten.
1681590Srgrimes */
1691590Srgrimesstatic void
170138232ShartiCondPushBack(Token t)
1711590Srgrimes{
172138264Sharti
173144473Sharti	condPushBack = t;
1741590Srgrimes}
175138232Sharti
176144473Sharti/**
177144473Sharti * CondGetArg
178104696Sjmallett *	Find the argument of a built-in function.  parens is set to TRUE
179104696Sjmallett *	if the arguments are bounded by parens.
1801590Srgrimes *
1811590Srgrimes * Results:
1821590Srgrimes *	The length of the argument and the address of the argument.
1831590Srgrimes *
1841590Srgrimes * Side Effects:
1851590Srgrimes *	The pointer is set to point to the closing parenthesis of the
1861590Srgrimes *	function call.
1871590Srgrimes */
1881590Srgrimesstatic int
189141192ShartiCondGetArg(char **linePtr, char **argPtr, const char *func, Boolean parens)
1901590Srgrimes{
191144473Sharti	char	*cp;
192144473Sharti	size_t	argLen;
193144473Sharti	Buffer	*buf;
1941590Srgrimes
195144473Sharti	cp = *linePtr;
196144473Sharti	if (parens) {
197144473Sharti		while (*cp != '(' && *cp != '\0') {
198144473Sharti			cp++;
199144473Sharti		}
200144473Sharti		if (*cp == '(') {
201144473Sharti			cp++;
202144473Sharti		}
2031590Srgrimes	}
204144473Sharti
205144473Sharti	if (*cp == '\0') {
206144473Sharti		/*
207144473Sharti		 * No arguments whatsoever. Because 'make' and 'defined'
208144473Sharti		 * aren't really "reserved words", we don't print a message.
209144473Sharti		 * I think this is better than hitting the user with a warning
210144473Sharti		 * message every time s/he uses the word 'make' or 'defined'
211144473Sharti		 * at the beginning of a symbol...
212144473Sharti		 */
213144473Sharti		*argPtr = cp;
214144473Sharti		return (0);
2151590Srgrimes	}
2161590Srgrimes
217144473Sharti	while (*cp == ' ' || *cp == '\t') {
218144473Sharti		cp++;
219144473Sharti	}
220144473Sharti
2211590Srgrimes	/*
222144473Sharti	 * Create a buffer for the argument and start it out at 16 characters
223144473Sharti	 * long. Why 16? Why not?
2241590Srgrimes	 */
225144473Sharti	buf = Buf_Init(16);
2261590Srgrimes
227144473Sharti	while ((strchr(" \t)&|", *cp) == NULL) && (*cp != '\0')) {
228144473Sharti		if (*cp == '$') {
229144473Sharti			/*
230144473Sharti			 * Parse the variable spec and install it as part of
231144473Sharti			 * the argument if it's valid. We tell Var_Parse to
232144473Sharti			 * complain on an undefined variable, so we don't do
233144473Sharti			 * it too. Nor do we return an error, though perhaps
234144473Sharti			 * we should...
235144473Sharti			 */
236144473Sharti			char	*cp2;
237144473Sharti			size_t	len = 0;
238144473Sharti			Boolean	doFree;
2391590Srgrimes
240144473Sharti			cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree);
2418874Srgrimes
242144473Sharti			Buf_Append(buf, cp2);
243144473Sharti			if (doFree) {
244144473Sharti				free(cp2);
245144473Sharti			}
246144473Sharti			cp += len;
247144473Sharti		} else {
248144473Sharti			Buf_AddByte(buf, (Byte)*cp);
249144473Sharti			cp++;
250144473Sharti		}
251144473Sharti	}
2521590Srgrimes
253144473Sharti	Buf_AddByte(buf, (Byte)'\0');
254144473Sharti	*argPtr = (char *)Buf_GetAll(buf, &argLen);
255144473Sharti	Buf_Destroy(buf, FALSE);
2561590Srgrimes
257144473Sharti	while (*cp == ' ' || *cp == '\t') {
258144473Sharti		cp++;
2591590Srgrimes	}
260144473Sharti	if (parens && *cp != ')') {
261144473Sharti		Parse_Error(PARSE_WARNING,
262144473Sharti		    "Missing closing parenthesis for %s()", func);
263144473Sharti		return (0);
264144473Sharti	} else if (parens) {
265144473Sharti		/*
266144473Sharti		 * Advance pointer past close parenthesis.
267144473Sharti		 */
268144473Sharti		cp++;
269144473Sharti	}
2701590Srgrimes
271144473Sharti	*linePtr = cp;
272144473Sharti	return (argLen);
2731590Srgrimes}
274138232Sharti
275144473Sharti/**
276144473Sharti * CondDoDefined
2771590Srgrimes *	Handle the 'defined' function for conditionals.
2781590Srgrimes *
2791590Srgrimes * Results:
2801590Srgrimes *	TRUE if the given variable is defined.
2811590Srgrimes */
2821590Srgrimesstatic Boolean
283138232ShartiCondDoDefined(int argLen, char *arg)
2841590Srgrimes{
285144473Sharti	char	savec = arg[argLen];
286144473Sharti	Boolean	result;
2871590Srgrimes
288144473Sharti	arg[argLen] = '\0';
289146580Sharti	if (Var_Value(arg, VAR_CMD) != NULL) {
290144473Sharti		result = TRUE;
291144473Sharti	} else {
292144473Sharti		result = FALSE;
293144473Sharti	}
294144473Sharti	arg[argLen] = savec;
295144473Sharti	return (result);
2961590Srgrimes}
297138232Sharti
298144473Sharti/**
299144473Sharti * CondDoMake
3001590Srgrimes *	Handle the 'make' function for conditionals.
3011590Srgrimes *
3021590Srgrimes * Results:
3031590Srgrimes *	TRUE if the given target is being made.
3041590Srgrimes */
3051590Srgrimesstatic Boolean
306138232ShartiCondDoMake(int argLen, char *arg)
3071590Srgrimes{
308144473Sharti	char	savec = arg[argLen];
309144473Sharti	Boolean	result;
310144473Sharti	const LstNode *ln;
3111590Srgrimes
312144473Sharti	arg[argLen] = '\0';
313144473Sharti	result = FALSE;
314144473Sharti	LST_FOREACH(ln, &create) {
315144473Sharti		if (Str_Match(Lst_Datum(ln), arg)) {
316144473Sharti			result = TRUE;
317144473Sharti			break;
318144473Sharti		}
319143810Sharti	}
320144473Sharti	arg[argLen] = savec;
321144473Sharti	return (result);
3221590Srgrimes}
323138232Sharti
324144473Sharti/**
325144473Sharti * CondDoExists
3261590Srgrimes *	See if the given file exists.
3271590Srgrimes *
3281590Srgrimes * Results:
3291590Srgrimes *	TRUE if the file exists and FALSE if it does not.
3301590Srgrimes */
3311590Srgrimesstatic Boolean
332138232ShartiCondDoExists(int argLen, char *arg)
3331590Srgrimes{
334144473Sharti	char	savec = arg[argLen];
335144473Sharti	Boolean	result;
336144473Sharti	char	*path;
3371590Srgrimes
338144473Sharti	arg[argLen] = '\0';
339144473Sharti	path = Path_FindFile(arg, &dirSearchPath);
340144473Sharti	if (path != NULL) {
341144473Sharti		result = TRUE;
342144473Sharti		free(path);
343144473Sharti	} else {
344144473Sharti		result = FALSE;
345144473Sharti	}
346144473Sharti	arg[argLen] = savec;
347144473Sharti	return (result);
3481590Srgrimes}
349138232Sharti
350144473Sharti/**
351144473Sharti * CondDoTarget
3521590Srgrimes *	See if the given node exists and is an actual target.
3531590Srgrimes *
3541590Srgrimes * Results:
3551590Srgrimes *	TRUE if the node exists as a target and FALSE if it does not.
3561590Srgrimes */
3571590Srgrimesstatic Boolean
358138232ShartiCondDoTarget(int argLen, char *arg)
3591590Srgrimes{
360144473Sharti	char	savec = arg[argLen];
361144473Sharti	Boolean	result;
362144473Sharti	GNode	*gn;
3631590Srgrimes
364144473Sharti	arg[argLen] = '\0';
365144473Sharti	gn = Targ_FindNode(arg, TARG_NOCREATE);
366144473Sharti	if ((gn != NULL) && !OP_NOP(gn->type)) {
367144473Sharti		result = TRUE;
368144473Sharti	} else {
369144473Sharti		result = FALSE;
370144473Sharti	}
371144473Sharti	arg[argLen] = savec;
372144473Sharti	return (result);
3731590Srgrimes}
3741590Srgrimes
375144473Sharti/**
376144473Sharti * CondCvtArg
3771590Srgrimes *	Convert the given number into a double. If the number begins
3781590Srgrimes *	with 0x, it is interpreted as a hexadecimal integer
3791590Srgrimes *	and converted to a double from there. All other strings just have
3801590Srgrimes *	strtod called on them.
3811590Srgrimes *
3821590Srgrimes * Results:
3831590Srgrimes *	Sets 'value' to double value of string.
38447494Shoek *	Returns address of the first character after the last valid
38547494Shoek *	character of the converted number.
3861590Srgrimes *
3871590Srgrimes * Side Effects:
3881590Srgrimes *	Can change 'value' even if string is not a valid number.
3891590Srgrimes */
39047494Shoekstatic char *
391104696SjmallettCondCvtArg(char *str, double *value)
3921590Srgrimes{
3931590Srgrimes
394144473Sharti	if ((*str == '0') && (str[1] == 'x')) {
395144473Sharti		long i;
396144473Sharti
397144473Sharti		for (str += 2, i = 0; ; str++) {
398144473Sharti			int x;
399144473Sharti
400144473Sharti			if (isdigit((unsigned char)*str))
401144473Sharti				x  = *str - '0';
402144473Sharti			else if (isxdigit((unsigned char)*str))
403144473Sharti				x = 10 + *str -
404144473Sharti				    isupper((unsigned char)*str) ? 'A' : 'a';
405144473Sharti			else {
406144473Sharti				*value = (double)i;
407144473Sharti				return (str);
408144473Sharti			}
409144473Sharti			i = (i << 4) + x;
410144473Sharti		}
411144473Sharti
412144473Sharti	} else {
413144473Sharti		char *eptr;
414144473Sharti
415144473Sharti		*value = strtod(str, &eptr);
416144473Sharti		return (eptr);
4171590Srgrimes	}
4181590Srgrimes}
419138232Sharti
420144473Sharti/**
421144473Sharti * CondToken
4221590Srgrimes *	Return the next token from the input.
4231590Srgrimes *
4241590Srgrimes * Results:
4251590Srgrimes *	A Token for the next lexical token in the stream.
4261590Srgrimes *
4271590Srgrimes * Side Effects:
4281590Srgrimes *	condPushback will be set back to None if it is used.
4291590Srgrimes */
4301590Srgrimesstatic Token
431104696SjmallettCondToken(Boolean doEval)
4321590Srgrimes{
433144473Sharti	Token	t;
4341590Srgrimes
435146152Sharti	if (condPushBack != None) {
436146152Sharti		t = condPushBack;
437146152Sharti		condPushBack = None;
438146152Sharti		return (t);
439146152Sharti	}
440146152Sharti
441146152Sharti	while (*condExpr == ' ' || *condExpr == '\t') {
442146152Sharti		condExpr++;
443146152Sharti	}
444146152Sharti	switch (*condExpr) {
445146152Sharti	  case '(':
446146152Sharti		t = LParen;
447146152Sharti		condExpr++;
448146152Sharti		break;
449146152Sharti	  case ')':
450146152Sharti		t = RParen;
451146152Sharti		condExpr++;
452146152Sharti		break;
453146152Sharti	  case '|':
454146152Sharti		if (condExpr[1] == '|') {
455144473Sharti			condExpr++;
4561590Srgrimes		}
457146152Sharti		condExpr++;
458146152Sharti		t = Or;
459146152Sharti		break;
460146152Sharti	  case '&':
461146152Sharti		if (condExpr[1] == '&') {
462144473Sharti			condExpr++;
463146152Sharti		}
464146152Sharti		condExpr++;
465146152Sharti		t = And;
466146152Sharti		break;
467146152Sharti	  case '!':
468146152Sharti		t = Not;
469146152Sharti		condExpr++;
470146152Sharti		break;
471146152Sharti	  case '\n':
472146152Sharti	  case '\0':
473146152Sharti		t = EndOfFile;
474146152Sharti		break;
475146152Sharti	  case '$': {
476146154Sharti		char		*lhs;
477146154Sharti		const char	*op;
478146154Sharti		char		*rhs;
479146154Sharti		char		zero[] = "0";
480146154Sharti		size_t		varSpecLen = 0;
481146154Sharti		Boolean		doFree;
4821590Srgrimes
483146152Sharti		/*
484146152Sharti		 * Parse the variable spec and skip over it, saving its
485146152Sharti		 * value in lhs.
486146152Sharti		 */
487146152Sharti		t = Err;
488146152Sharti		lhs = Var_Parse(condExpr, VAR_CMD, doEval,
489146152Sharti		    &varSpecLen, &doFree);
490146152Sharti		if (lhs == var_Error) {
491144473Sharti			/*
492146152Sharti			 * Even if !doEval, we still report syntax
493146152Sharti			 * errors, which is what getting var_Error
494146152Sharti			 * back with !doEval means.
495144473Sharti			 */
496146152Sharti			return (Err);
497146152Sharti		}
498146152Sharti		condExpr += varSpecLen;
4991590Srgrimes
500146152Sharti		if (!isspace((unsigned char)*condExpr) &&
501146152Sharti		    strchr("!=><", *condExpr) == NULL) {
502146152Sharti			Buffer *buf;
5031590Srgrimes
504146152Sharti			buf = Buf_Init(0);
5051590Srgrimes
506146152Sharti			Buf_Append(buf, lhs);
5071590Srgrimes
508146152Sharti			if (doFree)
509146152Sharti				free(lhs);
5101590Srgrimes
511146152Sharti			for (;*condExpr &&
512146152Sharti			    !isspace((unsigned char)*condExpr);
513146152Sharti			    condExpr++)
514146152Sharti				Buf_AddByte(buf, (Byte)*condExpr);
5151590Srgrimes
516146152Sharti			Buf_AddByte(buf, (Byte)'\0');
517146152Sharti			lhs = (char *)Buf_GetAll(buf, &varSpecLen);
518146152Sharti			Buf_Destroy(buf, FALSE);
5191590Srgrimes
520146152Sharti			doFree = TRUE;
521146152Sharti		}
522146152Sharti
523146152Sharti		/*
524146152Sharti		 * Skip whitespace to get to the operator
525146152Sharti		 */
526146152Sharti		while (isspace((unsigned char)*condExpr))
527146152Sharti			condExpr++;
528146152Sharti
529146152Sharti		/*
530146152Sharti		 * Make sure the operator is a valid one. If it isn't a
531146152Sharti		 * known relational operator, pretend we got a
532146152Sharti		 * != 0 comparison.
533146152Sharti		 */
534146152Sharti		op = condExpr;
535146152Sharti		switch (*condExpr) {
536146152Sharti		  case '!':
537146152Sharti		  case '=':
538146152Sharti		  case '<':
539146152Sharti		  case '>':
540146152Sharti			if (condExpr[1] == '=') {
541146152Sharti				condExpr += 2;
542146152Sharti			} else {
543146152Sharti				condExpr += 1;
544144473Sharti			}
545146153Sharti			while (isspace((unsigned char)*condExpr)) {
546146153Sharti				condExpr++;
547146153Sharti			}
548146153Sharti			if (*condExpr == '\0') {
549146153Sharti				Parse_Error(PARSE_WARNING,
550146153Sharti				    "Missing right-hand-side of operator");
551146153Sharti				goto error;
552146153Sharti			}
553146153Sharti			rhs = condExpr;
554146152Sharti			break;
555146153Sharti
556146152Sharti		  default:
557146152Sharti			op = "!=";
558146154Sharti			rhs = zero;
559146153Sharti			break;
560146152Sharti		}
561146152Sharti		if (*rhs == '"') {
562144473Sharti			/*
563146152Sharti			 * Doing a string comparison. Only allow == and
564146152Sharti			 * != for * operators.
565144473Sharti			 */
566146152Sharti			char	*string;
567146152Sharti			char	*cp, *cp2;
568146152Sharti			int	qt;
569146152Sharti			Buffer	*buf;
5701590Srgrimes
571146152Sharti  do_string_compare:
572146152Sharti			if (((*op != '!') && (*op != '=')) ||
573146152Sharti			    (op[1] != '=')) {
574144473Sharti				Parse_Error(PARSE_WARNING,
575146152Sharti				    "String comparison operator should "
576146152Sharti				    "be either == or !=");
577144473Sharti				goto error;
578144473Sharti			}
5791590Srgrimes
580146152Sharti			buf = Buf_Init(0);
581146152Sharti			qt = *rhs == '"' ? 1 : 0;
5821590Srgrimes
583146152Sharti			for (cp = &rhs[qt];
584146152Sharti			    ((qt && (*cp != '"')) ||
585146152Sharti			    (!qt && strchr(" \t)", *cp) == NULL)) &&
586146152Sharti			    (*cp != '\0'); cp++) {
587146152Sharti				if ((*cp == '\\') && (cp[1] != '\0')) {
588146152Sharti					/*
589146152Sharti					 * Backslash escapes things --
590146152Sharti					 * skip over next character,							 * if it exists.
591146152Sharti					 */
592146152Sharti					cp++;
593146152Sharti					Buf_AddByte(buf, (Byte)*cp);
5941590Srgrimes
595146152Sharti				} else if (*cp == '$') {
596146152Sharti					size_t	len = 0;
597146152Sharti					Boolean	freeIt;
5988874Srgrimes
599146152Sharti					cp2 = Var_Parse(cp, VAR_CMD,
600146152Sharti					    doEval, &len, &freeIt);
601146152Sharti					if (cp2 != var_Error) {
602146152Sharti						Buf_Append(buf, cp2);
603146152Sharti						if (freeIt) {
604146152Sharti							free(cp2);
605144473Sharti						}
606146152Sharti						cp += len - 1;
607144473Sharti					} else {
608146152Sharti						Buf_AddByte(buf,
609146152Sharti						    (Byte)*cp);
610144473Sharti					}
611146152Sharti				} else {
612146152Sharti					Buf_AddByte(buf, (Byte)*cp);
6131590Srgrimes				}
614146152Sharti			}
615144473Sharti
616146152Sharti			string = Buf_Peel(buf);
617144473Sharti
618146152Sharti			DEBUGF(COND, ("lhs = \"%s\", rhs = \"%s\", "
619146152Sharti			    "op = %.2s\n", lhs, string, op));
620146152Sharti			/*
621146152Sharti			 * Null-terminate rhs and perform the
622146152Sharti			 * comparison. t is set to the result.
623146152Sharti			 */
624146152Sharti			if (*op == '=') {
625146152Sharti				t = strcmp(lhs, string) ? False : True;
6261590Srgrimes			} else {
627146152Sharti				t = strcmp(lhs, string) ? True : False;
628146152Sharti			}
629146152Sharti			free(string);
630146152Sharti			if (rhs == condExpr) {
631146625Sharti				if (*cp == '\0' || (!qt && *cp == ')'))
632146152Sharti					condExpr = cp;
633146152Sharti				else
634146152Sharti					condExpr = cp + 1;
635146152Sharti			}
636146152Sharti		} else {
637146152Sharti			/*
638146152Sharti			 * rhs is either a float or an integer.
639146152Sharti			 * Convert both the lhs and the rhs to a
640146152Sharti			 * double and compare the two.
641146152Sharti			 */
642146152Sharti			double	left, right;
643146152Sharti			char	*string;
6441590Srgrimes
645146152Sharti			if (*CondCvtArg(lhs, &left) != '\0')
646146152Sharti				goto do_string_compare;
647146152Sharti			if (*rhs == '$') {
648146152Sharti				size_t	len = 0;
649146152Sharti				Boolean	freeIt;
6501590Srgrimes
651146152Sharti				string = Var_Parse(rhs, VAR_CMD, doEval,
652146152Sharti				    &len, &freeIt);
653146152Sharti				if (string == var_Error) {
654146152Sharti					right = 0.0;
655146152Sharti				} else {
656146152Sharti					if (*CondCvtArg(string,
657146152Sharti					    &right) != '\0') {
658144473Sharti						if (freeIt)
659144473Sharti							free(string);
660144473Sharti						goto do_string_compare;
661144473Sharti					}
662146152Sharti					if (freeIt)
663146152Sharti						free(string);
664146152Sharti					if (rhs == condExpr)
665146152Sharti						condExpr += len;
666144473Sharti				}
667146152Sharti			} else {
668146152Sharti				char *c = CondCvtArg(rhs, &right);
6698874Srgrimes
670146152Sharti				if (c == rhs)
671146152Sharti					goto do_string_compare;
672146152Sharti				if (rhs == condExpr) {
673146152Sharti					/*
674146152Sharti					 * Skip over the right-hand side
675146152Sharti					 */
676146152Sharti					condExpr = c;
677144473Sharti				}
6781590Srgrimes			}
679146152Sharti
680146152Sharti			DEBUGF(COND, ("left = %f, right = %f, "
681146152Sharti			    "op = %.2s\n", left, right, op));
682146152Sharti			switch (op[0]) {
683146152Sharti			  case '!':
684146152Sharti				if (op[1] != '=') {
685146152Sharti					Parse_Error(PARSE_WARNING,
686146152Sharti					    "Unknown operator");
687146152Sharti					goto error;
688146152Sharti				}
689146152Sharti				t = (left != right ? True : False);
690146152Sharti				break;
691146152Sharti			  case '=':
692146152Sharti				if (op[1] != '=') {
693146152Sharti					Parse_Error(PARSE_WARNING,
694146152Sharti					    "Unknown operator");
695146152Sharti					goto error;
696146152Sharti				}
697146152Sharti				t = (left == right ? True : False);
698146152Sharti				break;
699146152Sharti			  case '<':
700146152Sharti				if (op[1] == '=') {
701146152Sharti					t = (left <= right?True:False);
702146152Sharti				} else {
703146152Sharti					t = (left < right?True:False);
704146152Sharti				}
705146152Sharti				break;
706146152Sharti			case '>':
707146152Sharti				if (op[1] == '=') {
708146152Sharti					t = (left >= right?True:False);
709146152Sharti				} else {
710146152Sharti					t = (left > right?True:False);
711146152Sharti				}
712146152Sharti				break;
713146152Sharti			default:
714146152Sharti				break;
7151590Srgrimes			}
716146152Sharti		}
717146152Sharti  error:
718146152Sharti		if (doFree)
719146152Sharti			free(lhs);
720146152Sharti		break;
721146152Sharti		}
7228874Srgrimes
723146152Sharti	  default: {
724146152Sharti		CondProc	*evalProc;
725146152Sharti		Boolean		invert = FALSE;
726146152Sharti		char		*arg;
727146152Sharti		int		arglen;
7281590Srgrimes
729146152Sharti		if (strncmp(condExpr, "defined", 7) == 0) {
730146152Sharti			/*
731146152Sharti			 * Use CondDoDefined to evaluate the argument
732146152Sharti			 * and CondGetArg to extract the argument from
733146152Sharti			 * the 'function call'.
734146152Sharti			 */
735146152Sharti			evalProc = CondDoDefined;
736146152Sharti			condExpr += 7;
737146152Sharti			arglen = CondGetArg(&condExpr, &arg,
738146152Sharti			    "defined", TRUE);
739146152Sharti			if (arglen == 0) {
740146152Sharti				condExpr -= 7;
741146152Sharti				goto use_default;
742146152Sharti			}
7431590Srgrimes
744146152Sharti		} else if (strncmp(condExpr, "make", 4) == 0) {
745146152Sharti			/*
746146152Sharti			 * Use CondDoMake to evaluate the argument and
747146152Sharti			 * CondGetArg to extract the argument from the
748146152Sharti			 * 'function call'.
749146152Sharti			 */
750146152Sharti			evalProc = CondDoMake;
751146152Sharti			condExpr += 4;
752146152Sharti			arglen = CondGetArg(&condExpr, &arg,
753146152Sharti			    "make", TRUE);
754146152Sharti			if (arglen == 0) {
755146152Sharti				condExpr -= 4;
756146152Sharti				goto use_default;
757146152Sharti			}
7585814Sjkh
759146152Sharti		} else if (strncmp(condExpr, "exists", 6) == 0) {
760146152Sharti			/*
761146152Sharti			 * Use CondDoExists to evaluate the argument and
762146152Sharti			 * CondGetArg to extract the argument from the
763146152Sharti			 * 'function call'.
764146152Sharti			 */
765146152Sharti			evalProc = CondDoExists;
766146152Sharti			condExpr += 6;
767146152Sharti			arglen = CondGetArg(&condExpr, &arg,
768146152Sharti			    "exists", TRUE);
769146152Sharti			if (arglen == 0) {
770146152Sharti				condExpr -= 6;
771146152Sharti				goto use_default;
772146152Sharti			}
773144473Sharti
774146152Sharti		} else if (strncmp(condExpr, "empty", 5) == 0) {
775146152Sharti			/*
776146152Sharti			 * Use Var_Parse to parse the spec in parens and
777146152Sharti			 * return True if the resulting string is empty.
778146152Sharti			 */
779146152Sharti			size_t	length;
780146152Sharti			Boolean	doFree;
781146152Sharti			char	*val;
782144473Sharti
783146152Sharti			condExpr += 5;
784144473Sharti
785146152Sharti			for (arglen = 0;
786146152Sharti			    condExpr[arglen] != '(' &&
787146152Sharti			    condExpr[arglen] != '\0'; arglen += 1)
788146152Sharti				continue;
789144473Sharti
790146152Sharti			if (condExpr[arglen] != '\0') {
791146152Sharti				length = 0;
792146152Sharti				val = Var_Parse(&condExpr[arglen - 1],
793146152Sharti				    VAR_CMD, FALSE, &length, &doFree);
794146152Sharti				if (val == var_Error) {
795146152Sharti					t = Err;
796146152Sharti				} else {
797144473Sharti					/*
798146152Sharti					 * A variable is empty when it
799146152Sharti					 * just contains spaces...
800146152Sharti					 * 4/15/92, christos
801144473Sharti					 */
802146152Sharti					char *p;
803146152Sharti
804146152Sharti					for (p = val;
805146152Sharti					    *p &&
806146152Sharti					    isspace((unsigned char)*p);
807146152Sharti					    p++)
808146152Sharti						continue;
809146152Sharti					t = (*p == '\0') ? True : False;
810144473Sharti				}
811146152Sharti				if (doFree) {
812146152Sharti					free(val);
813146152Sharti				}
814144473Sharti				/*
815146152Sharti				 * Advance condExpr to beyond the
816146152Sharti				 * closing ). Note that we subtract
817146152Sharti				 * one from arglen + length b/c length
818146152Sharti				 * is calculated from
819146152Sharti				 * condExpr[arglen - 1].
820144473Sharti				 */
821146152Sharti				condExpr += arglen + length - 1;
8221590Srgrimes			} else {
823146152Sharti				condExpr -= 5;
824146152Sharti				goto use_default;
8251590Srgrimes			}
826146152Sharti			break;
827144473Sharti
828146152Sharti		} else if (strncmp(condExpr, "target", 6) == 0) {
8291590Srgrimes			/*
830146152Sharti			 * Use CondDoTarget to evaluate the argument and
831146152Sharti			 * CondGetArg to extract the argument from the
832146152Sharti			 * 'function call'.
8331590Srgrimes			 */
834146152Sharti			evalProc = CondDoTarget;
835146152Sharti			condExpr += 6;
836146152Sharti			arglen = CondGetArg(&condExpr, &arg,
837146152Sharti			    "target", TRUE);
838146152Sharti			if (arglen == 0) {
839146152Sharti				condExpr -= 6;
840146152Sharti				goto use_default;
841144473Sharti			}
842146152Sharti
843146152Sharti		} else {
844146152Sharti			/*
845146152Sharti			 * The symbol is itself the argument to the
846146152Sharti			 * default function. We advance condExpr to
847146152Sharti			 * the end of the symbol by hand (the next
848146152Sharti			 * whitespace, closing paren or binary operator)
849146152Sharti			 * and set to invert the evaluation
850146152Sharti			 * function if condInvert is TRUE.
851146152Sharti			 */
852146152Sharti  use_default:
853146152Sharti			invert = condInvert;
854146152Sharti			evalProc = condDefProc;
855146152Sharti			arglen = CondGetArg(&condExpr, &arg, "", FALSE);
8561590Srgrimes		}
857146152Sharti
858146152Sharti		/*
859146152Sharti		 * Evaluate the argument using the set function. If
860146152Sharti		 * invert is TRUE, we invert the sense of the function.
861146152Sharti		 */
862146152Sharti		t = (!doEval || (* evalProc) (arglen, arg) ?
863146152Sharti		    (invert ? False : True) :
864146152Sharti		    (invert ? True : False));
865146152Sharti		free(arg);
866146152Sharti		break;
867146152Sharti		}
8681590Srgrimes	}
869144473Sharti	return (t);
8701590Srgrimes}
871138232Sharti
872144473Sharti/**
873144473Sharti * CondT
8741590Srgrimes *	Parse a single term in the expression. This consists of a terminal
8751590Srgrimes *	symbol or Not and a terminal symbol (not including the binary
8761590Srgrimes *	operators):
8771590Srgrimes *	    T -> defined(variable) | make(target) | exists(file) | symbol
8781590Srgrimes *	    T -> ! T | ( E )
8791590Srgrimes *
8801590Srgrimes * Results:
8811590Srgrimes *	True, False or Err.
8821590Srgrimes *
8831590Srgrimes * Side Effects:
8841590Srgrimes *	Tokens are consumed.
8851590Srgrimes */
8861590Srgrimesstatic Token
887104696SjmallettCondT(Boolean doEval)
8881590Srgrimes{
889144473Sharti	Token	t;
8901590Srgrimes
891144473Sharti	t = CondToken(doEval);
892144473Sharti	if (t == EndOfFile) {
893144473Sharti		/*
894144473Sharti		 * If we reached the end of the expression, the expression
895144473Sharti		 * is malformed...
896144473Sharti		 */
8971590Srgrimes		t = Err;
898144473Sharti	} else if (t == LParen) {
899144473Sharti		/*
900144473Sharti		 * T -> ( E )
901144473Sharti		 */
902144473Sharti		t = CondE(doEval);
903144473Sharti		if (t != Err) {
904144473Sharti			if (CondToken(doEval) != RParen) {
905144473Sharti				t = Err;
906144473Sharti			}
907144473Sharti		}
908144473Sharti	} else if (t == Not) {
909144473Sharti		t = CondT(doEval);
910144473Sharti		if (t == True) {
911144473Sharti			t = False;
912144473Sharti		} else if (t == False) {
913144473Sharti			t = True;
914144473Sharti		}
9151590Srgrimes	}
916144473Sharti	return (t);
9171590Srgrimes}
918138232Sharti
919144473Sharti/**
9201590Srgrimes * CondF --
9211590Srgrimes *	Parse a conjunctive factor (nice name, wot?)
9221590Srgrimes *	    F -> T && F | T
9231590Srgrimes *
9241590Srgrimes * Results:
9251590Srgrimes *	True, False or Err
9261590Srgrimes *
9271590Srgrimes * Side Effects:
9281590Srgrimes *	Tokens are consumed.
9291590Srgrimes */
9301590Srgrimesstatic Token
931104696SjmallettCondF(Boolean doEval)
9321590Srgrimes{
933144473Sharti	Token	l, o;
9341590Srgrimes
935144473Sharti	l = CondT(doEval);
936144473Sharti	if (l != Err) {
937144473Sharti		o = CondToken(doEval);
9381590Srgrimes
939144473Sharti		if (o == And) {
940144473Sharti			/*
941144473Sharti			 * F -> T && F
942144473Sharti			 *
943144473Sharti			 * If T is False, the whole thing will be False, but
944144473Sharti			 * we have to parse the r.h.s. anyway (to throw it
945144473Sharti			 * away). If T is True, the result is the r.h.s.,
946144473Sharti			 * be it an Err or no.
947144473Sharti			 */
948144473Sharti			if (l == True) {
949144473Sharti				l = CondF(doEval);
950144473Sharti			} else {
951144473Sharti				CondF(FALSE);
952144473Sharti			}
953144473Sharti		} else {
954144473Sharti			/*
955144473Sharti			 * F -> T
956144473Sharti			 */
957144473Sharti			CondPushBack(o);
958144473Sharti		}
9591590Srgrimes	}
960144473Sharti	return (l);
9611590Srgrimes}
962138232Sharti
963144473Sharti/**
9641590Srgrimes * CondE --
9651590Srgrimes *	Main expression production.
9661590Srgrimes *	    E -> F || E | F
9671590Srgrimes *
9681590Srgrimes * Results:
9691590Srgrimes *	True, False or Err.
9701590Srgrimes *
9711590Srgrimes * Side Effects:
9721590Srgrimes *	Tokens are, of course, consumed.
9731590Srgrimes */
9741590Srgrimesstatic Token
975104696SjmallettCondE(Boolean doEval)
9761590Srgrimes{
977144473Sharti	Token   l, o;
9781590Srgrimes
979144473Sharti	l = CondF(doEval);
980144473Sharti	if (l != Err) {
981144473Sharti		o = CondToken(doEval);
9821590Srgrimes
983144473Sharti		if (o == Or) {
984144473Sharti			/*
985144473Sharti			 * E -> F || E
986144473Sharti			 *
987144473Sharti			 * A similar thing occurs for ||, except that here we
988144473Sharti			 * make sure the l.h.s. is False before we bother to
989144473Sharti			 * evaluate the r.h.s. Once again, if l is False, the
990144473Sharti			 * result is the r.h.s. and once again if l is True,
991144473Sharti			 * we parse the r.h.s. to throw it away.
992144473Sharti			 */
993144473Sharti			if (l == False) {
994144473Sharti				l = CondE(doEval);
995144473Sharti			} else {
996144473Sharti				CondE(FALSE);
997144473Sharti			}
998144473Sharti		} else {
999144473Sharti			/*
1000144473Sharti			 * E -> F
1001144473Sharti			 */
1002144473Sharti			CondPushBack(o);
1003144473Sharti		}
10041590Srgrimes	}
1005144473Sharti	return (l);
10061590Srgrimes}
1007138232Sharti
1008144473Sharti/**
1009144894Sharti * Cond_If
1010144894Sharti *	Handle .if<X> and .elif<X> directives.
1011144894Sharti *	This function is called even when we're skipping.
10121590Srgrimes */
1013144894Shartivoid
1014144894ShartiCond_If(char *line, int code, int lineno)
10151590Srgrimes{
1016144894Sharti	const struct If	*ifp;
1017144894Sharti	Boolean value;
10181590Srgrimes
1019144894Sharti	ifp = &ifs[code];
10201590Srgrimes
1021144894Sharti	if (ifp->isElse) {
1022144894Sharti		if (condTop == MAXIF) {
1023144894Sharti			Parse_Error(PARSE_FATAL, "if-less elif");
1024144894Sharti			return;
1025144894Sharti		}
1026144473Sharti		if (skipIfLevel != 0) {
1027144473Sharti			/*
1028144894Sharti			 * If skipping this conditional, just ignore
1029144894Sharti			 * the whole thing. If we don't, the user
1030144894Sharti			 * might be employing a variable that's
1031144894Sharti			 * undefined, for which there's an enclosing
1032144894Sharti			 * ifdef that we're skipping...
1033144473Sharti			 */
1034144894Sharti			skipIfLineno[skipIfLevel - 1] = lineno;
1035144894Sharti			return;
1036144473Sharti		}
10371590Srgrimes
1038144894Sharti	} else if (skipLine) {
1039144473Sharti		/*
1040144894Sharti		 * Don't even try to evaluate a conditional that's
1041144894Sharti		 * not an else if we're skipping things...
1042144473Sharti		 */
1043144894Sharti		skipIfLineno[skipIfLevel] = lineno;
1044144894Sharti		skipIfLevel += 1;
1045144894Sharti		return;
1046144894Sharti	}
10478874Srgrimes
1048144894Sharti	/*
1049144894Sharti	 * Initialize file-global variables for parsing
1050144894Sharti	 */
1051144894Sharti	condDefProc = ifp->defProc;
1052144894Sharti	condInvert = ifp->doNot;
10538874Srgrimes
1054144894Sharti	while (*line == ' ' || *line == '\t') {
1055144894Sharti		line++;
1056144894Sharti	}
1057144473Sharti
1058144894Sharti	condExpr = line;
1059144894Sharti	condPushBack = None;
1060144473Sharti
1061144894Sharti	switch (CondE(TRUE)) {
1062144894Sharti	  case True:
1063144894Sharti		if (CondToken(TRUE) != EndOfFile)
1064144473Sharti			goto err;
1065144894Sharti		value = TRUE;
1066144894Sharti		break;
1067144473Sharti
1068144894Sharti	  case False:
1069144894Sharti		if (CondToken(TRUE) != EndOfFile)
1070144894Sharti			goto err;
1071144894Sharti		value = FALSE;
1072144894Sharti		break;
1073144473Sharti
1074144894Sharti	  case Err:
1075144894Sharti  err:		Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line);
1076144894Sharti		return;
1077144894Sharti
1078144894Sharti	  default:
1079144894Sharti		abort();
1080144473Sharti	}
1081144894Sharti
1082144894Sharti	if (!ifp->isElse) {
1083144894Sharti		/* push this value */
1084144473Sharti		condTop -= 1;
1085144473Sharti
1086144894Sharti	} else if (skipIfLevel != 0 || condStack[condTop]) {
1087144473Sharti		/*
1088144473Sharti		 * If this is an else-type conditional, it should only take
1089144473Sharti		 * effect if its corresponding if was evaluated and FALSE.
1090144473Sharti		 * If its if was TRUE or skipped, we return COND_SKIP (and
1091144473Sharti		 * start skipping in case we weren't already), leaving the
1092144473Sharti		 * stack unmolested so later elif's don't screw up...
1093144473Sharti		 */
1094144473Sharti		skipLine = TRUE;
1095144894Sharti		return;
1096144473Sharti	}
1097144473Sharti
1098144473Sharti	if (condTop < 0) {
1099144473Sharti		/*
1100144473Sharti		 * This is the one case where we can definitely proclaim a fatal
1101144473Sharti		 * error. If we don't, we're hosed.
1102144473Sharti		 */
1103144894Sharti		Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.",MAXIF);
1104144894Sharti		return;
11051590Srgrimes	}
1106144894Sharti
1107144894Sharti	/* push */
1108144894Sharti	condStack[condTop] = value;
1109144894Sharti	condLineno[condTop] = lineno;
1110144894Sharti	skipLine = !value;
11111590Srgrimes}
1112138232Sharti
1113144473Sharti/**
1114144894Sharti * Cond_Else
1115144894Sharti *	Handle .else statement.
1116144894Sharti */
1117144894Shartivoid
1118144894ShartiCond_Else(char *line __unused, int code __unused, int lineno __unused)
1119144894Sharti{
1120144894Sharti
1121144894Sharti	while (isspace((u_char)*line))
1122144894Sharti		line++;
1123144894Sharti
1124145679Sharti	if (*line != '\0' && (warn_flags & WARN_DIRSYNTAX)) {
1125145627Sharti		Parse_Error(PARSE_WARNING, "junk after .else ignored '%s'",
1126145627Sharti		    line);
1127144894Sharti	}
1128144894Sharti
1129144894Sharti	if (condTop == MAXIF) {
1130144894Sharti		Parse_Error(PARSE_FATAL, "if-less else");
1131144894Sharti		return;
1132144894Sharti	}
1133144894Sharti	if (skipIfLevel != 0)
1134144894Sharti		return;
1135144894Sharti
1136144894Sharti	if (skipIfLevel != 0 || condStack[condTop]) {
1137144894Sharti		/*
1138144894Sharti		 * An else should only take effect if its corresponding if was
1139144894Sharti		 * evaluated and FALSE.
1140144894Sharti		 * If its if was TRUE or skipped, we return COND_SKIP (and
1141144894Sharti		 * start skipping in case we weren't already), leaving the
1142144894Sharti		 * stack unmolested so later elif's don't screw up...
1143144894Sharti		 * XXX How does this work with two .else's?
1144144894Sharti		 */
1145144894Sharti		skipLine = TRUE;
1146144894Sharti		return;
1147144894Sharti	}
1148144894Sharti
1149144894Sharti	/* inverse value */
1150144894Sharti	condStack[condTop] = !condStack[condTop];
1151144894Sharti	skipLine = !condStack[condTop];
1152144894Sharti}
1153144894Sharti
1154144894Sharti/**
1155144894Sharti * Cond_Endif
1156144894Sharti *	Handle .endif statement.
1157144894Sharti */
1158144894Shartivoid
1159144894ShartiCond_Endif(char *line __unused, int code __unused, int lineno __unused)
1160144894Sharti{
1161144894Sharti
1162144894Sharti	while (isspace((u_char)*line))
1163144894Sharti		line++;
1164144894Sharti
1165145679Sharti	if (*line != '\0' && (warn_flags & WARN_DIRSYNTAX)) {
1166145627Sharti		Parse_Error(PARSE_WARNING, "junk after .endif ignored '%s'",
1167145627Sharti		    line);
1168144894Sharti	}
1169145627Sharti
1170144894Sharti	/*
1171144894Sharti	 * End of a conditional section. If skipIfLevel is non-zero,
1172144894Sharti	 * that conditional was skipped, so lines following it should
1173144894Sharti	 * also be skipped. Hence, we return COND_SKIP. Otherwise,
1174144894Sharti	 * the conditional was read so succeeding lines should be
1175144894Sharti	 * parsed (think about it...) so we return COND_PARSE, unless
1176144894Sharti	 * this endif isn't paired with a decent if.
1177144894Sharti	 */
1178144894Sharti	if (skipIfLevel != 0) {
1179144894Sharti		skipIfLevel -= 1;
1180144894Sharti		return;
1181144894Sharti	}
1182144894Sharti
1183144894Sharti	if (condTop == MAXIF) {
1184144894Sharti		Parse_Error(PARSE_FATAL, "if-less endif");
1185144894Sharti		return;
1186144894Sharti	}
1187144894Sharti
1188144894Sharti	/* pop */
1189144894Sharti	skipLine = FALSE;
1190144894Sharti	condTop += 1;
1191144894Sharti}
1192144894Sharti
1193144894Sharti/**
1194144473Sharti * Cond_End
11951590Srgrimes *	Make sure everything's clean at the end of a makefile.
11961590Srgrimes *
11971590Srgrimes * Side Effects:
11981590Srgrimes *	Parse_Error will be called if open conditionals are around.
11991590Srgrimes */
12001590Srgrimesvoid
1201104696SjmallettCond_End(void)
12021590Srgrimes{
1203144473Sharti	int level;
1204132439Sharti
1205144473Sharti	if (condTop != MAXIF) {
1206144473Sharti		Parse_Error(PARSE_FATAL, "%d open conditional%s:",
1207144473Sharti		    MAXIF - condTop + skipIfLevel,
1208144473Sharti		    MAXIF - condTop + skipIfLevel== 1 ? "" : "s");
1209132439Sharti
1210144473Sharti		for (level = skipIfLevel; level > 0; level--)
1211144473Sharti			Parse_Error(PARSE_FATAL, "\t%*sat line %d (skipped)",
1212144473Sharti			    MAXIF - condTop + level + 1, "",
1213144473Sharti			    skipIfLineno[level - 1]);
1214144473Sharti		for (level = condTop; level < MAXIF; level++)
1215144473Sharti			Parse_Error(PARSE_FATAL, "\t%*sat line %d "
1216144473Sharti			    "(evaluated to %s)", MAXIF - level + skipIfLevel,
1217144473Sharti			    "", condLineno[level],
1218144473Sharti			    condStack[level] ? "true" : "false");
1219144473Sharti	}
1220144473Sharti	condTop = MAXIF;
12211590Srgrimes}
1222