Home | History | Annotate | Line # | Download | only in make
cond.c revision 1.48
      1 /*	$NetBSD: cond.c,v 1.48 2008/11/29 14:42:21 dsl Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Adam de Boor.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. Neither the name of the University nor the names of its contributors
     19  *    may be used to endorse or promote products derived from this software
     20  *    without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 /*
     36  * Copyright (c) 1988, 1989 by Adam de Boor
     37  * Copyright (c) 1989 by Berkeley Softworks
     38  * All rights reserved.
     39  *
     40  * This code is derived from software contributed to Berkeley by
     41  * Adam de Boor.
     42  *
     43  * Redistribution and use in source and binary forms, with or without
     44  * modification, are permitted provided that the following conditions
     45  * are met:
     46  * 1. Redistributions of source code must retain the above copyright
     47  *    notice, this list of conditions and the following disclaimer.
     48  * 2. Redistributions in binary form must reproduce the above copyright
     49  *    notice, this list of conditions and the following disclaimer in the
     50  *    documentation and/or other materials provided with the distribution.
     51  * 3. All advertising materials mentioning features or use of this software
     52  *    must display the following acknowledgement:
     53  *	This product includes software developed by the University of
     54  *	California, Berkeley and its contributors.
     55  * 4. Neither the name of the University nor the names of its contributors
     56  *    may be used to endorse or promote products derived from this software
     57  *    without specific prior written permission.
     58  *
     59  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     60  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     61  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     62  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     63  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     64  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     65  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     66  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     67  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     68  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     69  * SUCH DAMAGE.
     70  */
     71 
     72 #ifndef MAKE_NATIVE
     73 static char rcsid[] = "$NetBSD: cond.c,v 1.48 2008/11/29 14:42:21 dsl Exp $";
     74 #else
     75 #include <sys/cdefs.h>
     76 #ifndef lint
     77 #if 0
     78 static char sccsid[] = "@(#)cond.c	8.2 (Berkeley) 1/2/94";
     79 #else
     80 __RCSID("$NetBSD: cond.c,v 1.48 2008/11/29 14:42:21 dsl Exp $");
     81 #endif
     82 #endif /* not lint */
     83 #endif
     84 
     85 /*-
     86  * cond.c --
     87  *	Functions to handle conditionals in a makefile.
     88  *
     89  * Interface:
     90  *	Cond_Eval 	Evaluate the conditional in the passed line.
     91  *
     92  */
     93 
     94 #include    <ctype.h>
     95 #include    <errno.h>    /* For strtoul() error checking */
     96 
     97 #include    "make.h"
     98 #include    "hash.h"
     99 #include    "dir.h"
    100 #include    "buf.h"
    101 
    102 /*
    103  * The parsing of conditional expressions is based on this grammar:
    104  *	E -> F || E
    105  *	E -> F
    106  *	F -> T && F
    107  *	F -> T
    108  *	T -> defined(variable)
    109  *	T -> make(target)
    110  *	T -> exists(file)
    111  *	T -> empty(varspec)
    112  *	T -> target(name)
    113  *	T -> commands(name)
    114  *	T -> symbol
    115  *	T -> $(varspec) op value
    116  *	T -> $(varspec) == "string"
    117  *	T -> $(varspec) != "string"
    118  *	T -> "string"
    119  *	T -> ( E )
    120  *	T -> ! T
    121  *	op -> == | != | > | < | >= | <=
    122  *
    123  * 'symbol' is some other symbol to which the default function (condDefProc)
    124  * is applied.
    125  *
    126  * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
    127  * will return And for '&' and '&&', Or for '|' and '||', Not for '!',
    128  * LParen for '(', RParen for ')' and will evaluate the other terminal
    129  * symbols, using either the default function or the function given in the
    130  * terminal, and return the result as either True or False.
    131  *
    132  * All Non-Terminal functions (CondE, CondF and CondT) return Err on error.
    133  */
    134 typedef enum {
    135     And, Or, Not, True, False, LParen, RParen, EndOfFile, None, Err
    136 } Token;
    137 
    138 /*-
    139  * Structures to handle elegantly the different forms of #if's. The
    140  * last two fields are stored in condInvert and condDefProc, respectively.
    141  */
    142 static void CondPushBack(Token);
    143 static int CondGetArg(char **, char **, const char *, Boolean);
    144 static Boolean CondDoDefined(int, char *);
    145 static int CondStrMatch(ClientData, ClientData);
    146 static Boolean CondDoMake(int, char *);
    147 static Boolean CondDoExists(int, char *);
    148 static Boolean CondDoTarget(int, char *);
    149 static Boolean CondDoCommands(int, char *);
    150 static Boolean CondCvtArg(char *, double *);
    151 static Token CondToken(Boolean);
    152 static Token CondT(Boolean);
    153 static Token CondF(Boolean);
    154 static Token CondE(Boolean);
    155 
    156 static const struct If {
    157     const char	*form;	      /* Form of if */
    158     int		formlen;      /* Length of form */
    159     Boolean	doNot;	      /* TRUE if default function should be negated */
    160     Boolean	(*defProc)(int, char *); /* Default function to apply */
    161 } ifs[] = {
    162     { "def",	  3,	  FALSE,  CondDoDefined },
    163     { "ndef",	  4,	  TRUE,	  CondDoDefined },
    164     { "make",	  4,	  FALSE,  CondDoMake },
    165     { "nmake",	  5,	  TRUE,	  CondDoMake },
    166     { "",	  0,	  FALSE,  CondDoDefined },
    167     { NULL,	  0,	  FALSE,  NULL }
    168 };
    169 
    170 static Boolean	  condInvert;	    	/* Invert the default function */
    171 static Boolean	  (*condDefProc)(int, char *);	/* Default function to apply */
    172 static char 	  *condExpr;	    	/* The expression to parse */
    173 static Token	  condPushBack=None;	/* Single push-back token used in
    174 					 * parsing */
    175 
    176 static unsigned int	cond_depth = 0;  	/* current .if nesting level */
    177 static unsigned int	cond_min_depth = 0;  	/* depth at makefile open */
    178 
    179 static int
    180 istoken(const char *str, const char *tok, size_t len)
    181 {
    182 	return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]);
    183 }
    184 
    185 /*-
    186  *-----------------------------------------------------------------------
    187  * CondPushBack --
    188  *	Push back the most recent token read. We only need one level of
    189  *	this, so the thing is just stored in 'condPushback'.
    190  *
    191  * Input:
    192  *	t		Token to push back into the "stream"
    193  *
    194  * Results:
    195  *	None.
    196  *
    197  * Side Effects:
    198  *	condPushback is overwritten.
    199  *
    200  *-----------------------------------------------------------------------
    201  */
    202 static void
    203 CondPushBack(Token t)
    204 {
    205     condPushBack = t;
    206 }
    207 
    208 /*-
    210  *-----------------------------------------------------------------------
    211  * CondGetArg --
    212  *	Find the argument of a built-in function.
    213  *
    214  * Input:
    215  *	parens		TRUE if arg should be bounded by parens
    216  *
    217  * Results:
    218  *	The length of the argument and the address of the argument.
    219  *
    220  * Side Effects:
    221  *	The pointer is set to point to the closing parenthesis of the
    222  *	function call.
    223  *
    224  *-----------------------------------------------------------------------
    225  */
    226 static int
    227 CondGetArg(char **linePtr, char **argPtr, const char *func, Boolean parens)
    228 {
    229     char	  *cp;
    230     int	    	  argLen;
    231     Buffer	  buf;
    232 
    233     cp = *linePtr;
    234     if (parens) {
    235 	while (*cp != '(' && *cp != '\0') {
    236 	    cp++;
    237 	}
    238 	if (*cp == '(') {
    239 	    cp++;
    240 	}
    241     }
    242 
    243     if (*cp == '\0') {
    244 	/*
    245 	 * No arguments whatsoever. Because 'make' and 'defined' aren't really
    246 	 * "reserved words", we don't print a message. I think this is better
    247 	 * than hitting the user with a warning message every time s/he uses
    248 	 * the word 'make' or 'defined' at the beginning of a symbol...
    249 	 */
    250 	*argPtr = NULL;
    251 	return (0);
    252     }
    253 
    254     while (*cp == ' ' || *cp == '\t') {
    255 	cp++;
    256     }
    257 
    258     /*
    259      * Create a buffer for the argument and start it out at 16 characters
    260      * long. Why 16? Why not?
    261      */
    262     buf = Buf_Init(16);
    263 
    264     while ((strchr(" \t)&|", *cp) == NULL) && (*cp != '\0')) {
    265 	if (*cp == '$') {
    266 	    /*
    267 	     * Parse the variable spec and install it as part of the argument
    268 	     * if it's valid. We tell Var_Parse to complain on an undefined
    269 	     * variable, so we don't do it too. Nor do we return an error,
    270 	     * though perhaps we should...
    271 	     */
    272 	    char  	*cp2;
    273 	    int		len;
    274 	    void	*freeIt;
    275 
    276 	    cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &freeIt);
    277 	    Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
    278 	    if (freeIt)
    279 		free(freeIt);
    280 	    cp += len;
    281 	} else {
    282 	    Buf_AddByte(buf, (Byte)*cp);
    283 	    cp++;
    284 	}
    285     }
    286 
    287     Buf_AddByte(buf, (Byte)'\0');
    288     *argPtr = (char *)Buf_GetAll(buf, &argLen);
    289     Buf_Destroy(buf, FALSE);
    290 
    291     while (*cp == ' ' || *cp == '\t') {
    292 	cp++;
    293     }
    294     if (parens && *cp != ')') {
    295 	Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
    296 		     func);
    297 	return (0);
    298     } else if (parens) {
    299 	/*
    300 	 * Advance pointer past close parenthesis.
    301 	 */
    302 	cp++;
    303     }
    304 
    305     *linePtr = cp;
    306     return (argLen);
    307 }
    308 
    309 /*-
    311  *-----------------------------------------------------------------------
    312  * CondDoDefined --
    313  *	Handle the 'defined' function for conditionals.
    314  *
    315  * Results:
    316  *	TRUE if the given variable is defined.
    317  *
    318  * Side Effects:
    319  *	None.
    320  *
    321  *-----------------------------------------------------------------------
    322  */
    323 static Boolean
    324 CondDoDefined(int argLen, char *arg)
    325 {
    326     char    savec = arg[argLen];
    327     char    *p1;
    328     Boolean result;
    329 
    330     arg[argLen] = '\0';
    331     if (Var_Value(arg, VAR_CMD, &p1) != NULL) {
    332 	result = TRUE;
    333     } else {
    334 	result = FALSE;
    335     }
    336     if (p1)
    337 	free(p1);
    338     arg[argLen] = savec;
    339     return (result);
    340 }
    341 
    342 /*-
    344  *-----------------------------------------------------------------------
    345  * CondStrMatch --
    346  *	Front-end for Str_Match so it returns 0 on match and non-zero
    347  *	on mismatch. Callback function for CondDoMake via Lst_Find
    348  *
    349  * Results:
    350  *	0 if string matches pattern
    351  *
    352  * Side Effects:
    353  *	None
    354  *
    355  *-----------------------------------------------------------------------
    356  */
    357 static int
    358 CondStrMatch(ClientData string, ClientData pattern)
    359 {
    360     return(!Str_Match((char *)string,(char *)pattern));
    361 }
    362 
    363 /*-
    365  *-----------------------------------------------------------------------
    366  * CondDoMake --
    367  *	Handle the 'make' function for conditionals.
    368  *
    369  * Results:
    370  *	TRUE if the given target is being made.
    371  *
    372  * Side Effects:
    373  *	None.
    374  *
    375  *-----------------------------------------------------------------------
    376  */
    377 static Boolean
    378 CondDoMake(int argLen, char *arg)
    379 {
    380     char    savec = arg[argLen];
    381     Boolean result;
    382 
    383     arg[argLen] = '\0';
    384     if (Lst_Find(create, arg, CondStrMatch) == NILLNODE) {
    385 	result = FALSE;
    386     } else {
    387 	result = TRUE;
    388     }
    389     arg[argLen] = savec;
    390     return (result);
    391 }
    392 
    393 /*-
    395  *-----------------------------------------------------------------------
    396  * CondDoExists --
    397  *	See if the given file exists.
    398  *
    399  * Results:
    400  *	TRUE if the file exists and FALSE if it does not.
    401  *
    402  * Side Effects:
    403  *	None.
    404  *
    405  *-----------------------------------------------------------------------
    406  */
    407 static Boolean
    408 CondDoExists(int argLen, char *arg)
    409 {
    410     char    savec = arg[argLen];
    411     Boolean result;
    412     char    *path;
    413 
    414     arg[argLen] = '\0';
    415     path = Dir_FindFile(arg, dirSearchPath);
    416     if (path != NULL) {
    417 	result = TRUE;
    418 	free(path);
    419     } else {
    420 	result = FALSE;
    421     }
    422     arg[argLen] = savec;
    423     if (DEBUG(COND)) {
    424 	fprintf(debug_file, "exists(%s) result is \"%s\"\n",
    425 	       arg, path ? path : "");
    426     }
    427     return (result);
    428 }
    429 
    430 /*-
    432  *-----------------------------------------------------------------------
    433  * CondDoTarget --
    434  *	See if the given node exists and is an actual target.
    435  *
    436  * Results:
    437  *	TRUE if the node exists as a target and FALSE if it does not.
    438  *
    439  * Side Effects:
    440  *	None.
    441  *
    442  *-----------------------------------------------------------------------
    443  */
    444 static Boolean
    445 CondDoTarget(int argLen, char *arg)
    446 {
    447     char    savec = arg[argLen];
    448     Boolean result;
    449     GNode   *gn;
    450 
    451     arg[argLen] = '\0';
    452     gn = Targ_FindNode(arg, TARG_NOCREATE);
    453     if ((gn != NILGNODE) && !OP_NOP(gn->type)) {
    454 	result = TRUE;
    455     } else {
    456 	result = FALSE;
    457     }
    458     arg[argLen] = savec;
    459     return (result);
    460 }
    461 
    462 /*-
    463  *-----------------------------------------------------------------------
    464  * CondDoCommands --
    465  *	See if the given node exists and is an actual target with commands
    466  *	associated with it.
    467  *
    468  * Results:
    469  *	TRUE if the node exists as a target and has commands associated with
    470  *	it and FALSE if it does not.
    471  *
    472  * Side Effects:
    473  *	None.
    474  *
    475  *-----------------------------------------------------------------------
    476  */
    477 static Boolean
    478 CondDoCommands(int argLen, char *arg)
    479 {
    480     char    savec = arg[argLen];
    481     Boolean result;
    482     GNode   *gn;
    483 
    484     arg[argLen] = '\0';
    485     gn = Targ_FindNode(arg, TARG_NOCREATE);
    486     if ((gn != NILGNODE) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands)) {
    487 	result = TRUE;
    488     } else {
    489 	result = FALSE;
    490     }
    491     arg[argLen] = savec;
    492     return (result);
    493 }
    494 
    495 /*-
    497  *-----------------------------------------------------------------------
    498  * CondCvtArg --
    499  *	Convert the given number into a double.
    500  *	We try a base 10 or 16 integer conversion first, if that fails
    501  *	then we try a floating point conversion instead.
    502  *
    503  * Results:
    504  *	Sets 'value' to double value of string.
    505  *	Returns 'true' if the convertion suceeded
    506  *
    507  *-----------------------------------------------------------------------
    508  */
    509 static Boolean
    510 CondCvtArg(char *str, double *value)
    511 {
    512     char *eptr, ech;
    513     unsigned long l_val;
    514     double d_val;
    515 
    516     errno = 0;
    517     l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10);
    518     ech = *eptr;
    519     if (ech == 0 && errno != ERANGE) {
    520 	d_val = str[0] == '-' ? -(double)-l_val : (double)l_val;
    521     } else {
    522 	if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E')
    523 	    return FALSE;
    524 	d_val = strtod(str, &eptr);
    525 	if (*eptr)
    526 	    return FALSE;
    527     }
    528 
    529     *value = d_val;
    530     return TRUE;
    531 }
    532 
    533 /*-
    534  *-----------------------------------------------------------------------
    535  * CondGetString --
    536  *	Get a string from a variable reference or an optionally quoted
    537  *	string.  This is called for the lhs and rhs of string compares.
    538  *
    539  * Results:
    540  *	Sets freeIt if needed,
    541  *	Sets quoted if string was quoted,
    542  *	Returns NULL on error,
    543  *	else returns string - absent any quotes.
    544  *
    545  * Side Effects:
    546  *	Moves condExpr to end of this token.
    547  *
    548  *
    549  *-----------------------------------------------------------------------
    550  */
    551 /* coverity:[+alloc : arg-*2] */
    552 static char *
    553 CondGetString(Boolean doEval, Boolean *quoted, void **freeIt)
    554 {
    555     Buffer buf;
    556     char *cp;
    557     char *str;
    558     int	len;
    559     int qt;
    560     char *start;
    561 
    562     buf = Buf_Init(0);
    563     str = NULL;
    564     *freeIt = NULL;
    565     *quoted = qt = *condExpr == '"' ? 1 : 0;
    566     if (qt)
    567 	condExpr++;
    568     for (start = condExpr; *condExpr && str == NULL; condExpr++) {
    569 	switch (*condExpr) {
    570 	case '\\':
    571 	    if (condExpr[1] != '\0') {
    572 		condExpr++;
    573 		Buf_AddByte(buf, (Byte)*condExpr);
    574 	    }
    575 	    break;
    576 	case '"':
    577 	    if (qt) {
    578 		condExpr++;		/* we don't want the quotes */
    579 		goto got_str;
    580 	    } else
    581 		Buf_AddByte(buf, (Byte)*condExpr); /* likely? */
    582 	    break;
    583 	case ')':
    584 	case '!':
    585 	case '=':
    586 	case '>':
    587 	case '<':
    588 	case ' ':
    589 	case '\t':
    590 	    if (!qt)
    591 		goto got_str;
    592 	    else
    593 		Buf_AddByte(buf, (Byte)*condExpr);
    594 	    break;
    595 	case '$':
    596 	    /* if we are in quotes, then an undefined variable is ok */
    597 	    str = Var_Parse(condExpr, VAR_CMD, (qt ? 0 : doEval),
    598 			    &len, freeIt);
    599 	    if (str == var_Error) {
    600 		if (*freeIt) {
    601 		    free(*freeIt);
    602 		    *freeIt = NULL;
    603 		}
    604 		/*
    605 		 * Even if !doEval, we still report syntax errors, which
    606 		 * is what getting var_Error back with !doEval means.
    607 		 */
    608 		str = NULL;
    609 		goto cleanup;
    610 	    }
    611 	    condExpr += len;
    612 	    /*
    613 	     * If the '$' was first char (no quotes), and we are
    614 	     * followed by space, the operator or end of expression,
    615 	     * we are done.
    616 	     */
    617 	    if ((condExpr == start + len) &&
    618 		(*condExpr == '\0' ||
    619 		 isspace((unsigned char) *condExpr) ||
    620 		 strchr("!=><)", *condExpr))) {
    621 		goto cleanup;
    622 	    }
    623 	    /*
    624 	     * Nope, we better copy str to buf
    625 	     */
    626 	    for (cp = str; *cp; cp++) {
    627 		Buf_AddByte(buf, (Byte)*cp);
    628 	    }
    629 	    if (*freeIt) {
    630 		free(*freeIt);
    631 		*freeIt = NULL;
    632 	    }
    633 	    str = NULL;			/* not finished yet */
    634 	    condExpr--;			/* don't skip over next char */
    635 	    break;
    636 	default:
    637 	    Buf_AddByte(buf, (Byte)*condExpr);
    638 	    break;
    639 	}
    640     }
    641  got_str:
    642     Buf_AddByte(buf, (Byte)'\0');
    643     str = (char *)Buf_GetAll(buf, NULL);
    644     *freeIt = str;
    645  cleanup:
    646     Buf_Destroy(buf, FALSE);
    647     return str;
    648 }
    649 
    650 /*-
    652  *-----------------------------------------------------------------------
    653  * CondToken --
    654  *	Return the next token from the input.
    655  *
    656  * Results:
    657  *	A Token for the next lexical token in the stream.
    658  *
    659  * Side Effects:
    660  *	condPushback will be set back to None if it is used.
    661  *
    662  *-----------------------------------------------------------------------
    663  */
    664 static Token
    665 compare_expression(Boolean doEval)
    666 {
    667     Token	t;
    668     char	*lhs;
    669     char	*rhs;
    670     char	*op;
    671     void	*lhsFree;
    672     void	*rhsFree;
    673     Boolean lhsQuoted;
    674     Boolean rhsQuoted;
    675 
    676     rhs = NULL;
    677     lhsFree = rhsFree = FALSE;
    678     lhsQuoted = rhsQuoted = FALSE;
    679 
    680     /*
    681      * Parse the variable spec and skip over it, saving its
    682      * value in lhs.
    683      */
    684     t = Err;
    685     lhs = CondGetString(doEval, &lhsQuoted, &lhsFree);
    686     if (!lhs) {
    687 	if (lhsFree)
    688 	    free(lhsFree);
    689 	return Err;
    690     }
    691     /*
    692      * Skip whitespace to get to the operator
    693      */
    694     while (isspace((unsigned char) *condExpr))
    695 	condExpr++;
    696 
    697     /*
    698      * Make sure the operator is a valid one. If it isn't a
    699      * known relational operator, pretend we got a
    700      * != 0 comparison.
    701      */
    702     op = condExpr;
    703     switch (*condExpr) {
    704 	case '!':
    705 	case '=':
    706 	case '<':
    707 	case '>':
    708 	    if (condExpr[1] == '=') {
    709 		condExpr += 2;
    710 	    } else {
    711 		condExpr += 1;
    712 	    }
    713 	    break;
    714 	default:
    715 	    op = UNCONST("!=");
    716 	    if (lhsQuoted)
    717 		rhs = UNCONST("");
    718 	    else
    719 		rhs = UNCONST("0");
    720 
    721 	    goto do_compare;
    722     }
    723     while (isspace((unsigned char) *condExpr)) {
    724 	condExpr++;
    725     }
    726     if (*condExpr == '\0') {
    727 	Parse_Error(PARSE_WARNING,
    728 		    "Missing right-hand-side of operator");
    729 	goto error;
    730     }
    731     rhs = CondGetString(doEval, &rhsQuoted, &rhsFree);
    732     if (!rhs) {
    733 	if (lhsFree)
    734 	    free(lhsFree);
    735 	if (rhsFree)
    736 	    free(rhsFree);
    737 	return Err;
    738     }
    739 do_compare:
    740     if (rhsQuoted || lhsQuoted) {
    741 do_string_compare:
    742 	if (((*op != '!') && (*op != '=')) || (op[1] != '=')) {
    743 	    Parse_Error(PARSE_WARNING,
    744     "String comparison operator should be either == or !=");
    745 	    goto error;
    746 	}
    747 
    748 	if (DEBUG(COND)) {
    749 	    fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
    750 		   lhs, rhs, op);
    751 	}
    752 	/*
    753 	 * Null-terminate rhs and perform the comparison.
    754 	 * t is set to the result.
    755 	 */
    756 	if (*op == '=') {
    757 	    t = strcmp(lhs, rhs) ? False : True;
    758 	} else {
    759 	    t = strcmp(lhs, rhs) ? True : False;
    760 	}
    761     } else {
    762 	/*
    763 	 * rhs is either a float or an integer. Convert both the
    764 	 * lhs and the rhs to a double and compare the two.
    765 	 */
    766 	double  	left, right;
    767 
    768 	if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right))
    769 	    goto do_string_compare;
    770 
    771 	if (DEBUG(COND)) {
    772 	    fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left,
    773 		   right, op);
    774 	}
    775 	switch(op[0]) {
    776 	case '!':
    777 	    if (op[1] != '=') {
    778 		Parse_Error(PARSE_WARNING,
    779 			    "Unknown operator");
    780 		goto error;
    781 	    }
    782 	    t = (left != right ? True : False);
    783 	    break;
    784 	case '=':
    785 	    if (op[1] != '=') {
    786 		Parse_Error(PARSE_WARNING,
    787 			    "Unknown operator");
    788 		goto error;
    789 	    }
    790 	    t = (left == right ? True : False);
    791 	    break;
    792 	case '<':
    793 	    if (op[1] == '=') {
    794 		t = (left <= right ? True : False);
    795 	    } else {
    796 		t = (left < right ? True : False);
    797 	    }
    798 	    break;
    799 	case '>':
    800 	    if (op[1] == '=') {
    801 		t = (left >= right ? True : False);
    802 	    } else {
    803 		t = (left > right ? True : False);
    804 	    }
    805 	    break;
    806 	}
    807     }
    808 error:
    809     if (lhsFree)
    810 	free(lhsFree);
    811     if (rhsFree)
    812 	free(rhsFree);
    813     return t;
    814 }
    815 
    816 static int
    817 get_mpt_arg(char **linePtr, char **argPtr, const char *func, Boolean parens)
    818 {
    819     /*
    820      * Use Var_Parse to parse the spec in parens and return
    821      * True if the resulting string is empty.
    822      */
    823     int	    length;
    824     void    *freeIt;
    825     char    *val;
    826     char    *cp = *linePtr;
    827 
    828     /* We do all the work here and return the result as the length */
    829     *argPtr = NULL;
    830 
    831     val = Var_Parse(cp - 1, VAR_CMD, FALSE, &length, &freeIt);
    832     /*
    833      * Advance *linePtr to beyond the closing ). Note that
    834      * we subtract one because 'length' is calculated from 'cp - 1'.
    835      */
    836     *linePtr = cp - 1 + length;
    837 
    838     if (val == var_Error) {
    839 	free(freeIt);
    840 	return -1;
    841     }
    842 
    843     /* A variable is empty when it just contains spaces... 4/15/92, christos */
    844     while (isspace(*(unsigned char *)val))
    845 	val++;
    846 
    847     /*
    848      * For consistency with the other functions we can't generate the
    849      * true/false here.
    850      */
    851     length = *val ? 2 : 1;
    852     if (freeIt)
    853 	free(freeIt);
    854     return length;
    855 }
    856 
    857 static Boolean
    858 CondDoEmpty(int arglen, char *arg)
    859 {
    860     return arglen == 1;
    861 }
    862 
    863 static Token
    864 compare_function(Boolean doEval)
    865 {
    866     static const struct fn_def {
    867 	const char  *fn_name;
    868 	int         fn_name_len;
    869         int         (*fn_getarg)(char **, char **, const char *, Boolean);
    870 	Boolean     (*fn_proc)(int, char *);
    871     } fn_defs[] = {
    872 	{ "defined",   7, CondGetArg, CondDoDefined },
    873 	{ "make",      4, CondGetArg, CondDoMake },
    874 	{ "exists",    6, CondGetArg, CondDoExists },
    875 	{ "empty",     5, get_mpt_arg, CondDoEmpty },
    876 	{ "target",    6, CondGetArg, CondDoTarget },
    877 	{ "commands",  8, CondGetArg, CondDoCommands },
    878 	{ NULL,        0, NULL, NULL },
    879     };
    880     const struct fn_def *fn_def;
    881     Token	t;
    882     char	*arg = NULL;
    883     int	arglen;
    884     char *cp = condExpr;
    885     char *cp1;
    886 
    887     for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) {
    888 	if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len))
    889 	    continue;
    890 	cp += fn_def->fn_name_len;
    891 	/* There can only be whitespace before the '(' */
    892 	while (isspace(*(unsigned char *)cp))
    893 	    cp++;
    894 	if (*cp != '(')
    895 	    break;
    896 
    897 	arglen = fn_def->fn_getarg(&cp, &arg, fn_def->fn_name, TRUE);
    898 	if (arglen <= 0) {
    899 	    if (arglen < 0) {
    900 		condExpr = cp;
    901 		return Err;
    902 	    }
    903 	    break;
    904 	}
    905 	/* Evaluate the argument using the required function. */
    906 	t = !doEval || fn_def->fn_proc(arglen, arg) ? True : False;
    907 	if (arg)
    908 	    free(arg);
    909 	condExpr = cp;
    910 	return t;
    911     }
    912 
    913     /* Push anything numeric through the compare expression */
    914     cp = condExpr;
    915     if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0]))
    916 	return compare_expression(doEval);
    917 
    918     /*
    919      * Most likely we have a naked token to apply the default function to.
    920      * However ".if a == b" gets here when the "a" is unquoted and doesn't
    921      * start with a '$'. This surprises people - especially given the way
    922      * that for loops get expanded.
    923      * If what follows the function argument is a '=' or '!' then the syntax
    924      * would be invalid if we did "defined(a)" - so instead treat as an
    925      * expression.
    926      */
    927     arglen = CondGetArg(&cp, &arg, "", FALSE);
    928     for (cp1 = cp; isspace(*(unsigned char *)cp1); cp1++)
    929 	continue;
    930     if (*cp1 == '=' || *cp1 == '!')
    931 	return compare_expression(doEval);
    932     condExpr = cp;
    933 
    934     /*
    935      * Evaluate the argument using the default function. If invert
    936      * is TRUE, we invert the sense of the result.
    937      */
    938     t = !doEval || (* condDefProc)(arglen, arg) != condInvert ? True : False;
    939     if (arg)
    940 	free(arg);
    941     return t;
    942 }
    943 
    944 static Token
    945 CondToken(Boolean doEval)
    946 {
    947     Token t;
    948 
    949     t = condPushBack;
    950     if (t != None) {
    951 	condPushBack = None;
    952 	return t;
    953     }
    954 
    955     while (*condExpr == ' ' || *condExpr == '\t') {
    956 	condExpr++;
    957     }
    958 
    959     switch (*condExpr) {
    960 
    961     case '(':
    962 	condExpr++;
    963 	return LParen;
    964 
    965     case ')':
    966 	condExpr++;
    967 	return RParen;
    968 
    969     case '|':
    970 	if (condExpr[1] == '|') {
    971 	    condExpr++;
    972 	}
    973 	condExpr++;
    974 	return Or;
    975 
    976     case '&':
    977 	if (condExpr[1] == '&') {
    978 	    condExpr++;
    979 	}
    980 	condExpr++;
    981 	return And;
    982 
    983     case '!':
    984 	condExpr++;
    985 	return Not;
    986 
    987     case '#':
    988     case '\n':
    989     case '\0':
    990 	return EndOfFile;
    991 
    992     case '"':
    993     case '$':
    994 	return compare_expression(doEval);
    995 
    996     default:
    997 	return compare_function(doEval);
    998     }
    999 }
   1000 
   1001 /*-
   1002  *-----------------------------------------------------------------------
   1003  * CondT --
   1004  *	Parse a single term in the expression. This consists of a terminal
   1005  *	symbol or Not and a terminal symbol (not including the binary
   1006  *	operators):
   1007  *	    T -> defined(variable) | make(target) | exists(file) | symbol
   1008  *	    T -> ! T | ( E )
   1009  *
   1010  * Results:
   1011  *	True, False or Err.
   1012  *
   1013  * Side Effects:
   1014  *	Tokens are consumed.
   1015  *
   1016  *-----------------------------------------------------------------------
   1017  */
   1018 static Token
   1019 CondT(Boolean doEval)
   1020 {
   1021     Token   t;
   1022 
   1023     t = CondToken(doEval);
   1024 
   1025     if (t == EndOfFile) {
   1026 	/*
   1027 	 * If we reached the end of the expression, the expression
   1028 	 * is malformed...
   1029 	 */
   1030 	t = Err;
   1031     } else if (t == LParen) {
   1032 	/*
   1033 	 * T -> ( E )
   1034 	 */
   1035 	t = CondE(doEval);
   1036 	if (t != Err) {
   1037 	    if (CondToken(doEval) != RParen) {
   1038 		t = Err;
   1039 	    }
   1040 	}
   1041     } else if (t == Not) {
   1042 	t = CondT(doEval);
   1043 	if (t == True) {
   1044 	    t = False;
   1045 	} else if (t == False) {
   1046 	    t = True;
   1047 	}
   1048     }
   1049     return (t);
   1050 }
   1051 
   1052 /*-
   1054  *-----------------------------------------------------------------------
   1055  * CondF --
   1056  *	Parse a conjunctive factor (nice name, wot?)
   1057  *	    F -> T && F | T
   1058  *
   1059  * Results:
   1060  *	True, False or Err
   1061  *
   1062  * Side Effects:
   1063  *	Tokens are consumed.
   1064  *
   1065  *-----------------------------------------------------------------------
   1066  */
   1067 static Token
   1068 CondF(Boolean doEval)
   1069 {
   1070     Token   l, o;
   1071 
   1072     l = CondT(doEval);
   1073     if (l != Err) {
   1074 	o = CondToken(doEval);
   1075 
   1076 	if (o == And) {
   1077 	    /*
   1078 	     * F -> T && F
   1079 	     *
   1080 	     * If T is False, the whole thing will be False, but we have to
   1081 	     * parse the r.h.s. anyway (to throw it away).
   1082 	     * If T is True, the result is the r.h.s., be it an Err or no.
   1083 	     */
   1084 	    if (l == True) {
   1085 		l = CondF(doEval);
   1086 	    } else {
   1087 		(void)CondF(FALSE);
   1088 	    }
   1089 	} else {
   1090 	    /*
   1091 	     * F -> T
   1092 	     */
   1093 	    CondPushBack(o);
   1094 	}
   1095     }
   1096     return (l);
   1097 }
   1098 
   1099 /*-
   1101  *-----------------------------------------------------------------------
   1102  * CondE --
   1103  *	Main expression production.
   1104  *	    E -> F || E | F
   1105  *
   1106  * Results:
   1107  *	True, False or Err.
   1108  *
   1109  * Side Effects:
   1110  *	Tokens are, of course, consumed.
   1111  *
   1112  *-----------------------------------------------------------------------
   1113  */
   1114 static Token
   1115 CondE(Boolean doEval)
   1116 {
   1117     Token   l, o;
   1118 
   1119     l = CondF(doEval);
   1120     if (l != Err) {
   1121 	o = CondToken(doEval);
   1122 
   1123 	if (o == Or) {
   1124 	    /*
   1125 	     * E -> F || E
   1126 	     *
   1127 	     * A similar thing occurs for ||, except that here we make sure
   1128 	     * the l.h.s. is False before we bother to evaluate the r.h.s.
   1129 	     * Once again, if l is False, the result is the r.h.s. and once
   1130 	     * again if l is True, we parse the r.h.s. to throw it away.
   1131 	     */
   1132 	    if (l == False) {
   1133 		l = CondE(doEval);
   1134 	    } else {
   1135 		(void)CondE(FALSE);
   1136 	    }
   1137 	} else {
   1138 	    /*
   1139 	     * E -> F
   1140 	     */
   1141 	    CondPushBack(o);
   1142 	}
   1143     }
   1144     return (l);
   1145 }
   1146 
   1147 /*-
   1148  *-----------------------------------------------------------------------
   1149  * Cond_EvalExpression --
   1150  *	Evaluate an expression in the passed line. The expression
   1151  *	consists of &&, ||, !, make(target), defined(variable)
   1152  *	and parenthetical groupings thereof.
   1153  *
   1154  * Results:
   1155  *	COND_PARSE	if the condition was valid grammatically
   1156  *	COND_INVALID  	if not a valid conditional.
   1157  *
   1158  *	(*value) is set to the boolean value of the condition
   1159  *
   1160  * Side Effects:
   1161  *	None.
   1162  *
   1163  *-----------------------------------------------------------------------
   1164  */
   1165 int
   1166 Cond_EvalExpression(int dosetup, char *line, Boolean *value, int eprint)
   1167 {
   1168     if (dosetup) {
   1169 	condDefProc = CondDoDefined;
   1170 	condInvert = 0;
   1171     }
   1172 
   1173     while (*line == ' ' || *line == '\t')
   1174 	line++;
   1175 
   1176     condExpr = line;
   1177     condPushBack = None;
   1178 
   1179     switch (CondE(TRUE)) {
   1180     case True:
   1181 	if (CondToken(TRUE) == EndOfFile) {
   1182 	    *value = TRUE;
   1183 	    break;
   1184 	}
   1185 	goto err;
   1186 	/*FALLTHRU*/
   1187     case False:
   1188 	if (CondToken(TRUE) == EndOfFile) {
   1189 	    *value = FALSE;
   1190 	    break;
   1191 	}
   1192 	/*FALLTHRU*/
   1193     case Err:
   1194 err:
   1195 	if (eprint)
   1196 	    Parse_Error(PARSE_FATAL, "Malformed conditional (%s)",
   1197 			 line);
   1198 	return (COND_INVALID);
   1199     default:
   1200 	break;
   1201     }
   1202 
   1203     return COND_PARSE;
   1204 }
   1205 
   1206 
   1207 /*-
   1209  *-----------------------------------------------------------------------
   1210  * Cond_Eval --
   1211  *	Evaluate the conditional in the passed line. The line
   1212  *	looks like this:
   1213  *	    .<cond-type> <expr>
   1214  *	where <cond-type> is any of if, ifmake, ifnmake, ifdef,
   1215  *	ifndef, elif, elifmake, elifnmake, elifdef, elifndef
   1216  *	and <expr> consists of &&, ||, !, make(target), defined(variable)
   1217  *	and parenthetical groupings thereof.
   1218  *
   1219  * Input:
   1220  *	line		Line to parse
   1221  *
   1222  * Results:
   1223  *	COND_PARSE	if should parse lines after the conditional
   1224  *	COND_SKIP	if should skip lines after the conditional
   1225  *	COND_INVALID  	if not a valid conditional.
   1226  *
   1227  * Side Effects:
   1228  *	None.
   1229  *
   1230  * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order
   1231  * to detect splurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF)
   1232  * otherwise .else could be treated as '.elif 1'.
   1233  *
   1234  *-----------------------------------------------------------------------
   1235  */
   1236 int
   1237 Cond_Eval(char *line)
   1238 {
   1239     #define	    MAXIF	64	/* maximum depth of .if'ing */
   1240     enum if_states {
   1241 	IF_ACTIVE,		/* .if or .elif part active */
   1242 	ELSE_ACTIVE,		/* .else part active */
   1243 	SEARCH_FOR_ELIF,	/* searching for .elif/else to execute */
   1244 	SKIP_TO_ELSE,           /* has been true, but not seen '.else' */
   1245 	SKIP_TO_ENDIF		/* nothing else to execute */
   1246     };
   1247     static enum if_states cond_state[MAXIF + 1] = { IF_ACTIVE };
   1248 
   1249     const struct If *ifp;
   1250     Boolean 	    isElif;
   1251     Boolean 	    value;
   1252     int	    	    level;  	/* Level at which to report errors. */
   1253     enum if_states  state;
   1254 
   1255     level = PARSE_FATAL;
   1256 
   1257     /* skip leading character (the '.') and any whitespace */
   1258     for (line++; *line == ' ' || *line == '\t'; line++)
   1259 	continue;
   1260 
   1261     /* Find what type of if we're dealing with.  */
   1262     if (line[0] == 'e') {
   1263 	if (line[1] != 'l') {
   1264 	    if (!istoken(line + 1, "ndif", 4))
   1265 		return COND_INVALID;
   1266 	    /* End of conditional section */
   1267 	    if (cond_depth == cond_min_depth) {
   1268 		Parse_Error(level, "if-less endif");
   1269 		return COND_PARSE;
   1270 	    }
   1271 	    /* Return state for previous conditional */
   1272 	    cond_depth--;
   1273 	    if (cond_depth > MAXIF)
   1274 		return COND_SKIP;
   1275 	    return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
   1276 	}
   1277 
   1278 	/* Quite likely this is 'else' or 'elif' */
   1279 	line += 2;
   1280 	if (istoken(line, "se", 2)) {
   1281 	    /* It is else... */
   1282 	    if (cond_depth == cond_min_depth) {
   1283 		Parse_Error(level, "if-less else");
   1284 		return COND_PARSE;
   1285 	    }
   1286 
   1287 	    if (cond_depth > MAXIF)
   1288 		return COND_SKIP;
   1289 	    state = cond_state[cond_depth];
   1290 	    switch (state) {
   1291 	    case SEARCH_FOR_ELIF:
   1292 		state = ELSE_ACTIVE;
   1293 		break;
   1294 	    case ELSE_ACTIVE:
   1295 	    case SKIP_TO_ENDIF:
   1296 		Parse_Error(PARSE_WARNING, "extra else");
   1297 		/* FALLTHROUGH */
   1298 	    default:
   1299 	    case IF_ACTIVE:
   1300 	    case SKIP_TO_ELSE:
   1301 		state = SKIP_TO_ENDIF;
   1302 		break;
   1303 	    }
   1304 	    cond_state[cond_depth] = state;
   1305 	    return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
   1306 	}
   1307 	/* Assume for now it is an elif */
   1308 	isElif = TRUE;
   1309     } else
   1310 	isElif = FALSE;
   1311 
   1312     if (line[0] != 'i' || line[1] != 'f')
   1313 	/* Not an ifxxx or elifxxx line */
   1314 	return COND_INVALID;
   1315 
   1316     /*
   1317      * Figure out what sort of conditional it is -- what its default
   1318      * function is, etc. -- by looking in the table of valid "ifs"
   1319      */
   1320     line += 2;
   1321     for (ifp = ifs; ; ifp++) {
   1322 	if (ifp->form == NULL)
   1323 	    return COND_INVALID;
   1324 	if (istoken(ifp->form, line, ifp->formlen)) {
   1325 	    line += ifp->formlen;
   1326 	    break;
   1327 	}
   1328     }
   1329 
   1330     /* Now we know what sort of 'if' it is... */
   1331 
   1332     if (isElif) {
   1333 	if (cond_depth == cond_min_depth) {
   1334 	    Parse_Error(level, "if-less elif");
   1335 	    return COND_PARSE;
   1336 	}
   1337 	if (cond_depth > MAXIF)
   1338 	    /* Error reported when we saw the .if ... */
   1339 	    return COND_SKIP;
   1340 	state = cond_state[cond_depth];
   1341 	if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) {
   1342 	    Parse_Error(PARSE_WARNING, "extra elif");
   1343 	    cond_state[cond_depth] = SKIP_TO_ENDIF;
   1344 	    return COND_SKIP;
   1345 	}
   1346 	if (state != SEARCH_FOR_ELIF) {
   1347 	    /* Either just finished the 'true' block, or already SKIP_TO_ELSE */
   1348 	    cond_state[cond_depth] = SKIP_TO_ELSE;
   1349 	    return COND_SKIP;
   1350 	}
   1351     } else {
   1352 	/* Normal .if */
   1353 	if (cond_depth >= MAXIF) {
   1354 	    cond_depth++;
   1355 	    Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.", MAXIF);
   1356 	    return COND_SKIP;
   1357 	}
   1358 	state = cond_state[cond_depth];
   1359 	cond_depth++;
   1360 	if (state > ELSE_ACTIVE) {
   1361 	    /* If we aren't parsing the data, treat as always false */
   1362 	    cond_state[cond_depth] = SKIP_TO_ELSE;
   1363 	    return COND_SKIP;
   1364 	}
   1365     }
   1366 
   1367     /* Initialize file-global variables for parsing the expression */
   1368     condDefProc = ifp->defProc;
   1369     condInvert = ifp->doNot;
   1370 
   1371     /* And evaluate the conditional expresssion */
   1372     if (Cond_EvalExpression(0, line, &value, 1) == COND_INVALID) {
   1373 	/* Syntax error in conditional, error message already output. */
   1374 	/* Skip everything to matching .endif */
   1375 	cond_state[cond_depth] = SKIP_TO_ELSE;
   1376 	return COND_SKIP;
   1377     }
   1378 
   1379     if (!value) {
   1380 	cond_state[cond_depth] = SEARCH_FOR_ELIF;
   1381 	return COND_SKIP;
   1382     }
   1383     cond_state[cond_depth] = IF_ACTIVE;
   1384     return COND_PARSE;
   1385 }
   1386 
   1387 
   1388 
   1389 /*-
   1391  *-----------------------------------------------------------------------
   1392  * Cond_End --
   1393  *	Make sure everything's clean at the end of a makefile.
   1394  *
   1395  * Results:
   1396  *	None.
   1397  *
   1398  * Side Effects:
   1399  *	Parse_Error will be called if open conditionals are around.
   1400  *
   1401  *-----------------------------------------------------------------------
   1402  */
   1403 void
   1404 Cond_restore_depth(unsigned int saved_depth)
   1405 {
   1406     int open_conds = cond_depth - cond_min_depth;
   1407 
   1408     if (open_conds != 0 || saved_depth > cond_depth) {
   1409 	Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds,
   1410 		    open_conds == 1 ? "" : "s");
   1411 	cond_depth = cond_min_depth;
   1412     }
   1413 
   1414     cond_min_depth = saved_depth;
   1415 }
   1416 
   1417 unsigned int
   1418 Cond_save_depth(void)
   1419 {
   1420     int depth = cond_min_depth;
   1421 
   1422     cond_min_depth = cond_depth;
   1423     return depth;
   1424 }
   1425