/*
* Parse a line and determine its type. We keep the preprocessor line
* parser state between calls in the global variable linestate, with
* help from skipcomment().
*/
static Linetype
parseline(void)
{
const char *cp;
int cursym;
int kwlen;
Linetype retval;
Comment_state wascomment;
linenum++;
if (fgets(tline, MAXLINE, input) == NULL)
return (LT_EOF);
if (newline == NULL) {
if (strrchr(tline, ‘\n’) == strrchr(tline, ‘\r’) + 1)
newline = newline_crlf;
else
newline = newline_unix;
}
retval = LT_PLAIN;
wascomment = incomment;
cp = skipcomment(tline);
if (linestate == LS_START) {
if (*cp == ‘#’) {
linestate = LS_HASH;
firstsym = true;
cp = skipcomment(cp + 1);
} else if (*cp != ‘\0′)
linestate = LS_DIRTY;
}
if (!incomment && linestate == LS_HASH) {
keyword = tline + (cp – tline);
cp = skipsym(cp);
kwlen = cp – keyword;
/* no way can we deal with a continuation inside a keyword */
if (strncmp(cp, “\\\r\n”, 3) == 0 ||
strncmp(cp, “\\\n”, 2) == 0)
Eioccc();
if (strlcmp(“ifdef”, keyword, kwlen) == 0 ||
strlcmp(“ifndef”, keyword, kwlen) == 0) {
cp = skipcomment(cp);
if ((cursym = findsym(cp)) < 0)
retval = LT_IF;
else {
retval = (keyword[2] == ‘n’)
? LT_FALSE : LT_TRUE;
if (value[cursym] == NULL)
retval = (retval == LT_TRUE)
? LT_FALSE : LT_TRUE;
if (ignore[cursym])
retval = (retval == LT_TRUE)
? LT_TRUEI : LT_FALSEI;
}
cp = skipsym(cp);
} else if (strlcmp(“if”, keyword, kwlen) == 0)
retval = ifeval(&cp);
else if (strlcmp(“elif”, keyword, kwlen) == 0)
retval = ifeval(&cp) – LT_IF + LT_ELIF;
else if (strlcmp(“else”, keyword, kwlen) == 0)
retval = LT_ELSE;
else if (strlcmp(“endif”, keyword, kwlen) == 0)
retval = LT_ENDIF;
else {
linestate = LS_DIRTY;
retval = LT_PLAIN;
}
cp = skipcomment(cp);
if (*cp != ‘\0′) {
linestate = LS_DIRTY;
if (retval == LT_TRUE || retval == LT_FALSE ||
retval == LT_TRUEI || retval == LT_FALSEI)
retval = LT_IF;
if (retval == LT_ELTRUE || retval == LT_ELFALSE)
retval = LT_ELIF;
}
if (retval != LT_PLAIN && (wascomment || incomment)) {
retval += LT_DODGY;
if (incomment)
linestate = LS_DIRTY;
}
/* skipcomment normally changes the state, except
if the last line of the file lacks a newline, or
if there is too much whitespace in a directive */
if (linestate == LS_HASH) {
size_t len = cp – tline;
if (fgets(tline + len, MAXLINE – len, input) == NULL) {
/* append the missing newline */
strcpy(tline + len, newline);
cp += strlen(newline);
linestate = LS_START;
} else {
linestate = LS_DIRTY;
}
}
}
if (linestate == LS_DIRTY) {
while (*cp != ‘\0′)
cp = skipcomment(cp + 1);
}
debug(“parser line %d state %s comment %s line”, linenum,
comment_name[incomment], linestate_name[linestate]);
return (retval);
}
/*
* These are the binary operators that are supported by the expression
* evaluator.
*/
static Linetype op_strict(int *p, int v, Linetype at, Linetype bt) {
if(at == LT_IF || bt == LT_IF) return (LT_IF);
return (*p = v, v ? LT_TRUE : LT_FALSE);
}
static Linetype op_lt(int *p, Linetype at, int a, Linetype bt, int b) {
return op_strict(p, a < b, at, bt);
}
static Linetype op_gt(int *p, Linetype at, int a, Linetype bt, int b) {
return op_strict(p, a > b, at, bt);
}
static Linetype op_le(int *p, Linetype at, int a, Linetype bt, int b) {
return op_strict(p, a <= b, at, bt);
}
static Linetype op_ge(int *p, Linetype at, int a, Linetype bt, int b) {
return op_strict(p, a >= b, at, bt);
}
static Linetype op_eq(int *p, Linetype at, int a, Linetype bt, int b) {
return op_strict(p, a == b, at, bt);
}
static Linetype op_ne(int *p, Linetype at, int a, Linetype bt, int b) {
return op_strict(p, a != b, at, bt);
}
static Linetype op_or(int *p, Linetype at, int a, Linetype bt, int b) {
if (!strictlogic && (at == LT_TRUE || bt == LT_TRUE))
return (*p = 1, LT_TRUE);
return op_strict(p, a || b, at, bt);
}
static Linetype op_and(int *p, Linetype at, int a, Linetype bt, int b) {
if (!strictlogic && (at == LT_FALSE || bt == LT_FALSE))
return (*p = 0, LT_FALSE);
return op_strict(p, a && b, at, bt);
}