version 1.24, 2001/05/03 13:41:02 |
version 1.25, 2001/05/23 12:34:41 |
|
|
* SUCH DAMAGE. |
* SUCH DAMAGE. |
*/ |
*/ |
|
|
/*- |
#include <ctype.h> |
* cond.c -- |
#include <stddef.h> |
* Functions to handle conditionals in a makefile. |
#include <stdio.h> |
* |
#include <string.h> |
* Interface: |
#include "config.h" |
* Cond_Eval Evaluate the conditional in the passed line. |
#include "defines.h" |
* |
#include "dir.h" |
*/ |
#include "buf.h" |
|
#include "cond.h" |
|
#include "error.h" |
|
#include "var.h" |
|
#include "varname.h" |
|
#include "targ.h" |
|
#include "lowparse.h" |
|
#include "str.h" |
|
#include "main.h" |
|
#include "gnode.h" |
|
#include "lst.h" |
|
|
#include <ctype.h> |
|
#include <math.h> |
|
#include <stddef.h> |
|
#include "make.h" |
|
#include "ohash.h" |
|
#include "dir.h" |
|
#include "buf.h" |
|
|
|
#ifndef lint |
|
#if 0 |
|
static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94"; |
|
#else |
|
UNUSED |
|
static char rcsid[] = "$OpenBSD$"; |
|
#endif |
|
#endif /* not lint */ |
|
|
|
|
|
/* The parsing of conditional expressions is based on this grammar: |
/* The parsing of conditional expressions is based on this grammar: |
* E -> F || E |
* E -> F || E |
* E -> F |
* E -> F |
|
|
* will return And for '&' and '&&', Or for '|' and '||', Not for '!', |
* will return And for '&' and '&&', Or for '|' and '||', Not for '!', |
* LParen for '(', RParen for ')' and will evaluate the other terminal |
* LParen for '(', RParen for ')' and will evaluate the other terminal |
* symbols, using either the default function or the function given in the |
* symbols, using either the default function or the function given in the |
* terminal, and return the result as either True or False. |
* terminal, and return the result as either true or False. |
* |
* |
* All Non-Terminal functions (CondE, CondF and CondT) return Err on error. */ |
* All Non-Terminal functions (CondE, CondF and CondT) return Err on error. */ |
typedef enum { |
typedef enum { |
|
|
* Structures to handle elegantly the different forms of #if's. The |
* Structures to handle elegantly the different forms of #if's. The |
* last two fields are stored in condInvert and condDefProc, respectively. |
* last two fields are stored in condInvert and condDefProc, respectively. |
*/ |
*/ |
static Boolean CondGetArg(const char **, struct Name *, |
static bool CondGetArg(const char **, struct Name *, |
const char *, Boolean); |
const char *, bool); |
static Boolean CondDoDefined(struct Name *); |
static bool CondDoDefined(struct Name *); |
static Boolean CondDoMake(struct Name *); |
static bool CondDoMake(struct Name *); |
static Boolean CondDoExists(struct Name *); |
static bool CondDoExists(struct Name *); |
static Boolean CondDoTarget(struct Name *); |
static bool CondDoTarget(struct Name *); |
static Boolean CondCvtArg(const char *, double *); |
static bool CondCvtArg(const char *, double *); |
static Token CondToken(Boolean); |
static Token CondToken(bool); |
static Token CondT(Boolean); |
static Token CondT(bool); |
static Token CondF(Boolean); |
static Token CondF(bool); |
static Token CondE(Boolean); |
static Token CondE(bool); |
static Token CondHandleVarSpec(Boolean); |
static Token CondHandleVarSpec(bool); |
static Token CondHandleDefault(Boolean); |
static Token CondHandleDefault(bool); |
static const char *find_cond(const char *); |
static const char *find_cond(const char *); |
|
|
|
|
static struct If { |
static struct If { |
char *form; /* Form of if */ |
char *form; /* Form of if */ |
int formlen; /* Length of form */ |
int formlen; /* Length of form */ |
Boolean doNot; /* TRUE if default function should be negated */ |
bool doNot; /* true if default function should be negated */ |
Boolean (*defProc)(struct Name *); |
bool (*defProc)(struct Name *); |
/* Default function to apply */ |
/* Default function to apply */ |
} ifs[] = { |
} ifs[] = { |
{ "ifdef", 5, FALSE, CondDoDefined }, |
{ "ifdef", 5, false, CondDoDefined }, |
{ "ifndef", 6, TRUE, CondDoDefined }, |
{ "ifndef", 6, true, CondDoDefined }, |
{ "ifmake", 6, FALSE, CondDoMake }, |
{ "ifmake", 6, false, CondDoMake }, |
{ "ifnmake", 7, TRUE, CondDoMake }, |
{ "ifnmake", 7, true, CondDoMake }, |
{ "if", 2, FALSE, CondDoDefined }, |
{ "if", 2, false, CondDoDefined }, |
{ NULL, 0, FALSE, NULL } |
{ NULL, 0, false, NULL } |
}; |
}; |
|
|
static Boolean condInvert; /* Invert the default function */ |
static bool condInvert; /* Invert the default function */ |
static Boolean (*condDefProc) /* Default function to apply */ |
static bool (*condDefProc) /* Default function to apply */ |
(struct Name *); |
(struct Name *); |
static const char *condExpr; /* The expression to parse */ |
static const char *condExpr; /* The expression to parse */ |
static Token condPushBack=None; /* Single push-back token used in |
static Token condPushBack=None; /* Single push-back token used in |
|
|
#define MAXIF 30 /* greatest depth of #if'ing */ |
#define MAXIF 30 /* greatest depth of #if'ing */ |
|
|
static struct { |
static struct { |
Boolean value; |
bool value; |
unsigned long lineno; |
unsigned long lineno; |
const char *filename; |
const char *filename; |
} condStack[MAXIF]; /* Stack of conditionals */ |
} condStack[MAXIF]; /* Stack of conditionals */ |
static int condTop = MAXIF; /* Top-most conditional */ |
static int condTop = MAXIF; /* Top-most conditional */ |
static int skipIfLevel=0; /* Depth of skipped conditionals */ |
static int skipIfLevel=0; /* Depth of skipped conditionals */ |
static Boolean skipLine = FALSE; /* Whether the parse module is skipping |
static bool skipLine = false; /* Whether the parse module is skipping |
* lines */ |
* lines */ |
|
|
static const char * |
static const char * |
|
|
* Find the argument of a built-in function. |
* Find the argument of a built-in function. |
* |
* |
* Results: |
* Results: |
* TRUE if evaluation went okay |
* true if evaluation went okay |
* |
* |
* Side Effects: |
* Side Effects: |
* The line pointer is set to point to the closing parenthesis of the |
* The line pointer is set to point to the closing parenthesis of the |
* function call. The argument is filled. |
* function call. The argument is filled. |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
static bool |
CondGetArg(linePtr, arg, func, parens) |
CondGetArg(linePtr, arg, func, parens) |
const char **linePtr; |
const char **linePtr; |
struct Name *arg; |
struct Name *arg; |
const char *func; |
const char *func; |
Boolean parens; /* TRUE if arg should be bounded by parens */ |
bool parens; /* true if arg should be bounded by parens */ |
{ |
{ |
const char *cp; |
const char *cp; |
|
|
|
|
* the word 'make' or 'defined' at the beginning of a symbol... */ |
* the word 'make' or 'defined' at the beginning of a symbol... */ |
arg->s = cp; |
arg->s = cp; |
arg->e = cp; |
arg->e = cp; |
arg->tofree = FALSE; |
arg->tofree = false; |
return FALSE; |
return false; |
} |
} |
|
|
while (*cp == ' ' || *cp == '\t') |
while (*cp == ' ' || *cp == '\t') |
cp++; |
cp++; |
|
|
|
|
cp = Var_Name_Get(cp, arg, NULL, TRUE, find_cond); |
cp = VarName_Get(cp, arg, NULL, true, find_cond); |
|
|
while (*cp == ' ' || *cp == '\t') |
while (*cp == ' ' || *cp == '\t') |
cp++; |
cp++; |
if (parens && *cp != ')') { |
if (parens && *cp != ')') { |
Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", |
Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", |
func); |
func); |
return FALSE; |
return false; |
} else if (parens) |
} else if (parens) |
/* Advance pointer past close parenthesis. */ |
/* Advance pointer past close parenthesis. */ |
cp++; |
cp++; |
|
|
*linePtr = cp; |
*linePtr = cp; |
return TRUE; |
return true; |
} |
} |
|
|
/*- |
/*- |
|
|
* Handle the 'defined' function for conditionals. |
* Handle the 'defined' function for conditionals. |
* |
* |
* Results: |
* Results: |
* TRUE if the given variable is defined. |
* true if the given variable is defined. |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
static bool |
CondDoDefined(arg) |
CondDoDefined(arg) |
struct Name *arg; |
struct Name *arg; |
{ |
{ |
if (Var_Value_interval(arg->s, arg->e) != NULL) |
if (Var_Valuei(arg->s, arg->e) != NULL) |
return TRUE; |
return true; |
else |
else |
return FALSE; |
return false; |
} |
} |
|
|
/*- |
/*- |
|
|
* Handle the 'make' function for conditionals. |
* Handle the 'make' function for conditionals. |
* |
* |
* Results: |
* Results: |
* TRUE if the given target is being made. |
* true if the given target is being made. |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
static bool |
CondDoMake(arg) |
CondDoMake(arg) |
struct Name *arg; |
struct Name *arg; |
{ |
{ |
LstNode ln; |
LstNode ln; |
|
|
for (ln = Lst_First(&create); ln != NULL; ln = Lst_Adv(ln)) { |
for (ln = Lst_First(create); ln != NULL; ln = Lst_Adv(ln)) { |
if (Str_Matchi((char *)Lst_Datum(ln), arg->s, arg->e)) |
char *s = (char *)Lst_Datum(ln); |
return TRUE; |
if (Str_Matchi(s, strchr(s, '\0'), arg->s, arg->e)) |
|
return true; |
} |
} |
|
|
return FALSE; |
return false; |
} |
} |
|
|
/*- |
/*- |
|
|
* See if the given file exists. |
* See if the given file exists. |
* |
* |
* Results: |
* Results: |
* TRUE if the file exists and FALSE if it does not. |
* true if the file exists and false if it does not. |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
static bool |
CondDoExists(arg) |
CondDoExists(arg) |
struct Name *arg; |
struct Name *arg; |
{ |
{ |
Boolean result; |
bool result; |
char *path; |
char *path; |
|
|
path = Dir_FindFilei(arg->s, arg->e, &dirSearchPath); |
path = Dir_FindFilei(arg->s, arg->e, dirSearchPath); |
if (path != NULL) { |
if (path != NULL) { |
result = TRUE; |
result = true; |
free(path); |
free(path); |
} else { |
} else { |
result = FALSE; |
result = false; |
} |
} |
return result; |
return result; |
} |
} |
|
|
* See if the given node exists and is an actual target. |
* See if the given node exists and is an actual target. |
* |
* |
* Results: |
* Results: |
* TRUE if the node exists as a target and FALSE if it does not. |
* true if the node exists as a target and false if it does not. |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
static bool |
CondDoTarget(arg) |
CondDoTarget(arg) |
struct Name *arg; |
struct Name *arg; |
{ |
{ |
GNode *gn; |
GNode *gn; |
|
|
gn = Targ_FindNode(arg->s, arg->e, TARG_NOCREATE); |
gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE); |
if (gn != NULL && !OP_NOP(gn->type)) |
if (gn != NULL && !OP_NOP(gn->type)) |
return TRUE; |
return true; |
else |
else |
return FALSE; |
return false; |
} |
} |
|
|
|
|
|
|
* Can change 'value' even if string is not a valid number. |
* Can change 'value' even if string is not a valid number. |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
static Boolean |
static bool |
CondCvtArg(str, value) |
CondCvtArg(str, value) |
const char *str; |
const char *str; |
double *value; |
double *value; |
|
|
else if (isxdigit(*str)) |
else if (isxdigit(*str)) |
x = 10 + *str - isupper(*str) ? 'A' : 'a'; |
x = 10 + *str - isupper(*str) ? 'A' : 'a'; |
else |
else |
return FALSE; |
return false; |
i = (i << 4) + x; |
i = (i << 4) + x; |
} |
} |
*value = (double) i; |
*value = (double) i; |
return TRUE; |
return true; |
} |
} |
else { |
else { |
char *eptr; |
char *eptr; |
|
|
|
|
static Token |
static Token |
CondHandleVarSpec(doEval) |
CondHandleVarSpec(doEval) |
Boolean doEval; |
bool doEval; |
{ |
{ |
Token t; |
Token t; |
char *lhs; |
char *lhs; |
const char *rhs; |
const char *rhs; |
const char *op; |
const char *op; |
size_t varSpecLen; |
size_t varSpecLen; |
Boolean doFree; |
bool doFree; |
|
|
/* Parse the variable spec and skip over it, saving its |
/* Parse the variable spec and skip over it, saving its |
* value in lhs. */ |
* value in lhs. */ |
|
|
|
|
lhs = Buf_Retrieve(&buf); |
lhs = Buf_Retrieve(&buf); |
|
|
doFree = TRUE; |
doFree = true; |
} |
} |
|
|
/* Skip whitespace to get to the operator. */ |
/* Skip whitespace to get to the operator. */ |
|
|
if (*cp == '$') { |
if (*cp == '$') { |
size_t len; |
size_t len; |
|
|
if (Var_ParseBuffer(&buf, cp, NULL, doEval, &len) |
if (Var_ParseBuffer(&buf, cp, NULL, doEval, &len)) { |
== SUCCESS) { |
|
cp += len; |
cp += len; |
continue; |
continue; |
} |
} |
|
|
goto do_string_compare; |
goto do_string_compare; |
if (*rhs == '$') { |
if (*rhs == '$') { |
size_t len; |
size_t len; |
Boolean freeIt; |
bool freeIt; |
|
|
string = Var_Parse(rhs, NULL, doEval,&len,&freeIt); |
string = Var_Parse(rhs, NULL, doEval,&len,&freeIt); |
if (string == var_Error) |
if (string == var_Error) |
|
|
static struct operator { |
static struct operator { |
const char *s; |
const char *s; |
size_t len; |
size_t len; |
Boolean (*proc)(struct Name *); |
bool (*proc)(struct Name *); |
} ops[] = { |
} ops[] = { |
{S("defined"), CondDoDefined}, |
{S("defined"), CondDoDefined}, |
{S("make"), CondDoMake}, |
{S("make"), CondDoMake}, |
|
|
}; |
}; |
static Token |
static Token |
CondHandleDefault(doEval) |
CondHandleDefault(doEval) |
Boolean doEval; |
bool doEval; |
{ |
{ |
Boolean t; |
bool t; |
Boolean (*evalProc)(struct Name *); |
bool (*evalProc)(struct Name *); |
Boolean invert = FALSE; |
bool invert = false; |
struct Name arg; |
struct Name arg; |
size_t arglen; |
size_t arglen; |
|
|
|
|
/* Use Var_Parse to parse the spec in parens and return |
/* Use Var_Parse to parse the spec in parens and return |
* True if the resulting string is empty. */ |
* True if the resulting string is empty. */ |
size_t length; |
size_t length; |
Boolean doFree; |
bool doFree; |
char *val; |
char *val; |
|
|
condExpr += 5; |
condExpr += 5; |
|
|
for (op = ops; op != NULL; op++) |
for (op = ops; op != NULL; op++) |
if (strncmp(condExpr, op->s, op->len) == 0) { |
if (strncmp(condExpr, op->s, op->len) == 0) { |
condExpr += op->len; |
condExpr += op->len; |
if (CondGetArg(&condExpr, &arg, op->s, TRUE)) |
if (CondGetArg(&condExpr, &arg, op->s, true)) |
evalProc = op->proc; |
evalProc = op->proc; |
else |
else |
condExpr -= op->len; |
condExpr -= op->len; |
|
|
* function. We advance condExpr to the end of the symbol |
* function. We advance condExpr to the end of the symbol |
* by hand (the next whitespace, closing paren or |
* by hand (the next whitespace, closing paren or |
* binary operator) and set to invert the evaluation |
* binary operator) and set to invert the evaluation |
* function if condInvert is TRUE. */ |
* function if condInvert is true. */ |
invert = condInvert; |
invert = condInvert; |
evalProc = condDefProc; |
evalProc = condDefProc; |
/* XXX should we ignore problems now ? */ |
/* XXX should we ignore problems now ? */ |
CondGetArg(&condExpr, &arg, "", FALSE); |
CondGetArg(&condExpr, &arg, "", false); |
} |
} |
|
|
/* Evaluate the argument using the set function. If invert |
/* Evaluate the argument using the set function. If invert |
* is TRUE, we invert the sense of the function. */ |
* is true, we invert the sense of the function. */ |
t = (!doEval || (*evalProc)(&arg) ? |
t = (!doEval || (*evalProc)(&arg) ? |
(invert ? False : True) : |
(invert ? False : True) : |
(invert ? True : False)); |
(invert ? True : False)); |
Var_Name_Free(&arg); |
VarName_Free(&arg); |
return t; |
return t; |
} |
} |
|
|
|
|
*/ |
*/ |
static Token |
static Token |
CondToken(doEval) |
CondToken(doEval) |
Boolean doEval; |
bool doEval; |
{ |
{ |
|
|
if (condPushBack != None) { |
if (condPushBack != None) { |
|
|
*/ |
*/ |
static Token |
static Token |
CondT(doEval) |
CondT(doEval) |
Boolean doEval; |
bool doEval; |
{ |
{ |
Token t; |
Token t; |
|
|
|
|
*/ |
*/ |
static Token |
static Token |
CondF(doEval) |
CondF(doEval) |
Boolean doEval; |
bool doEval; |
{ |
{ |
Token l, o; |
Token l, o; |
|
|
|
|
if (l == True) |
if (l == True) |
l = CondF(doEval); |
l = CondF(doEval); |
else |
else |
(void)CondF(FALSE); |
(void)CondF(false); |
} else |
} else |
/* F -> T. */ |
/* F -> T. */ |
condPushBack = o; |
condPushBack = o; |
|
|
*/ |
*/ |
static Token |
static Token |
CondE(doEval) |
CondE(doEval) |
Boolean doEval; |
bool doEval; |
{ |
{ |
Token l, o; |
Token l, o; |
|
|
|
|
if (l == False) |
if (l == False) |
l = CondE(doEval); |
l = CondE(doEval); |
else |
else |
(void)CondE(FALSE); |
(void)CondE(false); |
} else |
} else |
/* E -> F. */ |
/* E -> F. */ |
condPushBack = o; |
condPushBack = o; |
|
|
return l; |
return l; |
} |
} |
|
|
/*- |
/* A conditional line looks like this: |
*----------------------------------------------------------------------- |
|
* Cond_Eval -- |
|
* Evaluate the conditional in the passed fragment. The fragment |
|
* looks like this: |
|
* <cond-type> <expr> |
* <cond-type> <expr> |
* where <cond-type> is any of if, ifmake, ifnmake, ifdef, |
* where <cond-type> is any of if, ifmake, ifnmake, ifdef, |
* ifndef, elif, elifmake, elifnmake, elifdef, elifndef |
* ifndef, elif, elifmake, elifnmake, elifdef, elifndef |
* and <expr> consists of &&, ||, !, make(target), defined(variable) |
* and <expr> consists of &&, ||, !, make(target), defined(variable) |
* and parenthetical groupings thereof. |
* and parenthetical groupings thereof. |
* |
|
* Results: |
|
* 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. |
|
*----------------------------------------------------------------------- |
|
*/ |
*/ |
int |
int |
Cond_Eval(line) |
Cond_Eval(line) |
char *line; /* Line to parse */ |
const char *line; /* Line to parse */ |
{ |
{ |
struct If *ifp; |
struct If *ifp; |
Boolean isElse; |
bool isElse; |
Boolean value = FALSE; |
bool value = false; |
int level; /* Level at which to report errors. */ |
int level; /* Level at which to report errors. */ |
|
|
level = PARSE_FATAL; |
level = PARSE_FATAL; |
|
|
* otherwise, this is not our turf. */ |
* otherwise, this is not our turf. */ |
|
|
/* Find what type of if we're dealing with. The result is left |
/* 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. */ |
* in ifp and isElse is set true if it's an elif line. */ |
if (line[0] == 'e' && line[1] == 'l') { |
if (line[0] == 'e' && line[1] == 'l') { |
line += 2; |
line += 2; |
isElse = TRUE; |
isElse = true; |
} else if (strncmp(line, "endif", 5) == 0) { |
} else if (strncmp(line, "endif", 5) == 0) { |
/* End of a conditional section. If skipIfLevel is non-zero, that |
/* End of a conditional section. If skipIfLevel is non-zero, that |
* conditional was skipped, so lines following it should also be |
* conditional was skipped, so lines following it should also be |
|
|
Parse_Error(level, "if-less endif"); |
Parse_Error(level, "if-less endif"); |
return COND_INVALID; |
return COND_INVALID; |
} else { |
} else { |
skipLine = FALSE; |
skipLine = false; |
condTop += 1; |
condTop += 1; |
return COND_PARSE; |
return COND_PARSE; |
} |
} |
} |
} |
} else |
} else |
isElse = FALSE; |
isElse = false; |
|
|
/* Figure out what sort of conditional it is -- what its default |
/* Figure out what sort of conditional it is -- what its default |
* function is, etc. -- by looking in the table of valid "ifs" */ |
* function is, etc. -- by looking in the table of valid "ifs" */ |
|
|
condExpr = line; |
condExpr = line; |
condPushBack = None; |
condPushBack = None; |
|
|
switch (CondE(TRUE)) { |
switch (CondE(true)) { |
case True: |
case True: |
if (CondToken(TRUE) == EndOfFile) { |
if (CondToken(true) == EndOfFile) { |
value = TRUE; |
value = true; |
break; |
break; |
} |
} |
goto err; |
goto err; |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
case False: |
case False: |
if (CondToken(TRUE) == EndOfFile) { |
if (CondToken(true) == EndOfFile) { |
value = FALSE; |
value = false; |
break; |
break; |
} |
} |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
|
|
condTop -= 1; |
condTop -= 1; |
else if (skipIfLevel != 0 || condStack[condTop].value) { |
else if (skipIfLevel != 0 || condStack[condTop].value) { |
/* If this is an else-type conditional, it should only take effect |
/* If this is an else-type conditional, it should only take effect |
* if its corresponding if was evaluated and FALSE. If its if was |
* if its corresponding if was evaluated and false. If its if was |
* TRUE or skipped, we return COND_SKIP (and start skipping in case |
* true or skipped, we return COND_SKIP (and start skipping in case |
* we weren't already), leaving the stack unmolested so later elif's |
* we weren't already), leaving the stack unmolested so later elif's |
* don't screw up... */ |
* don't screw up... */ |
skipLine = TRUE; |
skipLine = true; |
return COND_SKIP; |
return COND_SKIP; |
} |
} |
|
|
|
|
} |
} |
} |
} |
|
|
/*- |
|
*----------------------------------------------------------------------- |
|
* Cond_End -- |
|
* Make sure everything's clean at the end of a makefile. |
|
* |
|
* Side Effects: |
|
* Parse_Error will be called if open conditionals are around. |
|
*----------------------------------------------------------------------- |
|
*/ |
|
void |
void |
Cond_End() |
Cond_End() |
{ |
{ |