* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
* Copyright (c) 1988, 1989 by Adam de Boor
* Copyright (c) 1989 by Berkeley Softworks
* This code is derived from software contributed to Berkeley by
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)cond.c 5.6 (Berkeley) 6/1/90";
* Functions to handle conditionals in a makefile.
* Cond_Eval Evaluate the conditional in the passed line.
* The parsing of conditional expressions is based on this grammar:
* T -> $(varspec) op value
* T -> $(varspec) == "string"
* T -> $(varspec) != "string"
* op -> == | != | > | < | >= | <=
* 'symbol' is some other symbol to which the default function (condDefProc)
* Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
* will return And for '&' and '&&', Or for '|' and '||', Not for '!',
* LParen for '(', RParen for ')' and will evaluate the other terminal
* symbols, using either the default function or the function given in the
* terminal, and return the result as either True or False.
* All Non-Terminal functions (CondE, CondF and CondT) return Err on error.
And
, Or
, Not
, True
, False
, LParen
, RParen
, EndOfFile
, None
, Err
* Structures to handle elegantly the different forms of #if's. The
* last two fields are stored in condInvert and condDefProc, respectively.
static Boolean
CondDoDefined(),
char *form
; /* Form of if */
int formlen
; /* Length of form */
Boolean doNot
; /* TRUE if default function should be negated */
Boolean (*defProc
)(); /* Default function to apply */
"ifdef", 5, FALSE
, CondDoDefined
,
"ifndef", 6, TRUE
, CondDoDefined
,
"ifmake", 6, FALSE
, CondDoMake
,
"ifnmake", 7, TRUE
, CondDoMake
,
"if", 2, FALSE
, CondDoDefined
,
(char *)0, 0, FALSE
, (Boolean (*)())0,
static Boolean condInvert
; /* Invert the default function */
static Boolean (*condDefProc
)(); /* Default function to apply */
static char *condExpr
; /* The expression to parse */
static Token condPushBack
=None
; /* Single push-back token used in
#define MAXIF 30 /* greatest depth of #if'ing */
static Boolean condStack
[MAXIF
]; /* Stack of conditionals's values */
static int condTop
= MAXIF
; /* Top-most conditional */
static int skipIfLevel
=0; /* Depth of skipped conditionals */
static Boolean skipLine
= FALSE
; /* Whether the parse module is skipping
static Token
CondT(), CondF(), CondE();
*-----------------------------------------------------------------------
* Push back the most recent token read. We only need one level of
* this, so the thing is just stored in 'condPushback'.
* condPushback is overwritten.
*-----------------------------------------------------------------------
Token t
; /* Token to push back into the "stream" */
*-----------------------------------------------------------------------
* Find the argument of a built-in function.
* The length of the argument and the address of the argument.
* The pointer is set to point to the closing parenthesis of the
*-----------------------------------------------------------------------
CondGetArg (linePtr
, argPtr
, func
, parens
)
Boolean parens
; /* TRUE if arg should be bounded by parens */
while (*cp
!= '(' && *cp
!= '\0') {
* No arguments whatsoever. Because 'make' and 'defined' aren't really
* "reserved words", we don't print a message. I think this is better
* than hitting the user with a warning message every time s/he uses
* the word 'make' or 'defined' at the beginning of a symbol...
while (*cp
== ' ' || *cp
== '\t') {
* Create a buffer for the argument and start it out at 16 characters
while ((index(" \t)&|", *cp
) == (char *)NULL
) && (*cp
!= '\0')) {
* Parse the variable spec and install it as part of the argument
* if it's valid. We tell Var_Parse to complain on an undefined
* variable, so we don't do it too. Nor do we return an error,
* though perhaps we should...
cp2
= Var_Parse(cp
, VAR_CMD
, TRUE
, &len
, &doFree
);
Buf_AddBytes(buf
, strlen(cp2
), (Byte
*)cp2
);
Buf_AddByte(buf
, (Byte
)*cp
);
Buf_AddByte(buf
, (Byte
)'\0');
*argPtr
= (char *)Buf_GetAll(buf
, &argLen
);
while (*cp
== ' ' || *cp
== '\t') {
if (parens
&& *cp
!= ')') {
Parse_Error (PARSE_WARNING
, "Missing closing parenthesis for %s()",
* Advance pointer past close parenthesis.
*-----------------------------------------------------------------------
* Handle the 'defined' function for conditionals.
* TRUE if the given variable is defined.
*-----------------------------------------------------------------------
CondDoDefined (argLen
, arg
)
char savec
= arg
[argLen
];
if (Var_Value (arg
, VAR_CMD
) != (char *)NULL
) {
*-----------------------------------------------------------------------
* Front-end for Str_Match so it returns 0 on match and non-zero
* on mismatch. Callback function for CondDoMake via Lst_Find
* 0 if string matches pattern
*-----------------------------------------------------------------------
CondStrMatch(string
, pattern
)
return(!Str_Match(string
,pattern
));
*-----------------------------------------------------------------------
* Handle the 'make' function for conditionals.
* TRUE if the given target is being made.
*-----------------------------------------------------------------------
char savec
= arg
[argLen
];
if (Lst_Find (create
, (ClientData
)arg
, CondStrMatch
) == NILLNODE
) {
*-----------------------------------------------------------------------
* See if the given file exists.
* TRUE if the file exists and FALSE if it does not.
*-----------------------------------------------------------------------
CondDoExists (argLen
, arg
)
char savec
= arg
[argLen
];
path
= Dir_FindFile(arg
, dirSearchPath
);
if (path
!= (char *)NULL
) {
*-----------------------------------------------------------------------
* See if the given node exists and is an actual target.
* TRUE if the node exists as a target and FALSE if it does not.
*-----------------------------------------------------------------------
CondDoTarget (argLen
, arg
)
char savec
= arg
[argLen
];
gn
= Targ_FindNode(arg
, TARG_NOCREATE
);
if ((gn
!= NILGNODE
) && !OP_NOP(gn
->type
)) {
*-----------------------------------------------------------------------
* Convert the given number into a double. If the number begins
* with 0x, or just x, it is interpreted as a hexadecimal integer
* and converted to a double from there. All other strings just have
* The double value of string.
*-----------------------------------------------------------------------
} else if (*str
== '+') {
if (((*str
== '0') && (str
[1] == 'x')) ||
str
+= (*str
== 'x') ? 1 : 2;
} else if (*str
<= 'F') {
*-----------------------------------------------------------------------
* Return the next token from the input.
* A Token for the next lexical token in the stream.
* condPushback will be set back to None if it is used.
*-----------------------------------------------------------------------
if (condPushBack
== None
) {
while (*condExpr
== ' ' || *condExpr
== '\t') {
if (condExpr
[1] == '|') {
if (condExpr
[1] == '&') {
* Parse the variable spec and skip over it, saving its
lhs
= Var_Parse(condExpr
, VAR_CMD
, doEval
,&varSpecLen
,&doFree
);
* Even if !doEval, we still report syntax errors, which
* is what getting var_Error back with !doEval means.
* Skip whitespace to get to the operator
while (isspace(*condExpr
)) {
* Make sure the operator is a valid one. If it isn't a
* known relational operator, pretend we got a
if (condExpr
[1] == '=') {
while (isspace(*condExpr
)) {
Parse_Error(PARSE_WARNING
,
"Missing right-hand-side of operator");
* Doing a string comparison. Only allow == and != for
if (((*op
!= '!') && (*op
!= '=')) || (op
[1] != '=')) {
Parse_Error(PARSE_WARNING
,
"String comparison operator should be either == or !=");
for (cp
= rhs
+1; (*cp
!= '"') && (*cp
!= '\0'); cp
++) {
if ((*cp
== '\\') && (cp
[1] != '\0')) {
* Backslash escapes things -- skip over next
* character, if it exists.
Buf_AddByte(buf
, (Byte
)*cp
);
cp2
= Var_Parse(cp
, VAR_CMD
, doEval
,&len
, &freeIt
);
Buf_AddBytes(buf
, strlen(cp2
), (Byte
*)cp2
);
Buf_AddByte(buf
, (Byte
)*cp
);
Buf_AddByte(buf
, (Byte
)*cp
);
Buf_AddByte(buf
, (Byte
)0);
string
= (char *)Buf_GetAll(buf
, (int *)0);
printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
* Null-terminate rhs and perform the comparison.
* t is set to the result.
t
= strcmp(lhs
, string
) ? False
: True
;
t
= strcmp(lhs
, string
) ? True
: False
;
* rhs is either a float or an integer. Convert both the
* lhs and the rhs to a double and compare the two.
string
= Var_Parse(rhs
, VAR_CMD
, doEval
,&len
,&freeIt
);
if (string
== var_Error
) {
right
= CondCvtArg(string
);
* Skip over the right-hand side
while(!isspace(*condExpr
) && (*condExpr
!= '\0')) {
printf("left = %f, right = %f, op = %.2s\n", left
,
Parse_Error(PARSE_WARNING
,
t
= (left
!= right
? True
: False
);
Parse_Error(PARSE_WARNING
,
t
= (left
== right
? True
: False
);
t
= (left
<= right
? True
: False
);
t
= (left
< right
? True
: False
);
t
= (left
>= right
? True
: False
);
t
= (left
> right
? True
: False
);
if (strncmp (condExpr
, "defined", 7) == 0) {
* Use CondDoDefined to evaluate the argument and
* CondGetArg to extract the argument from the 'function
evalProc
= CondDoDefined
;
arglen
= CondGetArg (&condExpr
, &arg
, "defined", TRUE
);
} else if (strncmp (condExpr
, "make", 4) == 0) {
* Use CondDoMake to evaluate the argument and
* CondGetArg to extract the argument from the 'function
arglen
= CondGetArg (&condExpr
, &arg
, "make", TRUE
);
} else if (strncmp (condExpr
, "exists", 6) == 0) {
* Use CondDoExists to evaluate the argument and
* CondGetArg to extract the argument from the
arglen
= CondGetArg(&condExpr
, &arg
, "exists", TRUE
);
} else if (strncmp(condExpr
, "empty", 5) == 0) {
* Use Var_Parse to parse the spec in parens and return
* True if the resulting string is empty.
condExpr
[arglen
] != '(' && condExpr
[arglen
] != '\0';
if (condExpr
[arglen
] != '\0') {
val
= Var_Parse(&condExpr
[arglen
- 1], VAR_CMD
,
doEval
, &length
, &doFree
);
t
= (*val
== '\0') ? True
: False
;
* Advance condExpr to beyond the closing ). Note that
* we subtract one from arglen + length b/c length
* is calculated from condExpr[arglen - 1].
condExpr
+= arglen
+ length
- 1;
} else if (strncmp (condExpr
, "target", 6) == 0) {
* Use CondDoTarget to evaluate the argument and
* CondGetArg to extract the argument from the
arglen
= CondGetArg(&condExpr
, &arg
, "target", TRUE
);
* The symbol is itself the argument to the default
* function. We advance condExpr to the end of the symbol
* by hand (the next whitespace, closing paren or
* binary operator) and set to invert the evaluation
* function if condInvert is TRUE.
arglen
= CondGetArg(&condExpr
, &arg
, "", FALSE
);
* Evaluate the argument using the set function. If invert
* is TRUE, we invert the sense of the function.
t
= (!doEval
|| (* evalProc
) (arglen
, arg
) ?
(invert
? False
: True
) :
(invert
? True
: False
));
*-----------------------------------------------------------------------
* Parse a single term in the expression. This consists of a terminal
* symbol or Not and a terminal symbol (not including the binary
* T -> defined(variable) | make(target) | exists(file) | symbol
*-----------------------------------------------------------------------
* If we reached the end of the expression, the expression
} else if (t
== LParen
) {
if (CondToken(doEval
) != RParen
) {
*-----------------------------------------------------------------------
* Parse a conjunctive factor (nice name, wot?)
*-----------------------------------------------------------------------
* If T is False, the whole thing will be False, but we have to
* parse the r.h.s. anyway (to throw it away).
* If T is True, the result is the r.h.s., be it an Err or no.
*-----------------------------------------------------------------------
* Main expression production.
* Tokens are, of course, consumed.
*-----------------------------------------------------------------------
* A similar thing occurs for ||, except that here we make sure
* the l.h.s. is False before we bother to evaluate the r.h.s.
* Once again, if l is False, the result is the r.h.s. and once
* again if l is True, we parse the r.h.s. to throw it away.
*-----------------------------------------------------------------------
* Evaluate the conditional in the passed line. The line
* where <cond-type> is any of if, ifmake, ifnmake, ifdef,
* ifndef, elif, elifmake, elifnmake, elifdef, elifndef
* and <expr> consists of &&, ||, !, make(target), defined(variable)
* and parenthetical groupings thereof.
* COND_PARSE if should parse lines after the conditional
* COND_SKIP if should skip lines after the conditional
* COND_INVALID if not a valid conditional.
*-----------------------------------------------------------------------
char *line
; /* Line to parse */
int level
; /* Level at which to report errors. */
for (line
++; *line
== ' ' || *line
== '\t'; line
++) {
* Find what type of if we're dealing with. The result is left
* in ifp and isElse is set TRUE if it's an elif line.
if (line
[0] == 'e' && line
[1] == 'l') {
} else if (strncmp (line
, "endif", 5) == 0) {
* End of a conditional section. If skipIfLevel is non-zero, that
* conditional was skipped, so lines following it should also be
* skipped. Hence, we return COND_SKIP. Otherwise, the conditional
* was read so succeeding lines should be parsed (think about it...)
* so we return COND_PARSE, unless this endif isn't paired with
Parse_Error (level
, "if-less endif");
* Figure out what sort of conditional it is -- what its default
* function is, etc. -- by looking in the table of valid "ifs"
for (ifp
= ifs
; ifp
->form
!= (char *)0; ifp
++) {
if (strncmp (ifp
->form
, line
, ifp
->formlen
) == 0) {
if (ifp
->form
== (char *) 0) {
* Nothing fit. If the first word on the line is actually
* "else", it's a valid conditional whose value is the inverse
* of the previous if we parsed.
if (isElse
&& (line
[0] == 's') && (line
[1] == 'e')) {
Parse_Error (level
, "if-less else");
} else if (skipIfLevel
== 0) {
value
= !condStack
[condTop
];
* Not a valid conditional type. No error...
Parse_Error (level
, "if-less elif");
} else if (skipIfLevel
!= 0) {
* If skipping this conditional, just ignore the whole thing.
* If we don't, the user might be employing a variable that's
* undefined, for which there's an enclosing ifdef that
* Don't even try to evaluate a conditional that's not an else if
* we're skipping things...
* Initialize file-global variables for parsing
condDefProc
= ifp
->defProc
;
while (*line
== ' ' || *line
== '\t') {
if (CondToken(TRUE
) == EndOfFile
) {
if (CondToken(TRUE
) == EndOfFile
) {
Parse_Error (level
, "Malformed conditional (%s)",
} else if ((skipIfLevel
!= 0) || condStack
[condTop
]) {
* If this is an else-type conditional, it should only take effect
* if its corresponding if was evaluated and FALSE. If its if was
* TRUE or skipped, we return COND_SKIP (and start skipping in case
* we weren't already), leaving the stack unmolested so later elif's
* This is the one case where we can definitely proclaim a fatal
* error. If we don't, we're hosed.
Parse_Error (PARSE_FATAL
, "Too many nested if's. %d max.", MAXIF
);
condStack
[condTop
] = value
;
return (value
? COND_PARSE
: COND_SKIP
);
*-----------------------------------------------------------------------
* Make sure everything's clean at the end of a makefile.
* Parse_Error will be called if open conditionals are around.
*-----------------------------------------------------------------------
Parse_Error(PARSE_FATAL
, "%d open conditional%s", MAXIF
-condTop
,
MAXIF
-condTop
== 1 ? "" : "s");