forked from mirrors/linux
		
	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
					
				
					 7 changed files with 187 additions and 197 deletions
				
			
		|  | @ -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 @@ int conf_read_simple(const char *name, int def) | ||||||
| 
 | 
 | ||||||
| 	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 @@ int conf_read_simple(const char *name, int def) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	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 @@ int conf_read_simple(const char *name, int def) | ||||||
| 		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 a new issue
	
	 Masahiro Yamada
						Masahiro Yamada