File: [local] / src / usr.bin / make / cond.c (download)
Revision 1.52, Wed Jun 21 00:11:36 2017 UTC (6 years, 11 months ago) by espie
Branch: MAIN
CVS Tags: OPENBSD_6_2_BASE, OPENBSD_6_2 Changes since 1.51: +4 -1 lines
error out if an exists condition tests an empty path, because that makes
zero sense.
this would have caught DEPENDSFILE in gnu/cc/cc_tools
okay guenther@, also tested by krw@
|
/* $OpenBSD: cond.c,v 1.52 2017/06/21 00:11:36 espie Exp $ */
/* $NetBSD: cond.c,v 1.7 1996/11/06 17:59:02 christos Exp $ */
/*
* 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
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Adam de Boor.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 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. 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
* SUCH DAMAGE.
*/
#include <ctype.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ohash.h>
#include "config.h"
#include "defines.h"
#include "dir.h"
#include "buf.h"
#include "cond.h"
#include "cond_int.h"
#include "condhashconsts.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"
/* The parsing of conditional expressions is based on this grammar:
* E -> F || E
* E -> F
* F -> T && F
* F -> T
* T -> defined(variable)
* T -> make(target)
* T -> exists(file)
* T -> empty(varspec)
* T -> target(name)
* T -> commands(name)
* T -> symbol
* T -> $(varspec) op value
* T -> $(varspec) == "string"
* T -> $(varspec) != "string"
* T -> "string" == "string"
* T -> "string" != "string"
* T -> number op number
* T -> ( E )
* T -> ! T
* op -> == | != | > | < | >= | <=
*
* 'symbol' is some other symbol to which the default function (condDefProc)
* is applied.
*
* 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. */
typedef enum {
False = 0, True = 1, And, Or, Not, LParen, RParen, EndOfFile, None, Err
} Token;
/*-
* Structures to handle elegantly the different forms of #if's. The
* last two fields are stored in condInvert and condDefProc, respectively.
*/
static bool CondGetArg(const char **, struct Name *,
const char *, bool);
static bool CondDoDefined(struct Name *);
static bool CondDoMake(struct Name *);
static bool CondDoExists(struct Name *);
static bool CondDoTarget(struct Name *);
static bool CondDoTargetWithCommands(struct Name *);
static bool CondCvtArg(const char *, double *);
static Token CondToken(bool);
static Token CondT(bool);
static Token CondF(bool);
static Token CondE(bool);
static Token CondHandleVarSpec(bool);
static Token CondHandleDefault(bool);
static Token CondHandleComparison(char *, bool, bool);
static Token CondHandleString(bool);
static Token CondHandleNumber(bool);
static const char *find_cond(const char *);
struct If {
bool isElse; /* true for else forms */
bool doNot; /* true for embedded negation */
bool (*defProc)(struct Name *); /* function to apply */
};
static struct If ifs[] = {
{ false,false, CondDoDefined }, /* if, ifdef */
{ false,true, CondDoDefined }, /* ifndef */
{ false,false, CondDoMake }, /* ifmake */
{ false,true, CondDoMake }, /* ifnmake */
{ true, false, CondDoDefined }, /* elif, elifdef */
{ true, true, CondDoDefined }, /* elifndef */
{ true, false, CondDoMake }, /* elifmake */
{ true, true, CondDoMake }, /* elifnmake */
{ true, false, NULL }
};
#define COND_IF_INDEX 0
#define COND_IFDEF_INDEX 0
#define COND_IFNDEF_INDEX 1
#define COND_IFMAKE_INDEX 2
#define COND_IFNMAKE_INDEX 3
#define COND_ELIF_INDEX 4
#define COND_ELIFDEF_INDEX 4
#define COND_ELIFNDEF_INDEX 5
#define COND_ELIFMAKE_INDEX 6
#define COND_ELIFNMAKE_INDEX 7
#define COND_ELSE_INDEX 8
static bool condInvert; /* Invert the default function */
static bool (*condDefProc)(struct Name *);
/* Default function to apply */
static const char *condExpr; /* The expression to parse */
static Token condPushBack=None; /* Single push-back token used in parsing */
#define MAXIF 30 /* greatest depth of #if'ing */
static struct {
bool value;
Location origin;
} condStack[MAXIF]; /* Stack of conditionals */
static int condTop = MAXIF; /* Top-most conditional */
static int skipIfLevel=0; /* Depth of skipped conditionals */
static bool skipLine = false; /* Whether the parse module is skipping lines */
static const char *
find_cond(const char *p)
{
for (;;p++) {
/* XXX: when *p == '\0', strchr() returns !NULL */
if (strchr(" \t)&|$", *p) != NULL)
return p;
}
}
/*-
*-----------------------------------------------------------------------
* CondGetArg --
* Find the argument of a built-in function.
*
* Results:
* true if evaluation went okay
*
* Side Effects:
* The line pointer is set to point to the closing parenthesis of the
* function call. The argument is filled.
*-----------------------------------------------------------------------
*/
static bool
CondGetArg(const char **linePtr, struct Name *arg, const char *func,
bool parens) /* true if arg should be bounded by parens */
{
const char *cp;
cp = *linePtr;
/* Set things up to return faster in case of problem */
arg->s = cp;
arg->e = cp;
arg->tofree = false;
/* make and defined are not really keywords, so if CondGetArg doesn't
* work...
*/
if (parens) {
while (ISSPACE(*cp))
cp++;
if (*cp == '(')
cp++;
else
return false;
}
if (*cp == '\0')
return false;
while (ISSPACE(*cp))
cp++;
cp = VarName_Get(cp, arg, NULL, true, find_cond);
while (ISSPACE(*cp))
cp++;
if (parens) {
if (*cp == ')')
cp++;
else {
Parse_Error(PARSE_WARNING,
"Missing closing parenthesis for %s()", func);
return false;
}
}
*linePtr = cp;
return true;
}
/*-
*-----------------------------------------------------------------------
* CondDoDefined --
* Handle the 'defined' function for conditionals.
*
* Results:
* true if the given variable is defined.
*-----------------------------------------------------------------------
*/
static bool
CondDoDefined(struct Name *arg)
{
return Var_Definedi(arg->s, arg->e);
}
/*-
*-----------------------------------------------------------------------
* CondDoMake --
* Handle the 'make' function for conditionals.
*
* Results:
* true if the given target is being made.
*-----------------------------------------------------------------------
*/
static bool
CondDoMake(struct Name *arg)
{
LstNode ln;
for (ln = Lst_First(create); ln != NULL; ln = Lst_Adv(ln)) {
char *s = Lst_Datum(ln);
if (Str_Matchi(s, strchr(s, '\0'), arg->s, arg->e))
return true;
}
return false;
}
/*-
*-----------------------------------------------------------------------
* CondDoExists --
* See if the given file exists.
*
* Results:
* true if the file exists and false if it does not.
*-----------------------------------------------------------------------
*/
static bool
CondDoExists(struct Name *arg)
{
bool result;
char *path;
if (arg->s == arg->e)
Parse_Error(PARSE_FATAL, "Empty file name in .if exists()");
path = Dir_FindFilei(arg->s, arg->e, defaultPath);
if (path != NULL) {
result = true;
free(path);
} else {
result = false;
}
return result;
}
/*-
*-----------------------------------------------------------------------
* CondDoTarget --
* See if the given node exists and is an actual target.
*
* Results:
* true if the node exists as a target and false if it does not.
*-----------------------------------------------------------------------
*/
static bool
CondDoTarget(struct Name *arg)
{
GNode *gn;
gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE);
if (gn != NULL && !OP_NOP(gn->type))
return true;
else
return false;
}
/*-
*-----------------------------------------------------------------------
* CondDoTargetWithCommands --
* See if the given node exists and has commands.
*
* Results:
* true if the node is complete and false if it does not.
*-----------------------------------------------------------------------
*/
static bool
CondDoTargetWithCommands(struct Name *arg)
{
GNode *gn;
gn = Targ_FindNodei(arg->s, arg->e, TARG_NOCREATE);
if (gn != NULL && !OP_NOP(gn->type) && (gn->type & OP_HAS_COMMANDS))
return true;
else
return false;
}
/*-
*-----------------------------------------------------------------------
* CondCvtArg --
* Convert the given number into a double. If the number begins
* with 0x, it is interpreted as a hexadecimal integer
* and converted to a double from there. All other strings just have
* strtod called on them.
*
* Results:
* Sets 'value' to double value of string.
* Returns true if the string was a valid number, false o.w.
*
* Side Effects:
* Can change 'value' even if string is not a valid number.
*-----------------------------------------------------------------------
*/
static bool
CondCvtArg(const char *str, double *value)
{
if (*str == '0' && str[1] == 'x') {
long i;
for (str += 2, i = 0; *str; str++) {
int x;
if (ISDIGIT(*str))
x = *str - '0';
else if (ISXDIGIT(*str))
x = 10 + *str - (ISUPPER(*str) ? 'A' : 'a');
else
return false;
i = (i << 4) + x;
}
*value = (double) i;
return true;
}
else {
char *eptr;
*value = strtod(str, &eptr);
return *eptr == '\0';
}
}
static Token
CondHandleNumber(bool doEval)
{
const char *end;
char *lhs;
end = condExpr;
while (!ISSPACE(*end) && strchr("!=><", *end) == NULL)
end++;
lhs = Str_dupi(condExpr, end);
condExpr = end;
return CondHandleComparison(lhs, true, doEval);
}
static Token
CondHandleVarSpec(bool doEval)
{
char *lhs;
size_t varSpecLen;
bool doFree;
/* Parse the variable spec and skip over it, saving its
* value in lhs. */
lhs = Var_Parse(condExpr, NULL, doEval,&varSpecLen,&doFree);
if (lhs == var_Error)
/* Even if !doEval, we still report syntax errors, which
* is what getting var_Error back with !doEval means. */
return Err;
condExpr += varSpecLen;
if (!ISSPACE(*condExpr) &&
strchr("!=><", *condExpr) == NULL) {
BUFFER buf;
Buf_Init(&buf, 0);
Buf_AddString(&buf, lhs);
if (doFree)
free(lhs);
for (;*condExpr && !ISSPACE(*condExpr); condExpr++)
Buf_AddChar(&buf, *condExpr);
lhs = Var_Subst(Buf_Retrieve(&buf), NULL, doEval);
Buf_Destroy(&buf);
doFree = true;
}
return CondHandleComparison(lhs, doFree, doEval);
}
static Token
CondHandleString(bool doEval)
{
char *lhs;
const char *begin;
BUFFER buf;
/* find the extent of the string */
begin = ++condExpr;
while (*condExpr && *condExpr != '"') {
condExpr++;
}
Buf_Init(&buf, 0);
Buf_Addi(&buf, begin, condExpr);
if (*condExpr == '"')
condExpr++;
lhs = Var_Subst(Buf_Retrieve(&buf), NULL, doEval);
Buf_Destroy(&buf);
return CondHandleComparison(lhs, true, doEval);
}
static Token
CondHandleComparison(char *lhs, bool doFree, bool doEval)
{
Token t;
const char *rhs;
const char *op;
t = Err;
/* Skip whitespace to get to the operator. */
while (ISSPACE(*condExpr))
condExpr++;
/* Make sure the operator is a valid one. If it isn't a
* known relational operator, pretend we got a
* != 0 comparison. */
op = condExpr;
switch (*condExpr) {
case '!':
case '=':
case '<':
case '>':
if (condExpr[1] == '=')
condExpr += 2;
else
condExpr += 1;
break;
default:
op = "!=";
rhs = "0";
goto do_compare;
}
while (ISSPACE(*condExpr))
condExpr++;
if (*condExpr == '\0') {
Parse_Error(PARSE_WARNING,
"Missing right-hand-side of operator");
goto error;
}
rhs = condExpr;
do_compare:
if (*rhs == '"') {
/* Doing a string comparison. Only allow == and != for
* operators. */
char *string;
const char *cp;
int qt;
BUFFER buf;
do_string_compare:
if ((*op != '!' && *op != '=') || op[1] != '=') {
Parse_Error(PARSE_WARNING,
"String comparison operator should be either == or !=");
goto error;
}
Buf_Init(&buf, 0);
qt = *rhs == '"' ? 1 : 0;
for (cp = &rhs[qt]; ((qt && *cp != '"') ||
(!qt && strchr(" \t)", *cp) == NULL)) && *cp != '\0';) {
if (*cp == '$') {
size_t len;
if (Var_ParseBuffer(&buf, cp, NULL, doEval,
&len)) {
cp += len;
continue;
}
} else if (*cp == '\\' && cp[1] != '\0')
/* Backslash escapes things -- skip over next
* character, if it exists. */
cp++;
Buf_AddChar(&buf, *cp++);
}
string = Buf_Retrieve(&buf);
if (DEBUG(COND))
printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
lhs, string, op);
/* Null-terminate rhs and perform the comparison.
* t is set to the result. */
if (*op == '=')
t = strcmp(lhs, string) ? False : True;
else
t = strcmp(lhs, string) ? True : False;
free(string);
if (rhs == condExpr) {
if (!qt && *cp == ')')
condExpr = cp;
else if (*cp == '\0')
condExpr = cp;
else
condExpr = cp + 1;
}
} else {
/* rhs is either a float or an integer. Convert both the
* lhs and the rhs to a double and compare the two. */
double left, right;
char *string;
if (!CondCvtArg(lhs, &left))
goto do_string_compare;
if (*rhs == '$') {
size_t len;
bool freeIt;
string = Var_Parse(rhs, NULL, doEval,&len,&freeIt);
if (string == var_Error)
right = 0.0;
else {
if (!CondCvtArg(string, &right)) {
if (freeIt)
free(string);
goto do_string_compare;
}
if (freeIt)
free(string);
if (rhs == condExpr)
condExpr += len;
}
} else {
if (!CondCvtArg(rhs, &right))
goto do_string_compare;
if (rhs == condExpr) {
/* Skip over the right-hand side. */
while (!ISSPACE(*condExpr) && *condExpr != '\0')
condExpr++;
}
}
if (DEBUG(COND))
printf("left = %f, right = %f, op = %.2s\n", left,
right, op);
switch (op[0]) {
case '!':
if (op[1] != '=') {
Parse_Error(PARSE_WARNING, "Unknown operator");
goto error;
}
t = left != right ? True : False;
break;
case '=':
if (op[1] != '=') {
Parse_Error(PARSE_WARNING, "Unknown operator");
goto error;
}
t = left == right ? True : False;
break;
case '<':
if (op[1] == '=')
t = left <= right ? True : False;
else
t = left < right ? True : False;
break;
case '>':
if (op[1] == '=')
t = left >= right ? True : False;
else
t = left > right ? True : False;
break;
}
}
error:
if (doFree)
free(lhs);
return t;
}
#define S(s) s, sizeof(s)-1
static struct operator {
const char *s;
size_t len;
bool (*proc)(struct Name *);
} ops[] = {
{S("defined"), CondDoDefined},
{S("make"), CondDoMake},
{S("exists"), CondDoExists},
{S("target"), CondDoTarget},
{S("commands"), CondDoTargetWithCommands},
{NULL, 0, NULL}
};
static Token
CondHandleDefault(bool doEval)
{
bool t;
bool (*evalProc)(struct Name *);
bool invert = false;
struct Name arg;
size_t arglen;
evalProc = NULL;
if (strncmp(condExpr, "empty", 5) == 0) {
/* Use Var_Parse to parse the spec in parens and return
* True if the resulting string is empty. */
size_t length;
bool doFree;
char *val;
condExpr += 5;
for (arglen = 0; condExpr[arglen] != '(' &&
condExpr[arglen] != '\0';)
arglen++;
if (condExpr[arglen] != '\0') {
val = Var_Parse(&condExpr[arglen - 1], NULL,
doEval, &length, &doFree);
if (val == var_Error)
t = Err;
else {
/* A variable is empty when it just contains
* spaces... 4/15/92, christos */
char *p;
for (p = val; ISSPACE(*p); p++)
continue;
t = *p == '\0' ? True : False;
}
if (doFree)
free(val);
/* 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;
return t;
} else
condExpr -= 5;
} else {
struct operator *op;
for (op = ops; op != NULL; op++)
if (strncmp(condExpr, op->s, op->len) == 0) {
condExpr += op->len;
if (CondGetArg(&condExpr, &arg, op->s, true))
evalProc = op->proc;
else
condExpr -= op->len;
break;
}
}
if (evalProc == NULL) {
/* 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. */
invert = condInvert;
evalProc = condDefProc;
/* XXX should we ignore problems now ? */
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)(&arg) ?
(invert ? False : True) :
(invert ? True : False));
VarName_Free(&arg);
return t;
}
/*-
*-----------------------------------------------------------------------
* CondToken --
* Return the next token from the input.
*
* Results:
* A Token for the next lexical token in the stream.
*
* Side Effects:
* condPushback will be set back to None if it is used.
*-----------------------------------------------------------------------
*/
static Token
CondToken(bool doEval)
{
if (condPushBack != None) {
Token t;
t = condPushBack;
condPushBack = None;
return t;
}
while (ISSPACE(*condExpr))
condExpr++;
switch (*condExpr) {
case '(':
condExpr++;
return LParen;
case ')':
condExpr++;
return RParen;
case '|':
if (condExpr[1] == '|')
condExpr++;
condExpr++;
return Or;
case '&':
if (condExpr[1] == '&')
condExpr++;
condExpr++;
return And;
case '!':
condExpr++;
return Not;
case '\n':
case '\0':
return EndOfFile;
case '"':
return CondHandleString(doEval);
case '$':
return CondHandleVarSpec(doEval);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return CondHandleNumber(doEval);
default:
return CondHandleDefault(doEval);
}
}
/*-
*-----------------------------------------------------------------------
* CondT --
* Parse a single term in the expression. This consists of a terminal
* symbol or Not and a terminal symbol (not including the binary
* operators):
* T -> defined(variable) | make(target) | exists(file) | symbol
* T -> ! T | ( E )
*
* Results:
* True, False or Err.
*
* Side Effects:
* Tokens are consumed.
*-----------------------------------------------------------------------
*/
static Token
CondT(bool doEval)
{
Token t;
t = CondToken(doEval);
if (t == EndOfFile)
/* If we reached the end of the expression, the expression
* is malformed... */
t = Err;
else if (t == LParen) {
/* T -> ( E ). */
t = CondE(doEval);
if (t != Err)
if (CondToken(doEval) != RParen)
t = Err;
} else if (t == Not) {
t = CondT(doEval);
if (t == True)
t = False;
else if (t == False)
t = True;
}
return t;
}
/*-
*-----------------------------------------------------------------------
* CondF --
* Parse a conjunctive factor (nice name, wot?)
* F -> T && F | T
*
* Results:
* True, False or Err
*
* Side Effects:
* Tokens are consumed.
*-----------------------------------------------------------------------
*/
static Token
CondF(bool doEval)
{
Token l, o;
l = CondT(doEval);
if (l != Err) {
o = CondToken(doEval);
if (o == And) {
/* F -> T && F
*
* 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.
* */
if (l == True)
l = CondF(doEval);
else
(void)CondF(false);
} else
/* F -> T. */
condPushBack = o;
}
return l;
}
/*-
*-----------------------------------------------------------------------
* CondE --
* Main expression production.
* E -> F || E | F
*
* Results:
* True, False or Err.
*
* Side Effects:
* Tokens are, of course, consumed.
*-----------------------------------------------------------------------
*/
static Token
CondE(bool doEval)
{
Token l, o;
l = CondF(doEval);
if (l != Err) {
o = CondToken(doEval);
if (o == Or) {
/* E -> F || E
*
* 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. */
if (l == False)
l = CondE(doEval);
else
(void)CondE(false);
} else
/* E -> F. */
condPushBack = o;
}
return l;
}
/* Evaluate conditional in line.
* returns COND_SKIP, COND_PARSE, COND_INVALID, COND_ISFOR, COND_ISINCLUDE,
* COND_ISUNDEF.
* A conditional line looks like this:
* <cond-type> <expr>
* 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.
*/
int
Cond_Eval(const char *line)
{
/* find end of keyword */
const char *end;
uint32_t k;
size_t len;
struct If *ifp;
bool value = false;
int level; /* Level at which to report errors. */
level = PARSE_FATAL;
for (end = line; ISLOWER(*end); end++)
;
/* quick path: recognize special targets early on */
if (*end == '.' || *end == ':')
return COND_INVALID;
len = end - line;
k = ohash_interval(line, &end);
switch(k % MAGICSLOTS2) {
case K_COND_IF % MAGICSLOTS2:
if (k == K_COND_IF && len == strlen(COND_IF) &&
strncmp(line, COND_IF, len) == 0) {
ifp = ifs + COND_IF_INDEX;
} else
return COND_INVALID;
break;
case K_COND_IFDEF % MAGICSLOTS2:
if (k == K_COND_IFDEF && len == strlen(COND_IFDEF) &&
strncmp(line, COND_IFDEF, len) == 0) {
ifp = ifs + COND_IFDEF_INDEX;
} else
return COND_INVALID;
break;
case K_COND_IFNDEF % MAGICSLOTS2:
if (k == K_COND_IFNDEF && len == strlen(COND_IFNDEF) &&
strncmp(line, COND_IFNDEF, len) == 0) {
ifp = ifs + COND_IFNDEF_INDEX;
} else
return COND_INVALID;
break;
case K_COND_IFMAKE % MAGICSLOTS2:
if (k == K_COND_IFMAKE && len == strlen(COND_IFMAKE) &&
strncmp(line, COND_IFMAKE, len) == 0) {
ifp = ifs + COND_IFMAKE_INDEX;
} else
return COND_INVALID;
break;
case K_COND_IFNMAKE % MAGICSLOTS2:
if (k == K_COND_IFNMAKE && len == strlen(COND_IFNMAKE) &&
strncmp(line, COND_IFNMAKE, len) == 0) {
ifp = ifs + COND_IFNMAKE_INDEX;
} else
return COND_INVALID;
break;
case K_COND_ELIF % MAGICSLOTS2:
if (k == K_COND_ELIF && len == strlen(COND_ELIF) &&
strncmp(line, COND_ELIF, len) == 0) {
ifp = ifs + COND_ELIF_INDEX;
} else
return COND_INVALID;
break;
case K_COND_ELIFDEF % MAGICSLOTS2:
if (k == K_COND_ELIFDEF && len == strlen(COND_ELIFDEF) &&
strncmp(line, COND_ELIFDEF, len) == 0) {
ifp = ifs + COND_ELIFDEF_INDEX;
} else
return COND_INVALID;
break;
case K_COND_ELIFNDEF % MAGICSLOTS2:
if (k == K_COND_ELIFNDEF && len == strlen(COND_ELIFNDEF) &&
strncmp(line, COND_ELIFNDEF, len) == 0) {
ifp = ifs + COND_ELIFNDEF_INDEX;
} else
return COND_INVALID;
break;
case K_COND_ELIFMAKE % MAGICSLOTS2:
if (k == K_COND_ELIFMAKE && len == strlen(COND_ELIFMAKE) &&
strncmp(line, COND_ELIFMAKE, len) == 0) {
ifp = ifs + COND_ELIFMAKE_INDEX;
} else
return COND_INVALID;
break;
case K_COND_ELIFNMAKE % MAGICSLOTS2:
if (k == K_COND_ELIFNMAKE && len == strlen(COND_ELIFNMAKE) &&
strncmp(line, COND_ELIFNMAKE, len) == 0) {
ifp = ifs + COND_ELIFNMAKE_INDEX;
} else
return COND_INVALID;
break;
case K_COND_ELSE % MAGICSLOTS2:
/* valid conditional whose value is the inverse
* of the previous if we parsed. */
if (k == K_COND_ELSE && len == strlen(COND_ELSE) &&
strncmp(line, COND_ELSE, len) == 0) {
if (condTop == MAXIF) {
Parse_Error(level, "if-less else");
return COND_INVALID;
} else if (skipIfLevel == 0) {
value = !condStack[condTop].value;
ifp = ifs + COND_ELSE_INDEX;
} else
return COND_SKIP;
} else
return COND_INVALID;
break;
case K_COND_ENDIF % MAGICSLOTS2:
if (k == K_COND_ENDIF && len == strlen(COND_ENDIF) &&
strncmp(line, COND_ENDIF, len) == 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 a decent if. */
if (skipIfLevel != 0) {
skipIfLevel--;
return COND_SKIP;
} else {
if (condTop == MAXIF) {
Parse_Error(level, "if-less endif");
return COND_INVALID;
} else {
skipLine = false;
condTop++;
return COND_PARSE;
}
}
} else
return COND_INVALID;
break;
/* Recognize other keywords there, to simplify parser's task */
case K_COND_FOR % MAGICSLOTS2:
if (k == K_COND_FOR && len == strlen(COND_FOR) &&
strncmp(line, COND_FOR, len) == 0)
return COND_ISFOR;
else
return COND_INVALID;
case K_COND_UNDEF % MAGICSLOTS2:
if (k == K_COND_UNDEF && len == strlen(COND_UNDEF) &&
strncmp(line, COND_UNDEF, len) == 0)
return COND_ISUNDEF;
else
return COND_INVALID;
case K_COND_POISON % MAGICSLOTS2:
if (k == K_COND_POISON && len == strlen(COND_POISON) &&
strncmp(line, COND_POISON, len) == 0)
return COND_ISPOISON;
else
return COND_INVALID;
case K_COND_INCLUDE % MAGICSLOTS2:
if (k == K_COND_INCLUDE && len == strlen(COND_INCLUDE) &&
strncmp(line, COND_INCLUDE, len) == 0)
return COND_ISINCLUDE;
else
return COND_INVALID;
default:
/* Not a valid conditional type. No error... */
return COND_INVALID;
}
if (ifp->isElse) {
if (condTop == MAXIF) {
Parse_Error(level, "if-less elif");
return COND_INVALID;
} else if (skipIfLevel != 0 || condStack[condTop].value) {
/*
* Skip if we're meant to or is an else-type
* conditional and previous corresponding one was
* evaluated to true.
*/
skipLine = true;
return COND_SKIP;
}
} else if (skipLine) {
/* Don't even try to evaluate a conditional that's not an else
* if we're skipping things... */
skipIfLevel++;
return COND_SKIP;
} else
condTop--;
if (condTop < 0) {
/* 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);
condTop = 0;
return COND_INVALID;
}
if (ifp->defProc) {
/* Initialize file-global variables for parsing. */
condDefProc = ifp->defProc;
condInvert = ifp->doNot;
line += len;
while (*line == ' ' || *line == '\t')
line++;
condExpr = line;
condPushBack = None;
switch (CondE(true)) {
case True:
if (CondToken(true) == EndOfFile) {
value = true;
break;
}
goto err;
/* FALLTHROUGH */
case False:
if (CondToken(true) == EndOfFile) {
value = false;
break;
}
/* FALLTHROUGH */
case Err:
err:
Parse_Error(level, "Malformed conditional (%s)", line);
return COND_INVALID;
default:
break;
}
}
condStack[condTop].value = value;
Parse_FillLocation(&condStack[condTop].origin);
skipLine = !value;
return value ? COND_PARSE : COND_SKIP;
}
void
Cond_End(void)
{
int i;
if (condTop != MAXIF) {
Parse_Error(PARSE_FATAL, "%s%d open conditional%s",
condTop == 0 ? "at least ": "", MAXIF-condTop,
MAXIF-condTop == 1 ? "" : "s");
for (i = MAXIF-1; i >= condTop; i--) {
fprintf(stderr, "\t(%s:%lu)\n",
condStack[i].origin.fname,
condStack[i].origin.lineno);
}
}
condTop = MAXIF;
}