1207536Smav/*-
2207536Smav * Copyright (c) 1988, 1989, 1990, 1993
3207536Smav *	The Regents of the University of California.  All rights reserved.
4207536Smav * Copyright (c) 1988, 1989 by Adam de Boor
5207536Smav * Copyright (c) 1989 by Berkeley Softworks
6207536Smav * All rights reserved.
7207536Smav *
8207536Smav * This code is derived from software contributed to Berkeley by
9207536Smav * Adam de Boor.
10207536Smav *
11207536Smav * Redistribution and use in source and binary forms, with or without
12207536Smav * modification, are permitted provided that the following conditions
13207536Smav * are met:
14207536Smav * 1. Redistributions of source code must retain the above copyright
15207536Smav *    notice, this list of conditions and the following disclaimer.
16207536Smav * 2. Redistributions in binary form must reproduce the above copyright
17207536Smav *    notice, this list of conditions and the following disclaimer in the
18207536Smav *    documentation and/or other materials provided with the distribution.
19207536Smav * 3. All advertising materials mentioning features or use of this software
20207536Smav *    must display the following acknowledgement:
21207536Smav *	This product includes software developed by the University of
22207536Smav *	California, Berkeley and its contributors.
23207536Smav * 4. Neither the name of the University nor the names of its contributors
24207536Smav *    may be used to endorse or promote products derived from this software
25207536Smav *    without specific prior written permission.
26207536Smav *
27207536Smav * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28207536Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29207536Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30207536Smav * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31207536Smav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32207536Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33207536Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34207536Smav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35207536Smav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36207536Smav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37207536Smav * SUCH DAMAGE.
38207536Smav *
39207536Smav * @(#)cond.c	8.2 (Berkeley) 1/2/94
40207536Smav */
41207536Smav
42207536Smav#include <sys/cdefs.h>
43207536Smav__FBSDID("$FreeBSD$");
44207536Smav
45210471Smav/*
46207536Smav * Functions to handle conditionals in a makefile.
47207536Smav *
48207536Smav * Interface:
49207536Smav *	Cond_Eval	Evaluate the conditional in the passed line.
50207536Smav */
51207536Smav
52207536Smav#include <ctype.h>
53207536Smav#include <string.h>
54207536Smav#include <stdlib.h>
55208393Smav
56208393Smav#include "buf.h"
57207536Smav#include "cond.h"
58207536Smav#include "dir.h"
59207536Smav#include "globals.h"
60214099Smav#include "GNode.h"
61214099Smav#include "make.h"
62207536Smav#include "parse.h"
63207536Smav#include "str.h"
64207536Smav#include "targ.h"
65207536Smav#include "util.h"
66207536Smav#include "var.h"
67207536Smav
68207536Smav/*
69207536Smav * The parsing of conditional expressions is based on this grammar:
70207536Smav *	E -> F || E
71207536Smav *	E -> F
72207536Smav *	F -> T && F
73207536Smav *	F -> T
74207536Smav *	T -> defined(variable)
75207536Smav *	T -> make(target)
76207536Smav *	T -> exists(file)
77207536Smav *	T -> empty(varspec)
78207536Smav *	T -> target(name)
79207536Smav *	T -> symbol
80207536Smav *	T -> $(varspec) op value
81207536Smav *	T -> $(varspec) == "string"
82207536Smav *	T -> $(varspec) != "string"
83214099Smav *	T -> ( E )
84214099Smav *	T -> ! T
85207536Smav *	op -> == | != | > | < | >= | <=
86207536Smav *
87207536Smav * 'symbol' is some other symbol to which the default function (condDefProc)
88207536Smav * is applied.
89220569Smav *
90207536Smav * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
91220569Smav * will return And for '&' and '&&', Or for '|' and '||', Not for '!',
92207536Smav * LParen for '(', RParen for ')' and will evaluate the other terminal
93207536Smav * symbols, using either the default function or the function given in the
94207536Smav * terminal, and return the result as either True or False.
95207536Smav *
96207536Smav * All Non-Terminal functions (CondE, CondF and CondT) return Err on error.
97207536Smav */
98220569Smavtypedef enum {
99220569Smav	And,
100220569Smav	Or,
101220569Smav	Not,
102220569Smav	True,
103220569Smav	False,
104207536Smav	LParen,
105207536Smav	RParen,
106207536Smav	EndOfFile,
107207536Smav	None,
108207536Smav	Err
109207536Smav} Token;
110207536Smav
111207536Smavtypedef Boolean CondProc(int, char *);
112207536Smav
113207536Smav/*-
114207536Smav * Structures to handle elegantly the different forms of #if's. The
115207536Smav * last two fields are stored in condInvert and condDefProc, respectively.
116207536Smav */
117207536Smavstatic void CondPushBack(Token);
118207536Smavstatic int CondGetArg(char **, char **, const char *, Boolean);
119207536Smavstatic CondProc	CondDoDefined;
120207536Smavstatic CondProc	CondDoMake;
121207536Smavstatic CondProc	CondDoExists;
122207536Smavstatic CondProc	CondDoTarget;
123207536Smavstatic char *CondCvtArg(char *, double *);
124207536Smavstatic Token CondToken(Boolean);
125207536Smavstatic Token CondT(Boolean);
126207536Smavstatic Token CondF(Boolean);
127207536Smavstatic Token CondE(Boolean);
128207536Smav
129207536Smavstatic const struct If {
130207536Smav	Boolean	doNot;		/* TRUE if default function should be negated */
131207536Smav	CondProc *defProc;	/* Default function to apply */
132207536Smav	Boolean	isElse;		/* actually el<XXX> */
133207536Smav} ifs[] = {
134207536Smav	[COND_IF] =		{ FALSE,	CondDoDefined,	FALSE },
135207536Smav	[COND_IFDEF] =		{ FALSE,	CondDoDefined,	FALSE },
136207536Smav	[COND_IFNDEF] =		{ TRUE,		CondDoDefined,	FALSE },
137207536Smav	[COND_IFMAKE] =		{ FALSE,	CondDoMake,	FALSE },
138207536Smav	[COND_IFNMAKE] =	{ TRUE,		CondDoMake,	FALSE },
139207536Smav	[COND_ELIF] =		{ FALSE,	CondDoDefined,	TRUE },
140207536Smav	[COND_ELIFDEF] =	{ FALSE,	CondDoDefined,	TRUE },
141207536Smav	[COND_ELIFNDEF] =	{ TRUE,		CondDoDefined,	TRUE },
142207536Smav	[COND_ELIFMAKE] =	{ FALSE,	CondDoMake,	TRUE },
143207536Smav	[COND_ELIFNMAKE] =	{ TRUE,		CondDoMake,	TRUE },
144207536Smav};
145207536Smav
146207536Smavstatic Boolean	condInvert;	/* Invert the default function */
147207536Smavstatic CondProc	*condDefProc;	/* default function to apply */
148208393Smavstatic char	*condExpr;	/* The expression to parse */
149207536Smavstatic Token	condPushBack = None; /* Single push-back token in parsing */
150207536Smav
151207536Smav#define MAXIF	30	/* greatest depth of #if'ing */
152207536Smav
153207536Smavstatic Boolean	condStack[MAXIF];	/* Stack of conditionals's values */
154207536Smavstatic int	condLineno[MAXIF];	/* Line numbers of the opening .if */
155207536Smavstatic int	condTop = MAXIF;	/* Top-most conditional */
156207536Smavstatic int	skipIfLevel = 0;	/* Depth of skipped conditionals */
157207536Smavstatic int	skipIfLineno[MAXIF];	/* Line numbers of skipped .ifs */
158207536SmavBoolean		skipLine = FALSE;	/* Whether the parse module is skipping
159207536Smav					 * lines */
160207536Smav
161207536Smav/**
162207536Smav * CondPushBack
163207536Smav *	Push back the most recent token read. We only need one level of
164207536Smav *	this, so the thing is just stored in 'condPushback'.
165207536Smav *
166207536Smav * Side Effects:
167207536Smav *	condPushback is overwritten.
168207536Smav */
169207536Smavstatic void
170207536SmavCondPushBack(Token t)
171207536Smav{
172207536Smav
173207536Smav	condPushBack = t;
174207536Smav}
175207536Smav
176207536Smav/**
177207536Smav * CondGetArg
178207536Smav *	Find the argument of a built-in function.  parens is set to TRUE
179207536Smav *	if the arguments are bounded by parens.
180207536Smav *
181207536Smav * Results:
182207536Smav *	The length of the argument and the address of the argument.
183207536Smav *
184207536Smav * Side Effects:
185207536Smav *	The pointer is set to point to the closing parenthesis of the
186207536Smav *	function call.
187207536Smav */
188207536Smavstatic int
189207536SmavCondGetArg(char **linePtr, char **argPtr, const char *func, Boolean parens)
190207536Smav{
191207536Smav	char	*cp;
192207536Smav	size_t	argLen;
193207536Smav	Buffer	*buf;
194207536Smav
195207536Smav	cp = *linePtr;
196207536Smav	if (parens) {
197207536Smav		while (*cp != '(' && *cp != '\0') {
198207536Smav			cp++;
199207536Smav		}
200207536Smav		if (*cp == '(') {
201207536Smav			cp++;
202207536Smav		}
203207536Smav	}
204207536Smav
205207536Smav	if (*cp == '\0') {
206207536Smav		/*
207207536Smav		 * No arguments whatsoever. Because 'make' and 'defined'
208207536Smav		 * aren't really "reserved words", we don't print a message.
209207536Smav		 * I think this is better than hitting the user with a warning
210207536Smav		 * message every time s/he uses the word 'make' or 'defined'
211207536Smav		 * at the beginning of a symbol...
212207536Smav		 */
213207536Smav		*argPtr = cp;
214207536Smav		return (0);
215207536Smav	}
216207536Smav
217207536Smav	while (*cp == ' ' || *cp == '\t') {
218207536Smav		cp++;
219207536Smav	}
220207536Smav
221207536Smav	/*
222207536Smav	 * Create a buffer for the argument and start it out at 16 characters
223207536Smav	 * long. Why 16? Why not?
224207536Smav	 */
225207536Smav	buf = Buf_Init(16);
226207536Smav
227207536Smav	while ((strchr(" \t)&|", *cp) == NULL) && (*cp != '\0')) {
228207536Smav		if (*cp == '$') {
229207536Smav			/*
230208393Smav			 * Parse the variable spec and install it as part of
231207536Smav			 * the argument if it's valid. We tell Var_Parse to
232207536Smav			 * complain on an undefined variable, so we don't do
233207536Smav			 * it too. Nor do we return an error, though perhaps
234207536Smav			 * we should...
235207536Smav			 */
236207536Smav			char	*cp2;
237207536Smav			size_t	len = 0;
238207536Smav			Boolean	doFree;
239207536Smav
240208393Smav			cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree);
241207536Smav
242207536Smav			Buf_Append(buf, cp2);
243207536Smav			if (doFree) {
244207536Smav				free(cp2);
245207536Smav			}
246207536Smav			cp += len;
247207536Smav		} else {
248207536Smav			Buf_AddByte(buf, (Byte)*cp);
249207536Smav			cp++;
250207536Smav		}
251207536Smav	}
252207536Smav
253207536Smav	Buf_AddByte(buf, (Byte)'\0');
254207536Smav	*argPtr = (char *)Buf_GetAll(buf, &argLen);
255207536Smav	Buf_Destroy(buf, FALSE);
256207536Smav
257207536Smav	while (*cp == ' ' || *cp == '\t') {
258207536Smav		cp++;
259207536Smav	}
260207536Smav	if (parens && *cp != ')') {
261207536Smav		Parse_Error(PARSE_WARNING,
262207536Smav		    "Missing closing parenthesis for %s()", func);
263207536Smav		return (0);
264207536Smav	} else if (parens) {
265207536Smav		/*
266207536Smav		 * Advance pointer past close parenthesis.
267208393Smav		 */
268208393Smav		cp++;
269208393Smav	}
270208393Smav
271208393Smav	*linePtr = cp;
272208393Smav	return (argLen);
273208393Smav}
274208393Smav
275208393Smav/**
276208393Smav * CondDoDefined
277208393Smav *	Handle the 'defined' function for conditionals.
278208393Smav *
279208393Smav * Results:
280208393Smav *	TRUE if the given variable is defined.
281208393Smav */
282208393Smavstatic Boolean
283208393SmavCondDoDefined(int argLen, char *arg)
284208393Smav{
285208393Smav	char	savec = arg[argLen];
286208393Smav	Boolean	result;
287208393Smav
288208393Smav	arg[argLen] = '\0';
289208393Smav	if (Var_Value(arg, VAR_CMD) != NULL) {
290208393Smav		result = TRUE;
291208393Smav	} else {
292208393Smav		result = FALSE;
293208393Smav	}
294208393Smav	arg[argLen] = savec;
295208393Smav	return (result);
296208393Smav}
297208393Smav
298208393Smav/**
299208393Smav * CondDoMake
300208393Smav *	Handle the 'make' function for conditionals.
301208393Smav *
302208393Smav * Results:
303208393Smav *	TRUE if the given target is being made.
304208393Smav */
305208393Smavstatic Boolean
306207536SmavCondDoMake(int argLen, char *arg)
307207536Smav{
308207536Smav	char	savec = arg[argLen];
309207536Smav	Boolean	result;
310207536Smav	const LstNode *ln;
311207536Smav
312207536Smav	arg[argLen] = '\0';
313207536Smav	result = FALSE;
314207536Smav	LST_FOREACH(ln, &create) {
315207536Smav		if (Str_Match(Lst_Datum(ln), arg)) {
316207536Smav			result = TRUE;
317207536Smav			break;
318207536Smav		}
319207536Smav	}
320207536Smav	arg[argLen] = savec;
321207536Smav	return (result);
322207536Smav}
323207536Smav
324207536Smav/**
325207536Smav * CondDoExists
326214099Smav *	See if the given file exists.
327214099Smav *
328214099Smav * Results:
329214099Smav *	TRUE if the file exists and FALSE if it does not.
330214099Smav */
331207536Smavstatic Boolean
332207536SmavCondDoExists(int argLen, char *arg)
333207536Smav{
334207536Smav	char	savec = arg[argLen];
335207536Smav	Boolean	result;
336207536Smav	char	*path;
337207536Smav
338207536Smav	arg[argLen] = '\0';
339207536Smav	path = Path_FindFile(arg, &dirSearchPath);
340207536Smav	if (path != NULL) {
341207536Smav		result = TRUE;
342207536Smav		free(path);
343214099Smav	} else {
344214099Smav		result = FALSE;
345214099Smav	}
346214099Smav	arg[argLen] = savec;
347214099Smav	return (result);
348207536Smav}
349207536Smav
350207536Smav/**
351207536Smav * CondDoTarget
352207536Smav *	See if the given node exists and is an actual target.
353207536Smav *
354207536Smav * Results:
355207536Smav *	TRUE if the node exists as a target and FALSE if it does not.
356207536Smav */
357207536Smavstatic Boolean
358207536SmavCondDoTarget(int argLen, char *arg)
359207536Smav{
360207536Smav	char	savec = arg[argLen];
361207536Smav	Boolean	result;
362207536Smav	GNode	*gn;
363207536Smav
364207536Smav	arg[argLen] = '\0';
365207536Smav	gn = Targ_FindNode(arg, TARG_NOCREATE);
366207536Smav	if ((gn != NULL) && !OP_NOP(gn->type)) {
367207536Smav		result = TRUE;
368207536Smav	} else {
369207536Smav		result = FALSE;
370207536Smav	}
371207536Smav	arg[argLen] = savec;
372207536Smav	return (result);
373207536Smav}
374207536Smav
375207536Smav/**
376207536Smav * CondCvtArg
377207536Smav *	Convert the given number into a double. If the number begins
378207536Smav *	with 0x, it is interpreted as a hexadecimal integer
379207536Smav *	and converted to a double from there. All other strings just have
380207536Smav *	strtod called on them.
381207536Smav *
382207536Smav * Results:
383207536Smav *	Sets 'value' to double value of string.
384207536Smav *	Returns address of the first character after the last valid
385207536Smav *	character of the converted number.
386207536Smav *
387214099Smav * Side Effects:
388214099Smav *	Can change 'value' even if string is not a valid number.
389207536Smav */
390207536Smavstatic char *
391207536SmavCondCvtArg(char *str, double *value)
392207536Smav{
393207536Smav
394207536Smav	if ((*str == '0') && (str[1] == 'x')) {
395207536Smav		long i;
396207536Smav
397207536Smav		for (str += 2, i = 0; ; str++) {
398207536Smav			int x;
399214099Smav
400214099Smav			if (isdigit((unsigned char)*str))
401207536Smav				x  = *str - '0';
402207536Smav			else if (isxdigit((unsigned char)*str))
403207536Smav				x = 10 + *str -
404207536Smav				    isupper((unsigned char)*str) ? 'A' : 'a';
405207536Smav			else {
406207536Smav				*value = (double)i;
407207536Smav				return (str);
408207536Smav			}
409207536Smav			i = (i << 4) + x;
410207536Smav		}
411207536Smav
412207536Smav	} else {
413207536Smav		char *eptr;
414207536Smav
415207536Smav		*value = strtod(str, &eptr);
416207536Smav		return (eptr);
417207536Smav	}
418207536Smav}
419207536Smav
420207536Smav/**
421207536Smav * CondToken
422207536Smav *	Return the next token from the input.
423207536Smav *
424207536Smav * Results:
425207536Smav *	A Token for the next lexical token in the stream.
426207536Smav *
427207536Smav * Side Effects:
428207536Smav *	condPushback will be set back to None if it is used.
429207536Smav */
430207536Smavstatic Token
431207536SmavCondToken(Boolean doEval)
432207536Smav{
433207536Smav	Token	t;
434207536Smav
435207536Smav	if (condPushBack != None) {
436207536Smav		t = condPushBack;
437207536Smav		condPushBack = None;
438207536Smav		return (t);
439207536Smav	}
440207536Smav
441207536Smav	while (*condExpr == ' ' || *condExpr == '\t') {
442207536Smav		condExpr++;
443207536Smav	}
444207536Smav	switch (*condExpr) {
445207536Smav	  case '(':
446207536Smav		t = LParen;
447207536Smav		condExpr++;
448207536Smav		break;
449207536Smav	  case ')':
450207536Smav		t = RParen;
451207536Smav		condExpr++;
452207536Smav		break;
453207536Smav	  case '|':
454207536Smav		if (condExpr[1] == '|') {
455207536Smav			condExpr++;
456207536Smav		}
457207536Smav		condExpr++;
458207536Smav		t = Or;
459207536Smav		break;
460207536Smav	  case '&':
461207536Smav		if (condExpr[1] == '&') {
462214099Smav			condExpr++;
463214099Smav		}
464207536Smav		condExpr++;
465214102Smav		t = And;
466207536Smav		break;
467207536Smav	  case '!':
468207536Smav		t = Not;
469207536Smav		condExpr++;
470214099Smav		break;
471214099Smav	  case '\n':
472207536Smav	  case '\0':
473207536Smav		t = EndOfFile;
474207536Smav		break;
475207536Smav	  case '$': {
476207536Smav		char		*lhs;
477207536Smav		const char	*op;
478207536Smav		char		*rhs;
479207536Smav		char		zero[] = "0";
480207536Smav		size_t		varSpecLen = 0;
481207536Smav		Boolean		doFree;
482207536Smav
483207536Smav		/*
484207536Smav		 * Parse the variable spec and skip over it, saving its
485207536Smav		 * value in lhs.
486207536Smav		 */
487207536Smav		t = Err;
488207536Smav		lhs = Var_Parse(condExpr, VAR_CMD, doEval,
489207536Smav		    &varSpecLen, &doFree);
490207536Smav		if (lhs == var_Error) {
491207536Smav			/*
492207536Smav			 * Even if !doEval, we still report syntax
493207536Smav			 * errors, which is what getting var_Error
494207536Smav			 * back with !doEval means.
495207536Smav			 */
496207536Smav			return (Err);
497207536Smav		}
498207536Smav		condExpr += varSpecLen;
499207536Smav
500207536Smav		if (!isspace((unsigned char)*condExpr) &&
501207536Smav		    strchr("!=><", *condExpr) == NULL) {
502207536Smav			Buffer *buf;
503207536Smav
504207536Smav			buf = Buf_Init(0);
505207536Smav
506207536Smav			Buf_Append(buf, lhs);
507207536Smav
508207536Smav			if (doFree)
509207536Smav				free(lhs);
510207536Smav
511207536Smav			for (;*condExpr &&
512207536Smav			    !isspace((unsigned char)*condExpr);
513207536Smav			    condExpr++)
514207536Smav				Buf_AddByte(buf, (Byte)*condExpr);
515207536Smav
516207536Smav			Buf_AddByte(buf, (Byte)'\0');
517207536Smav			lhs = (char *)Buf_GetAll(buf, &varSpecLen);
518207536Smav			Buf_Destroy(buf, FALSE);
519207536Smav
520207536Smav			doFree = TRUE;
521207536Smav		}
522207536Smav
523207536Smav		/*
524207536Smav		 * Skip whitespace to get to the operator
525207536Smav		 */
526207536Smav		while (isspace((unsigned char)*condExpr))
527207536Smav			condExpr++;
528207536Smav
529207536Smav		/*
530207536Smav		 * Make sure the operator is a valid one. If it isn't a
531207536Smav		 * known relational operator, pretend we got a
532207536Smav		 * != 0 comparison.
533207536Smav		 */
534207536Smav		op = condExpr;
535207536Smav		switch (*condExpr) {
536207536Smav		  case '!':
537207536Smav		  case '=':
538207536Smav		  case '<':
539207536Smav		  case '>':
540207536Smav			if (condExpr[1] == '=') {
541207536Smav				condExpr += 2;
542207536Smav			} else {
543207536Smav				condExpr += 1;
544207536Smav			}
545207536Smav			while (isspace((unsigned char)*condExpr)) {
546207536Smav				condExpr++;
547207536Smav			}
548207536Smav			if (*condExpr == '\0') {
549207536Smav				Parse_Error(PARSE_WARNING,
550207536Smav				    "Missing right-hand-side of operator");
551207536Smav				goto error;
552207536Smav			}
553207536Smav			rhs = condExpr;
554207536Smav			break;
555207536Smav
556207536Smav		  default:
557207536Smav			op = "!=";
558207536Smav			rhs = zero;
559207536Smav			break;
560207536Smav		}
561207536Smav		if (*rhs == '"') {
562207536Smav			/*
563207536Smav			 * Doing a string comparison. Only allow == and
564207536Smav			 * != for * operators.
565207536Smav			 */
566207536Smav			char	*string;
567207536Smav			char	*cp, *cp2;
568207536Smav			int	qt;
569207536Smav			Buffer	*buf;
570207536Smav
571207536Smav  do_string_compare:
572207536Smav			if (((*op != '!') && (*op != '=')) ||
573207536Smav			    (op[1] != '=')) {
574207536Smav				Parse_Error(PARSE_WARNING,
575207536Smav				    "String comparison operator should "
576207536Smav				    "be either == or !=");
577207536Smav				goto error;
578207536Smav			}
579207536Smav
580207536Smav			buf = Buf_Init(0);
581207536Smav			qt = *rhs == '"' ? 1 : 0;
582207536Smav
583207536Smav			for (cp = &rhs[qt];
584207536Smav			    ((qt && (*cp != '"')) ||
585207536Smav			    (!qt && strchr(" \t)", *cp) == NULL)) &&
586207536Smav			    (*cp != '\0'); cp++) {
587207536Smav				if ((*cp == '\\') && (cp[1] != '\0')) {
588207536Smav					/*
589207536Smav					 * Backslash escapes things --
590207536Smav					 * skip over next character,							 * if it exists.
591207536Smav					 */
592207536Smav					cp++;
593207536Smav					Buf_AddByte(buf, (Byte)*cp);
594207536Smav
595207536Smav				} else if (*cp == '$') {
596207536Smav					size_t	len = 0;
597207536Smav					Boolean	freeIt;
598207536Smav
599207536Smav					cp2 = Var_Parse(cp, VAR_CMD,
600207536Smav					    doEval, &len, &freeIt);
601207536Smav					if (cp2 != var_Error) {
602207536Smav						Buf_Append(buf, cp2);
603207536Smav						if (freeIt) {
604207536Smav							free(cp2);
605207536Smav						}
606207536Smav						cp += len - 1;
607207536Smav					} else {
608207536Smav						Buf_AddByte(buf,
609207536Smav						    (Byte)*cp);
610207536Smav					}
611207536Smav				} else {
612207536Smav					Buf_AddByte(buf, (Byte)*cp);
613207536Smav				}
614207536Smav			}
615207536Smav
616207536Smav			string = Buf_Peel(buf);
617207536Smav
618207536Smav			DEBUGF(COND, ("lhs = \"%s\", rhs = \"%s\", "
619207536Smav			    "op = %.2s\n", lhs, string, op));
620207536Smav			/*
621207536Smav			 * Null-terminate rhs and perform the
622207536Smav			 * comparison. t is set to the result.
623207536Smav			 */
624207536Smav			if (*op == '=') {
625207536Smav				t = strcmp(lhs, string) ? False : True;
626207536Smav			} else {
627207536Smav				t = strcmp(lhs, string) ? True : False;
628207536Smav			}
629207536Smav			free(string);
630207536Smav			if (rhs == condExpr) {
631207536Smav				if (*cp == '\0' || (!qt && *cp == ')'))
632207536Smav					condExpr = cp;
633207536Smav				else
634207536Smav					condExpr = cp + 1;
635207536Smav			}
636207536Smav		} else {
637207536Smav			/*
638207536Smav			 * rhs is either a float or an integer.
639207536Smav			 * Convert both the lhs and the rhs to a
640207536Smav			 * double and compare the two.
641207536Smav			 */
642207536Smav			double	left, right;
643207536Smav			char	*string;
644207536Smav
645207536Smav			if (*CondCvtArg(lhs, &left) != '\0')
646207536Smav				goto do_string_compare;
647207536Smav			if (*rhs == '$') {
648207536Smav				size_t	len = 0;
649207536Smav				Boolean	freeIt;
650207536Smav
651207536Smav				string = Var_Parse(rhs, VAR_CMD, doEval,
652207536Smav				    &len, &freeIt);
653207536Smav				if (string == var_Error) {
654207536Smav					right = 0.0;
655207536Smav				} else {
656207536Smav					if (*CondCvtArg(string,
657207536Smav					    &right) != '\0') {
658207536Smav						if (freeIt)
659207536Smav							free(string);
660207536Smav						goto do_string_compare;
661207536Smav					}
662207536Smav					if (freeIt)
663207536Smav						free(string);
664207536Smav					if (rhs == condExpr)
665207536Smav						condExpr += len;
666207536Smav				}
667207536Smav			} else {
668207536Smav				char *c = CondCvtArg(rhs, &right);
669207536Smav
670207536Smav				if (c == rhs)
671207536Smav					goto do_string_compare;
672207536Smav				if (rhs == condExpr) {
673207536Smav					/*
674207536Smav					 * Skip over the right-hand side
675207536Smav					 */
676207536Smav					condExpr = c;
677207536Smav				}
678207536Smav			}
679207536Smav
680207536Smav			DEBUGF(COND, ("left = %f, right = %f, "
681207536Smav			    "op = %.2s\n", left, right, op));
682207536Smav			switch (op[0]) {
683207536Smav			  case '!':
684207536Smav				if (op[1] != '=') {
685207536Smav					Parse_Error(PARSE_WARNING,
686207536Smav					    "Unknown operator");
687207536Smav					goto error;
688207536Smav				}
689207536Smav				t = (left != right ? True : False);
690207536Smav				break;
691207536Smav			  case '=':
692207536Smav				if (op[1] != '=') {
693207536Smav					Parse_Error(PARSE_WARNING,
694207536Smav					    "Unknown operator");
695207536Smav					goto error;
696207536Smav				}
697207536Smav				t = (left == right ? True : False);
698207536Smav				break;
699207536Smav			  case '<':
700207536Smav				if (op[1] == '=') {
701207536Smav					t = (left <= right?True:False);
702207536Smav				} else {
703207536Smav					t = (left < right?True:False);
704207536Smav				}
705207536Smav				break;
706207536Smav			case '>':
707207536Smav				if (op[1] == '=') {
708207536Smav					t = (left >= right?True:False);
709207536Smav				} else {
710207536Smav					t = (left > right?True:False);
711207536Smav				}
712207536Smav				break;
713207536Smav			default:
714207536Smav				break;
715207536Smav			}
716207536Smav		}
717207536Smav  error:
718207536Smav		if (doFree)
719207536Smav			free(lhs);
720207536Smav		break;
721207536Smav		}
722207536Smav
723207536Smav	  default: {
724207536Smav		CondProc	*evalProc;
725207536Smav		Boolean		invert = FALSE;
726207536Smav		char		*arg;
727207536Smav		int		arglen;
728207536Smav
729207536Smav		if (strncmp(condExpr, "defined", 7) == 0) {
730207536Smav			/*
731207536Smav			 * Use CondDoDefined to evaluate the argument
732207536Smav			 * and CondGetArg to extract the argument from
733207536Smav			 * the 'function call'.
734207536Smav			 */
735207536Smav			evalProc = CondDoDefined;
736207536Smav			condExpr += 7;
737207536Smav			arglen = CondGetArg(&condExpr, &arg,
738207536Smav			    "defined", TRUE);
739207536Smav			if (arglen == 0) {
740207536Smav				condExpr -= 7;
741207536Smav				goto use_default;
742207536Smav			}
743207536Smav
744207536Smav		} else if (strncmp(condExpr, "make", 4) == 0) {
745207536Smav			/*
746207536Smav			 * Use CondDoMake to evaluate the argument and
747207536Smav			 * CondGetArg to extract the argument from the
748207536Smav			 * 'function call'.
749207536Smav			 */
750207536Smav			evalProc = CondDoMake;
751207536Smav			condExpr += 4;
752207536Smav			arglen = CondGetArg(&condExpr, &arg,
753207536Smav			    "make", TRUE);
754207536Smav			if (arglen == 0) {
755207536Smav				condExpr -= 4;
756207536Smav				goto use_default;
757207536Smav			}
758207536Smav
759207536Smav		} else if (strncmp(condExpr, "exists", 6) == 0) {
760207536Smav			/*
761207536Smav			 * Use CondDoExists to evaluate the argument and
762207536Smav			 * CondGetArg to extract the argument from the
763207536Smav			 * 'function call'.
764207536Smav			 */
765207536Smav			evalProc = CondDoExists;
766207536Smav			condExpr += 6;
767207536Smav			arglen = CondGetArg(&condExpr, &arg,
768207536Smav			    "exists", TRUE);
769207536Smav			if (arglen == 0) {
770207536Smav				condExpr -= 6;
771207536Smav				goto use_default;
772207536Smav			}
773207536Smav
774207536Smav		} else if (strncmp(condExpr, "empty", 5) == 0) {
775207536Smav			/*
776207536Smav			 * Use Var_Parse to parse the spec in parens and
777207536Smav			 * return True if the resulting string is empty.
778207536Smav			 */
779207536Smav			size_t	length;
780207536Smav			Boolean	doFree;
781207536Smav			char	*val;
782207536Smav
783207536Smav			condExpr += 5;
784214099Smav
785214099Smav			for (arglen = 0;
786207536Smav			    condExpr[arglen] != '(' &&
787207536Smav			    condExpr[arglen] != '\0'; arglen += 1)
788207536Smav				continue;
789207536Smav
790207536Smav			if (condExpr[arglen] != '\0') {
791207536Smav				length = 0;
792207536Smav				val = Var_Parse(&condExpr[arglen - 1],
793207536Smav				    VAR_CMD, FALSE, &length, &doFree);
794207536Smav				if (val == var_Error) {
795207536Smav					t = Err;
796207536Smav				} else {
797207536Smav					/*
798207536Smav					 * A variable is empty when it
799207536Smav					 * just contains spaces...
800207536Smav					 * 4/15/92, christos
801207536Smav					 */
802207536Smav					char *p;
803207536Smav
804207536Smav					for (p = val;
805207536Smav					    *p &&
806207536Smav					    isspace((unsigned char)*p);
807207536Smav					    p++)
808207536Smav						continue;
809207536Smav					t = (*p == '\0') ? True : False;
810207536Smav				}
811207536Smav				if (doFree) {
812207536Smav					free(val);
813207536Smav				}
814207536Smav				/*
815207536Smav				 * Advance condExpr to beyond the
816207536Smav				 * closing ). Note that we subtract
817207536Smav				 * one from arglen + length b/c length
818207536Smav				 * is calculated from
819207536Smav				 * condExpr[arglen - 1].
820207536Smav				 */
821207536Smav				condExpr += arglen + length - 1;
822207536Smav			} else {
823207536Smav				condExpr -= 5;
824207536Smav				goto use_default;
825207536Smav			}
826207536Smav			break;
827207536Smav
828207536Smav		} else if (strncmp(condExpr, "target", 6) == 0) {
829207536Smav			/*
830207536Smav			 * Use CondDoTarget to evaluate the argument and
831207536Smav			 * CondGetArg to extract the argument from the
832220569Smav			 * 'function call'.
833220569Smav			 */
834207536Smav			evalProc = CondDoTarget;
835207536Smav			condExpr += 6;
836207536Smav			arglen = CondGetArg(&condExpr, &arg,
837207536Smav			    "target", TRUE);
838207536Smav			if (arglen == 0) {
839207536Smav				condExpr -= 6;
840207536Smav				goto use_default;
841207536Smav			}
842207536Smav
843207536Smav		} else {
844207536Smav			/*
845207536Smav			 * The symbol is itself the argument to the
846207536Smav			 * default function. We advance condExpr to
847207536Smav			 * the end of the symbol by hand (the next
848207536Smav			 * whitespace, closing paren or binary operator)
849207536Smav			 * and set to invert the evaluation
850207536Smav			 * function if condInvert is TRUE.
851207536Smav			 */
852207536Smav  use_default:
853207536Smav			invert = condInvert;
854207536Smav			evalProc = condDefProc;
855207536Smav			arglen = CondGetArg(&condExpr, &arg, "", FALSE);
856207536Smav		}
857207536Smav
858207536Smav		/*
859207536Smav		 * Evaluate the argument using the set function. If
860207536Smav		 * invert is TRUE, we invert the sense of the function.
861207536Smav		 */
862207536Smav		t = (!doEval || (* evalProc) (arglen, arg) ?
863207536Smav		    (invert ? False : True) :
864207536Smav		    (invert ? True : False));
865207536Smav		free(arg);
866207536Smav		break;
867207536Smav		}
868207536Smav	}
869207536Smav	return (t);
870207536Smav}
871207536Smav
872207536Smav/**
873207536Smav * CondT
874207536Smav *	Parse a single term in the expression. This consists of a terminal
875207536Smav *	symbol or Not and a terminal symbol (not including the binary
876207536Smav *	operators):
877207536Smav *	    T -> defined(variable) | make(target) | exists(file) | symbol
878207536Smav *	    T -> ! T | ( E )
879207536Smav *
880214099Smav * Results:
881214099Smav *	True, False or Err.
882207536Smav *
883207536Smav * Side Effects:
884207536Smav *	Tokens are consumed.
885207536Smav */
886207536Smavstatic Token
887207536SmavCondT(Boolean doEval)
888207536Smav{
889207536Smav	Token	t;
890207536Smav
891207536Smav	t = CondToken(doEval);
892207536Smav	if (t == EndOfFile) {
893207536Smav		/*
894207536Smav		 * If we reached the end of the expression, the expression
895207536Smav		 * is malformed...
896207536Smav		 */
897207536Smav		t = Err;
898207536Smav	} else if (t == LParen) {
899207536Smav		/*
900207536Smav		 * T -> ( E )
901207536Smav		 */
902207536Smav		t = CondE(doEval);
903207536Smav		if (t != Err) {
904214099Smav			if (CondToken(doEval) != RParen) {
905214099Smav				t = Err;
906220569Smav			}
907207536Smav		}
908207536Smav	} else if (t == Not) {
909207536Smav		t = CondT(doEval);
910207536Smav		if (t == True) {
911207536Smav			t = False;
912214099Smav		} else if (t == False) {
913207536Smav			t = True;
914207536Smav		}
915207536Smav	}
916207536Smav	return (t);
917207536Smav}
918207536Smav
919207536Smav/**
920207536Smav * CondF --
921207536Smav *	Parse a conjunctive factor (nice name, wot?)
922207536Smav *	    F -> T && F | T
923207536Smav *
924207536Smav * Results:
925220569Smav *	True, False or Err
926220569Smav *
927220569Smav * Side Effects:
928220569Smav *	Tokens are consumed.
929207536Smav */
930207536Smavstatic Token
931207536SmavCondF(Boolean doEval)
932207536Smav{
933207536Smav	Token	l, o;
934207536Smav
935207536Smav	l = CondT(doEval);
936207536Smav	if (l != Err) {
937207536Smav		o = CondToken(doEval);
938207536Smav
939207536Smav		if (o == And) {
940207536Smav			/*
941207536Smav			 * F -> T && F
942220569Smav			 *
943220569Smav			 * If T is False, the whole thing will be False, but
944220569Smav			 * we have to parse the r.h.s. anyway (to throw it
945220569Smav			 * away). If T is True, the result is the r.h.s.,
946220569Smav			 * be it an Err or no.
947220569Smav			 */
948220569Smav			if (l == True) {
949220569Smav				l = CondF(doEval);
950220569Smav			} else {
951220569Smav				CondF(FALSE);
952220569Smav			}
953220569Smav		} else {
954220569Smav			/*
955207536Smav			 * F -> T
956207536Smav			 */
957207536Smav			CondPushBack(o);
958207536Smav		}
959207536Smav	}
960207536Smav	return (l);
961207536Smav}
962207536Smav
963207536Smav/**
964207536Smav * CondE --
965207536Smav *	Main expression production.
966207536Smav *	    E -> F || E | F
967207536Smav *
968207536Smav * Results:
969207536Smav *	True, False or Err.
970207536Smav *
971207536Smav * Side Effects:
972207536Smav *	Tokens are, of course, consumed.
973207536Smav */
974207536Smavstatic Token
975207536SmavCondE(Boolean doEval)
976207536Smav{
977207536Smav	Token   l, o;
978207536Smav
979207536Smav	l = CondF(doEval);
980207536Smav	if (l != Err) {
981207536Smav		o = CondToken(doEval);
982207536Smav
983207536Smav		if (o == Or) {
984207536Smav			/*
985207536Smav			 * E -> F || E
986207536Smav			 *
987207536Smav			 * A similar thing occurs for ||, except that here we
988214099Smav			 * make sure the l.h.s. is False before we bother to
989214099Smav			 * evaluate the r.h.s. Once again, if l is False, the
990207536Smav			 * result is the r.h.s. and once again if l is True,
991207536Smav			 * we parse the r.h.s. to throw it away.
992207536Smav			 */
993207536Smav			if (l == False) {
994207536Smav				l = CondE(doEval);
995207536Smav			} else {
996207536Smav				CondE(FALSE);
997207536Smav			}
998207536Smav		} else {
999207536Smav			/*
1000207536Smav			 * E -> F
1001207536Smav			 */
1002207536Smav			CondPushBack(o);
1003207536Smav		}
1004207536Smav	}
1005214102Smav	return (l);
1006214102Smav}
1007207536Smav
1008207536Smav/**
1009214102Smav * Cond_If
1010214102Smav *	Handle .if<X> and .elif<X> directives.
1011214102Smav *	This function is called even when we're skipping.
1012214102Smav */
1013207536Smavvoid
1014207536SmavCond_If(char *line, int code, int lineno)
1015207536Smav{
1016214102Smav	const struct If	*ifp;
1017207536Smav	Boolean value;
1018207536Smav
1019207536Smav	ifp = &ifs[code];
1020214102Smav
1021214102Smav	if (ifp->isElse) {
1022207536Smav		if (condTop == MAXIF) {
1023207536Smav			Parse_Error(PARSE_FATAL, "if-less elif");
1024207536Smav			return;
1025207536Smav		}
1026207536Smav		if (skipIfLevel != 0) {
1027207536Smav			/*
1028214102Smav			 * If skipping this conditional, just ignore
1029214102Smav			 * the whole thing. If we don't, the user
1030214102Smav			 * might be employing a variable that's
1031214102Smav			 * undefined, for which there's an enclosing
1032214102Smav			 * ifdef that we're skipping...
1033214102Smav			 */
1034214102Smav			skipIfLineno[skipIfLevel - 1] = lineno;
1035214102Smav			return;
1036207536Smav		}
1037207536Smav
1038214102Smav	} else if (skipLine) {
1039214102Smav		/*
1040207536Smav		 * Don't even try to evaluate a conditional that's
1041207536Smav		 * not an else if we're skipping things...
1042214102Smav		 */
1043214102Smav		skipIfLineno[skipIfLevel] = lineno;
1044214102Smav		skipIfLevel += 1;
1045214102Smav		return;
1046214102Smav	}
1047214102Smav
1048214102Smav	/*
1049214102Smav	 * Initialize file-global variables for parsing
1050214102Smav	 */
1051214102Smav	condDefProc = ifp->defProc;
1052214102Smav	condInvert = ifp->doNot;
1053207536Smav
1054207536Smav	while (*line == ' ' || *line == '\t') {
1055207536Smav		line++;
1056207536Smav	}
1057207536Smav
1058207536Smav	condExpr = line;
1059207536Smav	condPushBack = None;
1060207536Smav
1061207536Smav	switch (CondE(TRUE)) {
1062207536Smav	  case True:
1063207536Smav		if (CondToken(TRUE) != EndOfFile)
1064207536Smav			goto err;
1065207536Smav		value = TRUE;
1066207536Smav		break;
1067207536Smav
1068207536Smav	  case False:
1069207536Smav		if (CondToken(TRUE) != EndOfFile)
1070207536Smav			goto err;
1071207536Smav		value = FALSE;
1072207536Smav		break;
1073207536Smav
1074207536Smav	  case Err:
1075207536Smav  err:		Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line);
1076207536Smav		return;
1077207536Smav
1078207536Smav	  default:
1079207536Smav		abort();
1080207536Smav	}
1081207536Smav
1082207536Smav	if (!ifp->isElse) {
1083207536Smav		/* push this value */
1084207536Smav		condTop -= 1;
1085207536Smav
1086207536Smav	} else if (skipIfLevel != 0 || condStack[condTop]) {
1087207536Smav		/*
1088207536Smav		 * If this is an else-type conditional, it should only take
1089207536Smav		 * effect if its corresponding if was evaluated and FALSE.
1090207536Smav		 * If its if was TRUE or skipped, we return COND_SKIP (and
1091207536Smav		 * start skipping in case we weren't already), leaving the
1092207536Smav		 * stack unmolested so later elif's don't screw up...
1093207536Smav		 */
1094207536Smav		skipLine = TRUE;
1095207536Smav		return;
1096207536Smav	}
1097207536Smav
1098207536Smav	if (condTop < 0) {
1099207536Smav		/*
1100207536Smav		 * This is the one case where we can definitely proclaim a fatal
1101207536Smav		 * error. If we don't, we're hosed.
1102207536Smav		 */
1103207536Smav		Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.",MAXIF);
1104207536Smav		return;
1105207536Smav	}
1106207536Smav
1107207536Smav	/* push */
1108207536Smav	condStack[condTop] = value;
1109207536Smav	condLineno[condTop] = lineno;
1110207536Smav	skipLine = !value;
1111207536Smav}
1112207536Smav
1113207536Smav/**
1114207536Smav * Cond_Else
1115207536Smav *	Handle .else statement.
1116207536Smav */
1117207536Smavvoid
1118207536SmavCond_Else(char *line __unused, int code __unused, int lineno __unused)
1119207536Smav{
1120207536Smav
1121207536Smav	while (isspace((u_char)*line))
1122207536Smav		line++;
1123207536Smav
1124207536Smav	if (*line != '\0' && (warn_flags & WARN_DIRSYNTAX)) {
1125207536Smav		Parse_Error(PARSE_WARNING, "junk after .else ignored '%s'",
1126207536Smav		    line);
1127207536Smav	}
1128207536Smav
1129207536Smav	if (condTop == MAXIF) {
1130207536Smav		Parse_Error(PARSE_FATAL, "if-less else");
1131207536Smav		return;
1132207536Smav	}
1133207536Smav	if (skipIfLevel != 0)
1134207536Smav		return;
1135207536Smav
1136207536Smav	if (skipIfLevel != 0 || condStack[condTop]) {
1137207536Smav		/*
1138207536Smav		 * An else should only take effect if its corresponding if was
1139207536Smav		 * evaluated and FALSE.
1140207536Smav		 * If its if was TRUE or skipped, we return COND_SKIP (and
1141207536Smav		 * start skipping in case we weren't already), leaving the
1142207536Smav		 * stack unmolested so later elif's don't screw up...
1143207536Smav		 * XXX How does this work with two .else's?
1144207536Smav		 */
1145207536Smav		skipLine = TRUE;
1146207536Smav		return;
1147207536Smav	}
1148207536Smav
1149207536Smav	/* inverse value */
1150207536Smav	condStack[condTop] = !condStack[condTop];
1151207536Smav	skipLine = !condStack[condTop];
1152207536Smav}
1153207536Smav
1154207536Smav/**
1155207536Smav * Cond_Endif
1156207536Smav *	Handle .endif statement.
1157207536Smav */
1158207536Smavvoid
1159207536SmavCond_Endif(char *line __unused, int code __unused, int lineno __unused)
1160207536Smav{
1161207536Smav
1162207536Smav	while (isspace((u_char)*line))
1163207536Smav		line++;
1164207536Smav
1165207536Smav	if (*line != '\0' && (warn_flags & WARN_DIRSYNTAX)) {
1166207536Smav		Parse_Error(PARSE_WARNING, "junk after .endif ignored '%s'",
1167207536Smav		    line);
1168207536Smav	}
1169207536Smav
1170207536Smav	/*
1171207536Smav	 * End of a conditional section. If skipIfLevel is non-zero,
1172207536Smav	 * that conditional was skipped, so lines following it should
1173207536Smav	 * also be skipped. Hence, we return COND_SKIP. Otherwise,
1174207536Smav	 * the conditional was read so succeeding lines should be
1175207536Smav	 * parsed (think about it...) so we return COND_PARSE, unless
1176207536Smav	 * this endif isn't paired with a decent if.
1177207536Smav	 */
1178207536Smav	if (skipIfLevel != 0) {
1179207536Smav		skipIfLevel -= 1;
1180207536Smav		return;
1181207536Smav	}
1182207536Smav
1183207536Smav	if (condTop == MAXIF) {
1184207536Smav		Parse_Error(PARSE_FATAL, "if-less endif");
1185207536Smav		return;
1186207536Smav	}
1187207536Smav
1188207536Smav	/* pop */
1189207536Smav	skipLine = FALSE;
1190207536Smav	condTop += 1;
1191207536Smav}
1192207536Smav
1193207536Smav/**
1194207536Smav * Cond_End
1195207536Smav *	Make sure everything's clean at the end of a makefile.
1196207536Smav *
1197207536Smav * Side Effects:
1198207536Smav *	Parse_Error will be called if open conditionals are around.
1199207536Smav */
1200207536Smavvoid
1201207536SmavCond_End(void)
1202207536Smav{
1203207536Smav	int level;
1204207536Smav
1205207536Smav	if (condTop != MAXIF) {
1206207536Smav		Parse_Error(PARSE_FATAL, "%d open conditional%s:",
1207207536Smav		    MAXIF - condTop + skipIfLevel,
1208207536Smav		    MAXIF - condTop + skipIfLevel== 1 ? "" : "s");
1209207536Smav
1210207536Smav		for (level = skipIfLevel; level > 0; level--)
1211207536Smav			Parse_Error(PARSE_FATAL, "\t%*sat line %d (skipped)",
1212207536Smav			    MAXIF - condTop + level + 1, "",
1213207536Smav			    skipIfLineno[level - 1]);
1214207536Smav		for (level = condTop; level < MAXIF; level++)
1215207536Smav			Parse_Error(PARSE_FATAL, "\t%*sat line %d "
1216207536Smav			    "(evaluated to %s)", MAXIF - level + skipIfLevel,
1217207536Smav			    "", condLineno[level],
1218207536Smav			    condStack[level] ? "true" : "false");
1219207536Smav	}
1220207536Smav	condTop = MAXIF;
1221207536Smav}
1222207536Smav