kbuild: fix headers_exports with boolean expression

When we had code like this in a header unifdef failed to
deduct that the expression was always false - and we had code exported
that was not intended for userspace.

#if defined(__KERNEL__) && !defined(__ASSEMBLY__)
  int a;
#endif

This commit implment support in unidef which allows it to work out if
an #if expression always evaluates true or false for symbols which
are being undefined/always defined.

The patch is slightly more complicated than I'd hoped because unifdef
needs to see lines fully evaluated - doing otherwise causes it to
mark the line as "dirty" and copy it over no matter what.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
diff --git a/scripts/unifdef.c b/scripts/unifdef.c
index 05a31a6..30d459f 100644
--- a/scripts/unifdef.c
+++ b/scripts/unifdef.c
@@ -678,8 +678,10 @@
 	if (*cp == '!') {
 		debug("eval%d !", ops - eval_ops);
 		cp++;
-		if (eval_unary(ops, valp, &cp) == LT_IF)
+		if (eval_unary(ops, valp, &cp) == LT_IF) {
+			*cpp = cp;
 			return (LT_IF);
+		}
 		*valp = !*valp;
 	} else if (*cp == '(') {
 		cp++;
@@ -700,13 +702,16 @@
 			return (LT_IF);
 		cp = skipcomment(cp);
 		sym = findsym(cp);
-		if (sym < 0)
-			return (LT_IF);
-		*valp = (value[sym] != NULL);
 		cp = skipsym(cp);
 		cp = skipcomment(cp);
 		if (*cp++ != ')')
 			return (LT_IF);
+		if (sym >= 0)
+			*valp = (value[sym] != NULL);
+		else {
+			*cpp = cp;
+			return (LT_IF);
+		}
 		keepthis = false;
 	} else if (!endsym(*cp)) {
 		debug("eval%d symbol", ops - eval_ops);
@@ -741,11 +746,11 @@
 	const struct op *op;
 	const char *cp;
 	int val;
+	Linetype lhs, rhs;
 
 	debug("eval%d", ops - eval_ops);
 	cp = *cpp;
-	if (ops->inner(ops+1, valp, &cp) == LT_IF)
-		return (LT_IF);
+	lhs = ops->inner(ops+1, valp, &cp);
 	for (;;) {
 		cp = skipcomment(cp);
 		for (op = ops->op; op->str != NULL; op++)
@@ -755,14 +760,32 @@
 			break;
 		cp += strlen(op->str);
 		debug("eval%d %s", ops - eval_ops, op->str);
-		if (ops->inner(ops+1, &val, &cp) == LT_IF)
-			return (LT_IF);
-		*valp = op->fn(*valp, val);
+		rhs = ops->inner(ops+1, &val, &cp);
+		if (op->fn == op_and && (lhs == LT_FALSE || rhs == LT_FALSE)) {
+			debug("eval%d: and always false", ops - eval_ops);
+			if (lhs == LT_IF)
+				*valp = val;
+			lhs = LT_FALSE;
+			continue;
+		}
+		if (op->fn == op_or && (lhs == LT_TRUE || rhs == LT_TRUE)) {
+			debug("eval%d: or always true", ops - eval_ops);
+			if (lhs == LT_IF)
+				*valp = val;
+			lhs = LT_TRUE;
+			continue;
+		}
+		if (rhs == LT_IF)
+			lhs = LT_IF;
+		if (lhs != LT_IF)
+			*valp = op->fn(*valp, val);
 	}
 
 	*cpp = cp;
 	debug("eval%d = %d", ops - eval_ops, *valp);
-	return (*valp ? LT_TRUE : LT_FALSE);
+	if (lhs != LT_IF)
+		lhs = (*valp ? LT_TRUE : LT_FALSE);
+	return lhs;
 }
 
 /*
@@ -773,12 +796,15 @@
 static Linetype
 ifeval(const char **cpp)
 {
+	const char *cp = *cpp;
 	int ret;
 	int val;
 
 	debug("eval %s", *cpp);
 	keepthis = killconsts ? false : true;
-	ret = eval_table(eval_ops, &val, cpp);
+	ret = eval_table(eval_ops, &val, &cp);
+	if (ret != LT_IF)
+		*cpp = cp;
 	debug("eval = %d", val);
 	return (keepthis ? LT_IF : ret);
 }