kconfig: refactor choice value calculation
Handling choices has always been in a PITA in Kconfig. For example, fixes and reverts were repeated for randconfig with KCONFIG_ALLCONFIG: -422c809f03
("kconfig: fix randomising choice entries in presence of KCONFIG_ALLCONFIG") -23a5dfdad2
("Revert "kconfig: fix randomising choice entries in presence of KCONFIG_ALLCONFIG"") -8357b48549
("kconfig: fix randomising choice entries in presence of KCONFIG_ALLCONFIG") -490f161711
("Revert "kconfig: fix randomising choice entries in presence of KCONFIG_ALLCONFIG"") As these commits pointed out, randconfig does not randomize choices when KCONFIG_ALLCONFIG is used. This issue still remains. [Test Case] choice prompt "choose" config A bool "A" config B bool "B" endchoice $ echo > all.config $ make KCONFIG_ALLCONFIG=1 randconfig The output is always as follows: CONFIG_A=y # CONFIG_B is not set Not only randconfig, but other all*config variants are also broken with KCONFIG_ALLCONFIG. With the same Kconfig, $ echo '# CONFIG_A is not set' > all.config $ make KCONFIG_ALLCONFIG=1 allyesconfig You will get this: CONFIG_A=y # CONFIG_B is not set This is incorrect because it does not respect all.config. The correct output should be: # CONFIG_A is not set CONFIG_B=y To handle user inputs more accurately, this commit refactors the code based on the following principles: - When a user value is given, Kconfig must set it immediately. Do not defer it by setting SYMBOL_NEED_SET_CHOICE_VALUES. - The SYMBOL_DEF_USER flag must not be cleared, unless a new config file is loaded. Kconfig must not forget user inputs. In addition, user values for choices must be managed with priority. If user inputs conflict within a choice block, the newest value wins. The values given by randconfig have lower priority than explicit user inputs. This commit implements it by using a linked list. Every time a choice block gets a new input, it is moved to the top of the list. Let me explain how it works. Let's say, we have a choice block that consists of five symbols: A, B, C, D, and E. Initially, the linked list looks like this: A(=?) --> B(=?) --> C(=?) --> D(=?) --> E(=?) Suppose randconfig is executed with the following KCONFIG_ALLCONFIG: CONFIG_C=y # CONFIG_A is not set CONFIG_D=y First, CONFIG_C=y is read. C is set to 'y' and moved to the top. C(=y) --> A(=?) --> B(=?) --> D(=?) --> E(=?) Next, '# CONFIG_A is not set' is read. A is set to 'n' and moved to the top. A(=n) --> C(=y) --> B(=?) --> D(=?) --> E(=?) Then, 'CONFIG_D=y' is read. D is set to 'y' and moved to the top. D(=y) --> A(=n) --> C(=y) --> B(=?) --> E(=?) Lastly, randconfig shuffles the order of the remaining symbols, resulting in: D(=y) --> A(=n) --> C(=y) --> B(=y) --> E(=y) or D(=y) --> A(=n) --> C(=y) --> E(=y) --> B(=y) When calculating the output, the linked list is traversed and the first visible symbol with 'y' is taken. In this case, it is D if visible. If D is hidden by 'depends on', the next node, A, is examined. Since it is already specified as 'n', it is skipped. Next, C is checked, and selected if it is visible. If C is also invisible, either B or E is chosen as a result of the randomization. If B and E are also invisible, the linked list is traversed in the reverse order, and the least prioritized 'n' symbol is chosen. It is A in this case. Now, Kconfig remembers all user values. This is a big difference from the previous implementation, where Kconfig would forget CONFIG_C=y when CONFIG_D=y appeared in the same input file. The new appaorch respects user-specified values as much as possible. Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
This commit is contained in:
parent
ee29e6204c
commit
f79dc03fe6
@ -114,41 +114,54 @@ static void set_randconfig_seed(void)
|
|||||||
srand(seed);
|
srand(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void randomize_choice_values(struct symbol *csym)
|
/**
|
||||||
|
* randomize_choice_values - randomize choice block
|
||||||
|
*
|
||||||
|
* @choice: menu entry for the choice
|
||||||
|
*/
|
||||||
|
static void randomize_choice_values(struct menu *choice)
|
||||||
{
|
{
|
||||||
struct property *prop;
|
struct menu *menu;
|
||||||
struct symbol *sym;
|
int x;
|
||||||
struct expr *e;
|
int cnt = 0;
|
||||||
int cnt, def;
|
|
||||||
|
|
||||||
prop = sym_get_choice_prop(csym);
|
|
||||||
|
|
||||||
/* count entries in choice block */
|
|
||||||
cnt = 0;
|
|
||||||
expr_list_for_each_sym(prop->expr, e, sym)
|
|
||||||
cnt++;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* find a random value and set it to yes,
|
* First, count the number of symbols to randomize. If sym_has_value()
|
||||||
* set the rest to no so we have only one set
|
* is true, it was specified by KCONFIG_ALLCONFIG. It needs to be
|
||||||
|
* respected.
|
||||||
*/
|
*/
|
||||||
def = rand() % cnt;
|
menu_for_each_sub_entry(menu, choice) {
|
||||||
|
struct symbol *sym = menu->sym;
|
||||||
|
|
||||||
cnt = 0;
|
if (sym && !sym_has_value(sym))
|
||||||
expr_list_for_each_sym(prop->expr, e, sym) {
|
cnt++;
|
||||||
if (def == cnt++) {
|
}
|
||||||
sym->def[S_DEF_USER].tri = yes;
|
|
||||||
csym->def[S_DEF_USER].val = sym;
|
while (cnt > 0) {
|
||||||
} else {
|
x = rand() % cnt;
|
||||||
sym->def[S_DEF_USER].tri = no;
|
|
||||||
}
|
menu_for_each_sub_entry(menu, choice) {
|
||||||
sym->flags |= SYMBOL_DEF_USER;
|
struct symbol *sym = menu->sym;
|
||||||
/* clear VALID to get value calculated */
|
|
||||||
sym->flags &= ~SYMBOL_VALID;
|
if (sym && !sym_has_value(sym))
|
||||||
|
x--;
|
||||||
|
|
||||||
|
if (x < 0) {
|
||||||
|
sym->def[S_DEF_USER].tri = yes;
|
||||||
|
sym->flags |= SYMBOL_DEF_USER;
|
||||||
|
/*
|
||||||
|
* Move the selected item to the _tail_ because
|
||||||
|
* this needs to have a lower priority than the
|
||||||
|
* user input from KCONFIG_ALLCONFIG.
|
||||||
|
*/
|
||||||
|
list_move_tail(&sym->choice_link,
|
||||||
|
&choice->choice_members);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cnt--;
|
||||||
}
|
}
|
||||||
csym->flags |= SYMBOL_DEF_USER;
|
|
||||||
/* clear VALID to get value calculated */
|
|
||||||
csym->flags &= ~SYMBOL_VALID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum conf_def_mode {
|
enum conf_def_mode {
|
||||||
@ -159,9 +172,9 @@ enum conf_def_mode {
|
|||||||
def_random
|
def_random
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool conf_set_all_new_symbols(enum conf_def_mode mode)
|
static void conf_set_all_new_symbols(enum conf_def_mode mode)
|
||||||
{
|
{
|
||||||
struct symbol *sym, *csym;
|
struct menu *menu;
|
||||||
int cnt;
|
int cnt;
|
||||||
/*
|
/*
|
||||||
* can't go as the default in switch-case below, otherwise gcc whines
|
* can't go as the default in switch-case below, otherwise gcc whines
|
||||||
@ -170,7 +183,6 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode)
|
|||||||
int pby = 50; /* probability of bool = y */
|
int pby = 50; /* probability of bool = y */
|
||||||
int pty = 33; /* probability of tristate = y */
|
int pty = 33; /* probability of tristate = y */
|
||||||
int ptm = 33; /* probability of tristate = m */
|
int ptm = 33; /* probability of tristate = m */
|
||||||
bool has_changed = false;
|
|
||||||
|
|
||||||
if (mode == def_random) {
|
if (mode == def_random) {
|
||||||
int n, p[3];
|
int n, p[3];
|
||||||
@ -217,14 +229,21 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for_all_symbols(sym) {
|
menu_for_each_entry(menu) {
|
||||||
|
struct symbol *sym = menu->sym;
|
||||||
tristate val;
|
tristate val;
|
||||||
|
|
||||||
if (sym_has_value(sym) || sym->flags & SYMBOL_VALID ||
|
if (!sym || !menu->prompt || sym_has_value(sym) ||
|
||||||
(sym->type != S_BOOLEAN && sym->type != S_TRISTATE))
|
(sym->type != S_BOOLEAN && sym->type != S_TRISTATE) ||
|
||||||
|
sym_is_choice_value(sym))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
has_changed = true;
|
if (sym_is_choice(sym)) {
|
||||||
|
if (mode == def_random)
|
||||||
|
randomize_choice_values(menu);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case def_yes:
|
case def_yes:
|
||||||
val = yes;
|
val = yes;
|
||||||
@ -251,34 +270,10 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sym->def[S_DEF_USER].tri = val;
|
sym->def[S_DEF_USER].tri = val;
|
||||||
|
sym->flags |= SYMBOL_DEF_USER;
|
||||||
if (!(sym_is_choice(sym) && mode == def_random))
|
|
||||||
sym->flags |= SYMBOL_DEF_USER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sym_clear_all_valid();
|
sym_clear_all_valid();
|
||||||
|
|
||||||
if (mode != def_random) {
|
|
||||||
for_all_symbols(csym) {
|
|
||||||
if ((sym_is_choice(csym) && !sym_has_value(csym)) ||
|
|
||||||
sym_is_choice_value(csym))
|
|
||||||
csym->flags |= SYMBOL_NEED_SET_CHOICE_VALUES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for_all_symbols(csym) {
|
|
||||||
if (sym_has_value(csym) || !sym_is_choice(csym))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
sym_calc_value(csym);
|
|
||||||
if (mode == def_random)
|
|
||||||
randomize_choice_values(csym);
|
|
||||||
else
|
|
||||||
set_all_choice_values(csym);
|
|
||||||
has_changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return has_changed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void conf_rewrite_tristates(tristate old_val, tristate new_val)
|
static void conf_rewrite_tristates(tristate old_val, tristate new_val)
|
||||||
@ -429,10 +424,9 @@ static void conf_choice(struct menu *menu)
|
|||||||
{
|
{
|
||||||
struct symbol *sym, *def_sym;
|
struct symbol *sym, *def_sym;
|
||||||
struct menu *child;
|
struct menu *child;
|
||||||
bool is_new;
|
bool is_new = false;
|
||||||
|
|
||||||
sym = menu->sym;
|
sym = menu->sym;
|
||||||
is_new = !sym_has_value(sym);
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int cnt, def;
|
int cnt, def;
|
||||||
@ -456,8 +450,10 @@ static void conf_choice(struct menu *menu)
|
|||||||
printf("%*c", indent, ' ');
|
printf("%*c", indent, ' ');
|
||||||
printf(" %d. %s (%s)", cnt, menu_get_prompt(child),
|
printf(" %d. %s (%s)", cnt, menu_get_prompt(child),
|
||||||
child->sym->name);
|
child->sym->name);
|
||||||
if (!sym_has_value(child->sym))
|
if (!sym_has_value(child->sym)) {
|
||||||
|
is_new = true;
|
||||||
printf(" (NEW)");
|
printf(" (NEW)");
|
||||||
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
printf("%*schoice", indent - 1, "");
|
printf("%*schoice", indent - 1, "");
|
||||||
@ -586,9 +582,7 @@ static void check_conf(struct menu *menu)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
sym = menu->sym;
|
sym = menu->sym;
|
||||||
if (sym && !sym_has_value(sym) &&
|
if (sym && !sym_has_value(sym) && sym_is_changeable(sym)) {
|
||||||
(sym_is_changeable(sym) || sym_is_choice(sym))) {
|
|
||||||
|
|
||||||
switch (input_mode) {
|
switch (input_mode) {
|
||||||
case listnewconfig:
|
case listnewconfig:
|
||||||
if (sym->name)
|
if (sym->name)
|
||||||
@ -804,8 +798,7 @@ int main(int ac, char **av)
|
|||||||
conf_set_all_new_symbols(def_default);
|
conf_set_all_new_symbols(def_default);
|
||||||
break;
|
break;
|
||||||
case randconfig:
|
case randconfig:
|
||||||
/* Really nothing to do in this loop */
|
conf_set_all_new_symbols(def_random);
|
||||||
while (conf_set_all_new_symbols(def_random)) ;
|
|
||||||
break;
|
break;
|
||||||
case defconfig:
|
case defconfig:
|
||||||
conf_set_all_new_symbols(def_default);
|
conf_set_all_new_symbols(def_default);
|
||||||
|
@ -382,10 +382,7 @@ load:
|
|||||||
|
|
||||||
def_flags = SYMBOL_DEF << def;
|
def_flags = SYMBOL_DEF << def;
|
||||||
for_all_symbols(sym) {
|
for_all_symbols(sym) {
|
||||||
sym->flags |= SYMBOL_CHANGED;
|
|
||||||
sym->flags &= ~(def_flags|SYMBOL_VALID);
|
sym->flags &= ~(def_flags|SYMBOL_VALID);
|
||||||
if (sym_is_choice(sym))
|
|
||||||
sym->flags |= def_flags;
|
|
||||||
switch (sym->type) {
|
switch (sym->type) {
|
||||||
case S_INT:
|
case S_INT:
|
||||||
case S_HEX:
|
case S_HEX:
|
||||||
@ -399,6 +396,8 @@ load:
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (getline_stripped(&line, &line_asize, in) != -1) {
|
while (getline_stripped(&line, &line_asize, in) != -1) {
|
||||||
|
struct menu *choice;
|
||||||
|
|
||||||
conf_lineno++;
|
conf_lineno++;
|
||||||
|
|
||||||
if (!line[0]) /* blank line */
|
if (!line[0]) /* blank line */
|
||||||
@ -460,15 +459,14 @@ load:
|
|||||||
if (conf_set_sym_val(sym, def, def_flags, val))
|
if (conf_set_sym_val(sym, def, def_flags, val))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (sym && sym_is_choice_value(sym)) {
|
/*
|
||||||
struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
|
* If this is a choice member, give it the highest priority.
|
||||||
if (sym->def[def].tri == yes) {
|
* If conflicting CONFIG options are given from an input file,
|
||||||
if (cs->def[def].tri != no)
|
* the last one wins.
|
||||||
conf_warning("override: %s changes choice state", sym->name);
|
*/
|
||||||
cs->def[def].val = sym;
|
choice = sym_get_choice_menu(sym);
|
||||||
cs->def[def].tri = yes;
|
if (choice)
|
||||||
}
|
list_move(&sym->choice_link, &choice->choice_members);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
free(line);
|
free(line);
|
||||||
fclose(in);
|
fclose(in);
|
||||||
@ -514,18 +512,6 @@ int conf_read(const char *name)
|
|||||||
/* maybe print value in verbose mode... */
|
/* maybe print value in verbose mode... */
|
||||||
}
|
}
|
||||||
|
|
||||||
for_all_symbols(sym) {
|
|
||||||
if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
|
|
||||||
/* Reset values of generates values, so they'll appear
|
|
||||||
* as new, if they should become visible, but that
|
|
||||||
* doesn't quite work if the Kconfig and the saved
|
|
||||||
* configuration disagree.
|
|
||||||
*/
|
|
||||||
if (sym->visible == no && !conf_unsaved)
|
|
||||||
sym->flags &= ~SYMBOL_DEF_USER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conf_warnings || conf_unsaved)
|
if (conf_warnings || conf_unsaved)
|
||||||
conf_set_changed(true);
|
conf_set_changed(true);
|
||||||
|
|
||||||
@ -1146,23 +1132,3 @@ void conf_set_changed_callback(void (*fn)(bool))
|
|||||||
{
|
{
|
||||||
conf_changed_callback = fn;
|
conf_changed_callback = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_all_choice_values(struct symbol *csym)
|
|
||||||
{
|
|
||||||
struct property *prop;
|
|
||||||
struct symbol *sym;
|
|
||||||
struct expr *e;
|
|
||||||
|
|
||||||
prop = sym_get_choice_prop(csym);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set all non-assinged choice values to no
|
|
||||||
*/
|
|
||||||
expr_list_for_each_sym(prop->expr, e, sym) {
|
|
||||||
if (!sym_has_value(sym))
|
|
||||||
sym->def[S_DEF_USER].tri = no;
|
|
||||||
}
|
|
||||||
csym->flags |= SYMBOL_DEF_USER;
|
|
||||||
/* clear VALID to get value calculated */
|
|
||||||
csym->flags &= ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES);
|
|
||||||
}
|
|
||||||
|
@ -73,6 +73,8 @@ enum {
|
|||||||
* Represents a configuration symbol.
|
* Represents a configuration symbol.
|
||||||
*
|
*
|
||||||
* Choices are represented as a special kind of symbol with null name.
|
* Choices are represented as a special kind of symbol with null name.
|
||||||
|
*
|
||||||
|
* @choice_link: linked to menu::choice_members
|
||||||
*/
|
*/
|
||||||
struct symbol {
|
struct symbol {
|
||||||
/* link node for the hash table */
|
/* link node for the hash table */
|
||||||
@ -110,6 +112,8 @@ struct symbol {
|
|||||||
/* config entries associated with this symbol */
|
/* config entries associated with this symbol */
|
||||||
struct list_head menus;
|
struct list_head menus;
|
||||||
|
|
||||||
|
struct list_head choice_link;
|
||||||
|
|
||||||
/* SYMBOL_* flags */
|
/* SYMBOL_* flags */
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
@ -133,7 +137,6 @@ struct symbol {
|
|||||||
#define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */
|
#define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */
|
||||||
#define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */
|
#define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */
|
||||||
#define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */
|
#define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */
|
||||||
#define SYMBOL_CHANGED 0x0400 /* ? */
|
|
||||||
#define SYMBOL_WRITTEN 0x0800 /* track info to avoid double-write to .config */
|
#define SYMBOL_WRITTEN 0x0800 /* track info to avoid double-write to .config */
|
||||||
#define SYMBOL_CHECKED 0x2000 /* used during dependency checking */
|
#define SYMBOL_CHECKED 0x2000 /* used during dependency checking */
|
||||||
#define SYMBOL_WARNED 0x8000 /* warning has been issued */
|
#define SYMBOL_WARNED 0x8000 /* warning has been issued */
|
||||||
@ -145,9 +148,6 @@ struct symbol {
|
|||||||
#define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */
|
#define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */
|
||||||
#define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */
|
#define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */
|
||||||
|
|
||||||
/* choice values need to be set before calculating this symbol value */
|
|
||||||
#define SYMBOL_NEED_SET_CHOICE_VALUES 0x100000
|
|
||||||
|
|
||||||
#define SYMBOL_MAXLENGTH 256
|
#define SYMBOL_MAXLENGTH 256
|
||||||
|
|
||||||
/* A property represent the config options that can be associated
|
/* A property represent the config options that can be associated
|
||||||
@ -204,6 +204,8 @@ struct property {
|
|||||||
* for all front ends). Each symbol, menu, etc. defined in the Kconfig files
|
* for all front ends). Each symbol, menu, etc. defined in the Kconfig files
|
||||||
* gets a node. A symbol defined in multiple locations gets one node at each
|
* gets a node. A symbol defined in multiple locations gets one node at each
|
||||||
* location.
|
* location.
|
||||||
|
*
|
||||||
|
* @choice_members: list of choice members with priority.
|
||||||
*/
|
*/
|
||||||
struct menu {
|
struct menu {
|
||||||
/* The next menu node at the same level */
|
/* The next menu node at the same level */
|
||||||
@ -223,6 +225,8 @@ struct menu {
|
|||||||
|
|
||||||
struct list_head link; /* link to symbol::menus */
|
struct list_head link; /* link to symbol::menus */
|
||||||
|
|
||||||
|
struct list_head choice_members;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The prompt associated with the node. This holds the prompt for a
|
* The prompt associated with the node. This holds the prompt for a
|
||||||
* symbol as well as the text for a menu or comment, along with the
|
* symbol as well as the text for a menu or comment, along with the
|
||||||
|
@ -40,7 +40,6 @@ void zconf_nextfile(const char *name);
|
|||||||
/* confdata.c */
|
/* confdata.c */
|
||||||
extern struct gstr autoconf_cmd;
|
extern struct gstr autoconf_cmd;
|
||||||
const char *conf_get_configname(void);
|
const char *conf_get_configname(void);
|
||||||
void set_all_choice_values(struct symbol *csym);
|
|
||||||
|
|
||||||
/* confdata.c and expr.c */
|
/* confdata.c and expr.c */
|
||||||
static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
|
static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
|
||||||
@ -121,11 +120,7 @@ static inline tristate sym_get_tristate_value(struct symbol *sym)
|
|||||||
return sym->curr.tri;
|
return sym->curr.tri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct symbol *sym_get_choice_value(struct symbol *sym);
|
||||||
static inline struct symbol *sym_get_choice_value(struct symbol *sym)
|
|
||||||
{
|
|
||||||
return (struct symbol *)sym->curr.val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool sym_is_choice(struct symbol *sym)
|
static inline bool sym_is_choice(struct symbol *sym)
|
||||||
{
|
{
|
||||||
|
@ -591,7 +591,6 @@ bool menu_is_empty(struct menu *menu)
|
|||||||
|
|
||||||
bool menu_is_visible(struct menu *menu)
|
bool menu_is_visible(struct menu *menu)
|
||||||
{
|
{
|
||||||
struct menu *child;
|
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
tristate visible;
|
tristate visible;
|
||||||
|
|
||||||
@ -610,21 +609,7 @@ bool menu_is_visible(struct menu *menu)
|
|||||||
} else
|
} else
|
||||||
visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
|
visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
|
||||||
|
|
||||||
if (visible != no)
|
return visible != no;
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!sym || sym_get_tristate_value(menu->sym) == no)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (child = menu->list; child; child = child->next) {
|
|
||||||
if (menu_is_visible(child)) {
|
|
||||||
if (sym)
|
|
||||||
sym->flags |= SYMBOL_DEF_USER;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *menu_get_prompt(struct menu *menu)
|
const char *menu_get_prompt(struct menu *menu)
|
||||||
|
@ -157,6 +157,9 @@ config_stmt: config_entry_start config_option_list
|
|||||||
current_entry->filename, current_entry->lineno);
|
current_entry->filename, current_entry->lineno);
|
||||||
yynerrs++;
|
yynerrs++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list_add_tail(¤t_entry->sym->choice_link,
|
||||||
|
¤t_choice->choice_members);
|
||||||
}
|
}
|
||||||
|
|
||||||
printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno);
|
printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno);
|
||||||
@ -240,6 +243,7 @@ choice: T_CHOICE T_EOL
|
|||||||
menu_add_entry(sym);
|
menu_add_entry(sym);
|
||||||
menu_add_expr(P_CHOICE, NULL, NULL);
|
menu_add_expr(P_CHOICE, NULL, NULL);
|
||||||
menu_set_type(S_BOOLEAN);
|
menu_set_type(S_BOOLEAN);
|
||||||
|
INIT_LIST_HEAD(¤t_entry->choice_members);
|
||||||
|
|
||||||
printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno);
|
printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno);
|
||||||
};
|
};
|
||||||
|
@ -188,7 +188,6 @@ static void sym_set_changed(struct symbol *sym)
|
|||||||
{
|
{
|
||||||
struct menu *menu;
|
struct menu *menu;
|
||||||
|
|
||||||
sym->flags |= SYMBOL_CHANGED;
|
|
||||||
list_for_each_entry(menu, &sym->menus, link)
|
list_for_each_entry(menu, &sym->menus, link)
|
||||||
menu->flags |= MENU_CHANGED;
|
menu->flags |= MENU_CHANGED;
|
||||||
}
|
}
|
||||||
@ -282,36 +281,95 @@ struct symbol *sym_choice_default(struct symbol *sym)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct symbol *sym_calc_choice(struct symbol *sym)
|
/*
|
||||||
|
* sym_calc_choice - calculate symbol values in a choice
|
||||||
|
*
|
||||||
|
* @choice: a menu of the choice
|
||||||
|
*
|
||||||
|
* Return: a chosen symbol
|
||||||
|
*/
|
||||||
|
static struct symbol *sym_calc_choice(struct menu *choice)
|
||||||
{
|
{
|
||||||
struct symbol *def_sym;
|
struct symbol *res = NULL;
|
||||||
struct property *prop;
|
struct symbol *sym;
|
||||||
struct expr *e;
|
struct menu *menu;
|
||||||
int flags;
|
|
||||||
|
|
||||||
/* first calculate all choice values' visibilities */
|
/* Traverse the list of choice members in the priority order. */
|
||||||
flags = sym->flags;
|
list_for_each_entry(sym, &choice->choice_members, choice_link) {
|
||||||
prop = sym_get_choice_prop(sym);
|
sym_calc_visibility(sym);
|
||||||
expr_list_for_each_sym(prop->expr, e, def_sym) {
|
if (sym->visible == no)
|
||||||
sym_calc_visibility(def_sym);
|
continue;
|
||||||
if (def_sym->visible != no)
|
|
||||||
flags &= def_sym->flags;
|
/* The first visible symble with the user value 'y'. */
|
||||||
|
if (sym_has_value(sym) && sym->def[S_DEF_USER].tri == yes) {
|
||||||
|
res = sym;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sym->flags &= flags | ~SYMBOL_DEF_USER;
|
/*
|
||||||
|
* If 'y' is not found in the user input, use the default, unless it is
|
||||||
|
* explicitly set to 'n'.
|
||||||
|
*/
|
||||||
|
if (!res) {
|
||||||
|
res = sym_choice_default(choice->sym);
|
||||||
|
if (res && sym_has_value(res) && res->def[S_DEF_USER].tri == no)
|
||||||
|
res = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* is the user choice visible? */
|
/* Still not found. Pick up the first visible, user-unspecified symbol. */
|
||||||
def_sym = sym->def[S_DEF_USER].val;
|
if (!res) {
|
||||||
if (def_sym && def_sym->visible != no)
|
menu_for_each_sub_entry(menu, choice) {
|
||||||
return def_sym;
|
sym = menu->sym;
|
||||||
|
|
||||||
def_sym = sym_choice_default(sym);
|
if (!sym || sym->visible == no || sym_has_value(sym))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (def_sym == NULL)
|
res = sym;
|
||||||
/* no choice? reset tristate value */
|
break;
|
||||||
sym->curr.tri = no;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return def_sym;
|
/*
|
||||||
|
* Still not found. Traverse the linked list in the _reverse_ order to
|
||||||
|
* pick up the least prioritized 'n'.
|
||||||
|
*/
|
||||||
|
if (!res) {
|
||||||
|
list_for_each_entry_reverse(sym, &choice->choice_members,
|
||||||
|
choice_link) {
|
||||||
|
if (sym->visible == no)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
res = sym;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menu_for_each_sub_entry(menu, choice) {
|
||||||
|
tristate val;
|
||||||
|
|
||||||
|
sym = menu->sym;
|
||||||
|
|
||||||
|
if (!sym || sym->visible == no)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
val = sym == res ? yes : no;
|
||||||
|
|
||||||
|
if (sym->curr.tri != val)
|
||||||
|
sym_set_changed(sym);
|
||||||
|
|
||||||
|
sym->curr.tri = val;
|
||||||
|
sym->flags |= SYMBOL_VALID | SYMBOL_WRITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct symbol *sym_get_choice_value(struct symbol *sym)
|
||||||
|
{
|
||||||
|
struct menu *menu = list_first_entry(&sym->menus, struct menu, link);
|
||||||
|
|
||||||
|
return sym_calc_choice(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sym_warn_unmet_dep(struct symbol *sym)
|
static void sym_warn_unmet_dep(struct symbol *sym)
|
||||||
@ -347,7 +405,7 @@ void sym_calc_value(struct symbol *sym)
|
|||||||
{
|
{
|
||||||
struct symbol_value newval, oldval;
|
struct symbol_value newval, oldval;
|
||||||
struct property *prop;
|
struct property *prop;
|
||||||
struct expr *e;
|
struct menu *choice_menu;
|
||||||
|
|
||||||
if (!sym)
|
if (!sym)
|
||||||
return;
|
return;
|
||||||
@ -355,13 +413,6 @@ void sym_calc_value(struct symbol *sym)
|
|||||||
if (sym->flags & SYMBOL_VALID)
|
if (sym->flags & SYMBOL_VALID)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (sym_is_choice_value(sym) &&
|
|
||||||
sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) {
|
|
||||||
sym->flags &= ~SYMBOL_NEED_SET_CHOICE_VALUES;
|
|
||||||
prop = sym_get_choice_prop(sym);
|
|
||||||
sym_calc_value(prop_get_symbol(prop));
|
|
||||||
}
|
|
||||||
|
|
||||||
sym->flags |= SYMBOL_VALID;
|
sym->flags |= SYMBOL_VALID;
|
||||||
|
|
||||||
oldval = sym->curr;
|
oldval = sym->curr;
|
||||||
@ -400,9 +451,11 @@ void sym_calc_value(struct symbol *sym)
|
|||||||
switch (sym_get_type(sym)) {
|
switch (sym_get_type(sym)) {
|
||||||
case S_BOOLEAN:
|
case S_BOOLEAN:
|
||||||
case S_TRISTATE:
|
case S_TRISTATE:
|
||||||
if (sym_is_choice_value(sym) && sym->visible == yes) {
|
choice_menu = sym_get_choice_menu(sym);
|
||||||
prop = sym_get_choice_prop(sym);
|
|
||||||
newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no;
|
if (choice_menu) {
|
||||||
|
sym_calc_choice(choice_menu);
|
||||||
|
newval.tri = sym->curr.tri;
|
||||||
} else {
|
} else {
|
||||||
if (sym->visible != no) {
|
if (sym->visible != no) {
|
||||||
/* if the symbol is visible use the user value
|
/* if the symbol is visible use the user value
|
||||||
@ -461,8 +514,6 @@ void sym_calc_value(struct symbol *sym)
|
|||||||
}
|
}
|
||||||
|
|
||||||
sym->curr = newval;
|
sym->curr = newval;
|
||||||
if (sym_is_choice(sym) && newval.tri == yes)
|
|
||||||
sym->curr.val = sym_calc_choice(sym);
|
|
||||||
sym_validate_range(sym);
|
sym_validate_range(sym);
|
||||||
|
|
||||||
if (memcmp(&oldval, &sym->curr, sizeof(oldval))) {
|
if (memcmp(&oldval, &sym->curr, sizeof(oldval))) {
|
||||||
@ -473,23 +524,8 @@ void sym_calc_value(struct symbol *sym)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sym_is_choice(sym)) {
|
if (sym_is_choice(sym))
|
||||||
struct symbol *choice_sym;
|
|
||||||
|
|
||||||
prop = sym_get_choice_prop(sym);
|
|
||||||
expr_list_for_each_sym(prop->expr, e, choice_sym) {
|
|
||||||
if ((sym->flags & SYMBOL_WRITE) &&
|
|
||||||
choice_sym->visible != no)
|
|
||||||
choice_sym->flags |= SYMBOL_WRITE;
|
|
||||||
if (sym->flags & SYMBOL_CHANGED)
|
|
||||||
sym_set_changed(choice_sym);
|
|
||||||
}
|
|
||||||
|
|
||||||
sym->flags &= ~SYMBOL_WRITE;
|
sym->flags &= ~SYMBOL_WRITE;
|
||||||
}
|
|
||||||
|
|
||||||
if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES)
|
|
||||||
set_all_choice_values(sym);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sym_clear_all_valid(void)
|
void sym_clear_all_valid(void)
|
||||||
@ -523,15 +559,15 @@ bool sym_set_tristate_value(struct symbol *sym, tristate val)
|
|||||||
{
|
{
|
||||||
tristate oldval = sym_get_tristate_value(sym);
|
tristate oldval = sym_get_tristate_value(sym);
|
||||||
|
|
||||||
if (oldval != val && !sym_tristate_within_range(sym, val))
|
if (!sym_tristate_within_range(sym, val))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!(sym->flags & SYMBOL_DEF_USER)) {
|
if (!(sym->flags & SYMBOL_DEF_USER) || sym->def[S_DEF_USER].tri != val) {
|
||||||
|
sym->def[S_DEF_USER].tri = val;
|
||||||
sym->flags |= SYMBOL_DEF_USER;
|
sym->flags |= SYMBOL_DEF_USER;
|
||||||
sym_set_changed(sym);
|
sym_set_changed(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
sym->def[S_DEF_USER].tri = val;
|
|
||||||
if (oldval != val)
|
if (oldval != val)
|
||||||
sym_clear_all_valid();
|
sym_clear_all_valid();
|
||||||
|
|
||||||
@ -565,10 +601,17 @@ void choice_set_value(struct menu *choice, struct symbol *sym)
|
|||||||
|
|
||||||
menu->sym->def[S_DEF_USER].tri = val;
|
menu->sym->def[S_DEF_USER].tri = val;
|
||||||
menu->sym->flags |= SYMBOL_DEF_USER;
|
menu->sym->flags |= SYMBOL_DEF_USER;
|
||||||
}
|
|
||||||
|
|
||||||
choice->sym->def[S_DEF_USER].val = sym;
|
/*
|
||||||
choice->sym->flags |= SYMBOL_DEF_USER;
|
* Now, the user has explicitly enabled or disabled this symbol,
|
||||||
|
* it should be given the highest priority. We are possibly
|
||||||
|
* setting multiple symbols to 'n', where the first symbol is
|
||||||
|
* given the least prioritized 'n'. This works well when the
|
||||||
|
* choice block ends up with selecting 'n' symbol.
|
||||||
|
* (see sym_calc_choice())
|
||||||
|
*/
|
||||||
|
list_move(&menu->sym->choice_link, &choice->choice_members);
|
||||||
|
}
|
||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
sym_clear_all_valid();
|
sym_clear_all_valid();
|
||||||
|
Loading…
Reference in New Issue
Block a user