[PATCH v3 1/2] kconfig: allow use of relations other than (in)equality

From: Jan Beulich
Date: Mon Jun 15 2015 - 08:00:33 EST


Over the years I found it desirable to be able to use all sorts of
relations, not just (in)equality. And apparently I'm not the only one,
as there's at least one example in the tree where the programmer
assumed this would work (see DEBUG_UART_8250_WORD in
arch/arm/Kconfig.debug). Another possible use would e.g. be to fold the
two SMP/NR_CPUS prompts into one: SMP could be promptless, simply
depending on NR_CPUS > 1.

A (desirable) side effect of this change - resulting from numeric
values now necessarily being compared as numbers rather than as
strings - is that comparing hex values now works as expected: Other
than int ones (which aren't allowed to have leading zeroes), zeroes
following the 0x prefix made them compare unequal even if their values
were equal.

Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
---
v3: Split changes to generated files from main patch to a subsequent
one.

--- a/scripts/kconfig/expr.c
+++ b/scripts/kconfig/expr.c
@@ -82,6 +82,10 @@ struct expr *expr_copy(const struct expr
e->left.expr = expr_copy(org->left.expr);
break;
case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
case E_UNEQUAL:
e->left.sym = org->left.sym;
e->right.sym = org->right.sym;
@@ -114,6 +118,10 @@ void expr_free(struct expr *e)
expr_free(e->left.expr);
return;
case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
case E_UNEQUAL:
break;
case E_OR:
@@ -200,6 +208,10 @@ static int expr_eq(struct expr *e1, stru
return 0;
switch (e1->type) {
case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
case E_UNEQUAL:
return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
case E_SYMBOL:
@@ -647,6 +659,10 @@ struct expr *expr_transform(struct expr
return NULL;
switch (e->type) {
case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
case E_UNEQUAL:
case E_SYMBOL:
case E_LIST:
@@ -719,6 +735,22 @@ struct expr *expr_transform(struct expr
e = tmp;
e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
break;
+ case E_LEQ:
+ case E_GEQ:
+ // !a<='x' -> a>'x'
+ tmp = e->left.expr;
+ free(e);
+ e = tmp;
+ e->type = e->type == E_LEQ ? E_GTH : E_LTH;
+ break;
+ case E_LTH:
+ case E_GTH:
+ // !a<'x' -> a>='x'
+ tmp = e->left.expr;
+ free(e);
+ e = tmp;
+ e->type = e->type == E_LTH ? E_GEQ : E_LEQ;
+ break;
case E_OR:
// !(a || b) -> !a && !b
tmp = e->left.expr;
@@ -789,6 +821,10 @@ int expr_contains_symbol(struct expr *de
case E_SYMBOL:
return dep->left.sym == sym;
case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
case E_UNEQUAL:
return dep->left.sym == sym ||
dep->right.sym == sym;
@@ -914,6 +950,10 @@ struct expr *expr_trans_compare(struct e
case E_NOT:
return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym);
case E_UNEQUAL:
+ case E_LTH:
+ case E_LEQ:
+ case E_GTH:
+ case E_GEQ:
case E_EQUAL:
if (type == E_EQUAL) {
if (sym == &symbol_yes)
@@ -941,10 +981,57 @@ struct expr *expr_trans_compare(struct e
return NULL;
}

+enum string_value_kind {
+ k_string,
+ k_signed,
+ k_unsigned,
+ k_invalid
+};
+
+union string_value {
+ unsigned long long u;
+ signed long long s;
+};
+
+static enum string_value_kind expr_parse_string(const char *str,
+ enum symbol_type type,
+ union string_value *val)
+{
+ char *tail;
+ enum string_value_kind kind;
+
+ errno = 0;
+ switch (type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ return k_string;
+ case S_INT:
+ val->s = strtoll(str, &tail, 10);
+ kind = k_signed;
+ break;
+ case S_HEX:
+ val->u = strtoull(str, &tail, 16);
+ kind = k_unsigned;
+ break;
+ case S_STRING:
+ case S_UNKNOWN:
+ val->s = strtoll(str, &tail, 0);
+ kind = k_signed;
+ break;
+ default:
+ return k_invalid;
+ }
+ return !errno && !*tail && tail > str && isxdigit(tail[-1])
+ ? kind : k_string;
+}
+
tristate expr_calc_value(struct expr *e)
{
tristate val1, val2;
const char *str1, *str2;
+ enum string_value_kind k1 = k_string, k2 = k_string;
+ union string_value lval = {}, rval = {};
+ int res;

if (!e)
return yes;
@@ -965,21 +1052,57 @@ tristate expr_calc_value(struct expr *e)
val1 = expr_calc_value(e->left.expr);
return EXPR_NOT(val1);
case E_EQUAL:
- sym_calc_value(e->left.sym);
- sym_calc_value(e->right.sym);
- str1 = sym_get_string_value(e->left.sym);
- str2 = sym_get_string_value(e->right.sym);
- return !strcmp(str1, str2) ? yes : no;
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
case E_UNEQUAL:
- sym_calc_value(e->left.sym);
- sym_calc_value(e->right.sym);
- str1 = sym_get_string_value(e->left.sym);
- str2 = sym_get_string_value(e->right.sym);
- return !strcmp(str1, str2) ? no : yes;
+ break;
default:
printf("expr_calc_value: %d?\n", e->type);
return no;
}
+
+ sym_calc_value(e->left.sym);
+ sym_calc_value(e->right.sym);
+ str1 = sym_get_string_value(e->left.sym);
+ str2 = sym_get_string_value(e->right.sym);
+
+ if (e->left.sym->type != S_STRING || e->right.sym->type != S_STRING) {
+ k1 = expr_parse_string(str1, e->left.sym->type, &lval);
+ k2 = expr_parse_string(str2, e->right.sym->type, &rval);
+ }
+
+ if (k1 == k_string || k2 == k_string)
+ res = strcmp(str1, str2);
+ else if (k1 == k_invalid || k2 == k_invalid) {
+ if (e->type != E_EQUAL && e->type != E_UNEQUAL) {
+ printf("Cannot compare \"%s\" and \"%s\"\n", str1, str2);
+ return no;
+ }
+ res = strcmp(str1, str2);
+ } else if (k1 == k_unsigned || k2 == k_unsigned)
+ res = (lval.u > rval.u) - (lval.u < rval.u);
+ else /* if (k1 == k_signed && k2 == k_signed) */
+ res = (lval.s > rval.s) - (lval.s < rval.s);
+
+ switch(e->type) {
+ case E_EQUAL:
+ return res ? no : yes;
+ case E_GEQ:
+ return res >= 0 ? yes : no;
+ case E_GTH:
+ return res > 0 ? yes : no;
+ case E_LEQ:
+ return res <= 0 ? yes : no;
+ case E_LTH:
+ return res < 0 ? yes : no;
+ case E_UNEQUAL:
+ return res ? yes : no;
+ default:
+ printf("expr_calc_value: relation %d?\n", e->type);
+ return no;
+ }
}

static int expr_compare_type(enum expr_type t1, enum expr_type t2)
@@ -987,6 +1110,12 @@ static int expr_compare_type(enum expr_t
if (t1 == t2)
return 0;
switch (t1) {
+ case E_LEQ:
+ case E_LTH:
+ case E_GEQ:
+ case E_GTH:
+ if (t2 == E_EQUAL || t2 == E_UNEQUAL)
+ return 1;
case E_EQUAL:
case E_UNEQUAL:
if (t2 == E_NOT)
@@ -1080,6 +1209,24 @@ void expr_print(struct expr *e, void (*f
fn(data, NULL, "=");
fn(data, e->right.sym, e->right.sym->name);
break;
+ case E_LEQ:
+ case E_LTH:
+ if (e->left.sym->name)
+ fn(data, e->left.sym, e->left.sym->name);
+ else
+ fn(data, NULL, "<choice>");
+ fn(data, NULL, e->type == E_LEQ ? "<=" : "<");
+ fn(data, e->right.sym, e->right.sym->name);
+ break;
+ case E_GEQ:
+ case E_GTH:
+ if (e->left.sym->name)
+ fn(data, e->left.sym, e->left.sym->name);
+ else
+ fn(data, NULL, "<choice>");
+ fn(data, NULL, e->type == E_LEQ ? ">=" : ">");
+ fn(data, e->right.sym, e->right.sym->name);
+ break;
case E_UNEQUAL:
if (e->left.sym->name)
fn(data, e->left.sym, e->left.sym->name);
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -29,7 +29,9 @@ typedef enum tristate {
} tristate;

enum expr_type {
- E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_LIST, E_SYMBOL, E_RANGE
+ E_NONE, E_OR, E_AND, E_NOT,
+ E_EQUAL, E_UNEQUAL, E_LTH, E_LEQ, E_GTH, E_GEQ,
+ E_LIST, E_SYMBOL, E_RANGE
};

union expr_data {
--- a/scripts/kconfig/symbol.c
+++ b/scripts/kconfig/symbol.c
@@ -1166,6 +1166,10 @@ static struct symbol *sym_check_expr_dep
case E_NOT:
return sym_check_expr_deps(e->left.expr);
case E_EQUAL:
+ case E_GEQ:
+ case E_GTH:
+ case E_LEQ:
+ case E_LTH:
case E_UNEQUAL:
sym = sym_check_deps(e->left.sym);
if (sym)
--- a/scripts/kconfig/zconf.l
+++ b/scripts/kconfig/zconf.l
@@ -122,6 +122,10 @@ n [A-Za-z0-9_]
"!" return T_NOT;
"=" return T_EQUAL;
"!=" return T_UNEQUAL;
+ "<=" return T_LESS_EQUAL;
+ ">=" return T_GREATER_EQUAL;
+ "<" return T_LESS;
+ ">" return T_GREATER;
\"|\' {
str = yytext[0];
new_string();
--- a/scripts/kconfig/zconf.y
+++ b/scripts/kconfig/zconf.y
@@ -69,6 +69,10 @@ static struct menu *current_menu, *curre
%token <string> T_WORD
%token <string> T_WORD_QUOTE
%token T_UNEQUAL
+%token T_LESS
+%token T_LESS_EQUAL
+%token T_GREATER
+%token T_GREATER_EQUAL
%token T_CLOSE_PAREN
%token T_OPEN_PAREN
%token T_EOL
@@ -76,6 +80,7 @@ static struct menu *current_menu, *curre
%left T_OR
%left T_AND
%left T_EQUAL T_UNEQUAL
+%left T_LESS T_LESS_EQUAL T_GREATER T_GREATER_EQUAL
%nonassoc T_NOT

%type <string> prompt
@@ -467,6 +472,10 @@ if_expr: /* empty */ { $$ = NULL; }
;

expr: symbol { $$ = expr_alloc_symbol($1); }
+ | symbol T_LESS symbol { $$ = expr_alloc_comp(E_LTH, $1, $3); }
+ | symbol T_LESS_EQUAL symbol { $$ = expr_alloc_comp(E_LEQ, $1, $3); }
+ | symbol T_GREATER symbol { $$ = expr_alloc_comp(E_GTH, $1, $3); }
+ | symbol T_GREATER_EQUAL symbol { $$ = expr_alloc_comp(E_GEQ, $1, $3); }
| symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); }
| symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); }
| T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; }


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/