summaryrefslogtreecommitdiffstats
path: root/scripts/kconfig
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/kconfig')
-rw-r--r--scripts/kconfig/Makefile8
-rw-r--r--scripts/kconfig/conf.c16
-rw-r--r--scripts/kconfig/confdata.c59
-rw-r--r--scripts/kconfig/expr.c2
-rw-r--r--scripts/kconfig/expr.h11
-rw-r--r--scripts/kconfig/gconf.c80
-rw-r--r--scripts/kconfig/lexer.l1
-rw-r--r--scripts/kconfig/lkc.h13
-rw-r--r--scripts/kconfig/lkc_proto.h1
-rw-r--r--scripts/kconfig/lxdialog/util.c9
-rw-r--r--scripts/kconfig/mconf.c104
-rw-r--r--scripts/kconfig/menu.c51
-rw-r--r--scripts/kconfig/nconf.c118
-rw-r--r--scripts/kconfig/parser.y76
-rw-r--r--scripts/kconfig/symbol.c73
-rw-r--r--scripts/kconfig/tests/choice/Kconfig26
-rw-r--r--scripts/kconfig/tests/choice/__init__.py2
-rw-r--r--scripts/kconfig/tests/choice/allmod_expected_config4
-rw-r--r--scripts/kconfig/tests/choice/allyes_expected_config4
-rw-r--r--scripts/kconfig/tests/choice/oldask0_expected_stdout2
-rw-r--r--scripts/kconfig/tests/choice/oldask1_config1
-rw-r--r--scripts/kconfig/tests/choice/oldask1_expected_stdout6
22 files changed, 272 insertions, 395 deletions
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index ea1bf3b3db..a0a0be38cb 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -57,7 +57,7 @@ $(foreach c, config menuconfig nconfig gconfig xconfig, $(eval $(call config_rul
PHONY += localmodconfig localyesconfig
localyesconfig localmodconfig: $(obj)/conf
- $(Q)$(PERL) $(srctree)/$(src)/streamline_config.pl --$@ $(srctree) $(Kconfig) > .tmp.config
+ $(Q)$(PERL) $(src)/streamline_config.pl --$@ $(srctree) $(Kconfig) > .tmp.config
$(Q)if [ -f .config ]; then \
cmp -s .tmp.config .config || \
(mv -f .config .config.old.1; \
@@ -118,7 +118,7 @@ tinyconfig:
# CHECK: -o cache_dir=<path> working?
PHONY += testconfig
testconfig: $(obj)/conf
- $(Q)$(PYTHON3) -B -m pytest $(srctree)/$(src)/tests \
+ $(Q)$(PYTHON3) -B -m pytest $(src)/tests \
-o cache_dir=$(abspath $(obj)/tests/.cache) \
$(if $(findstring 1,$(KBUILD_VERBOSE)),--capture=no)
clean-files += tests/.cache
@@ -165,8 +165,8 @@ common-objs := confdata.o expr.o lexer.lex.o menu.o parser.tab.o \
preprocess.o symbol.o util.o
$(obj)/lexer.lex.o: $(obj)/parser.tab.h
-HOSTCFLAGS_lexer.lex.o := -I $(srctree)/$(src)
-HOSTCFLAGS_parser.tab.o := -I $(srctree)/$(src)
+HOSTCFLAGS_lexer.lex.o := -I $(src)
+HOSTCFLAGS_parser.tab.o := -I $(src)
# conf: Used for defconfig, oldconfig and related targets
hostprogs += conf
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index 965bb40c50..8ad2c52d9b 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -446,7 +446,7 @@ help:
}
}
-static int conf_choice(struct menu *menu)
+static void conf_choice(struct menu *menu)
{
struct symbol *sym, *def_sym;
struct menu *child;
@@ -459,19 +459,18 @@ static int conf_choice(struct menu *menu)
sym_calc_value(sym);
switch (sym_get_tristate_value(sym)) {
case no:
- return 1;
case mod:
- return 0;
+ return;
case yes:
break;
}
} else {
switch (sym_get_tristate_value(sym)) {
case no:
- return 1;
+ return;
case mod:
printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
- return 0;
+ return;
case yes:
break;
}
@@ -497,9 +496,8 @@ static int conf_choice(struct menu *menu)
printf("%*c", indent, '>');
} else
printf("%*c", indent, ' ');
- printf(" %d. %s", cnt, menu_get_prompt(child));
- if (child->sym->name)
- printf(" (%s)", child->sym->name);
+ printf(" %d. %s (%s)", cnt, menu_get_prompt(child),
+ child->sym->name);
if (!sym_has_value(child->sym))
printf(" (NEW)");
printf("\n");
@@ -552,7 +550,7 @@ static int conf_choice(struct menu *menu)
continue;
}
sym_set_tristate_value(child->sym, yes);
- return 1;
+ return;
}
}
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index 0e35c4819c..85b53069ba 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -502,7 +502,7 @@ int conf_read(const char *name)
for_all_symbols(sym) {
sym_calc_value(sym);
- if (sym_is_choice(sym) || (sym->flags & SYMBOL_NO_WRITE))
+ if (sym_is_choice(sym))
continue;
if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) {
/* check that calculated value agrees with saved value */
@@ -533,19 +533,6 @@ int conf_read(const char *name)
*/
if (sym->visible == no && !conf_unsaved)
sym->flags &= ~SYMBOL_DEF_USER;
- switch (sym->type) {
- case S_STRING:
- case S_INT:
- case S_HEX:
- /* Reset a string value if it's out of range */
- if (sym_string_within_range(sym, sym->def[S_DEF_USER].val))
- break;
- sym->flags &= ~SYMBOL_VALID;
- conf_unsaved++;
- break;
- default:
- break;
- }
}
}
@@ -793,59 +780,39 @@ int conf_write_defconfig(const char *filename)
sym_clear_all_valid();
- /* Traverse all menus to find all relevant symbols */
- menu = rootmenu.list;
+ menu_for_each_entry(menu) {
+ struct menu *choice;
- while (menu != NULL)
- {
sym = menu->sym;
if (sym && !sym_is_choice(sym)) {
sym_calc_value(sym);
if (!(sym->flags & SYMBOL_WRITE))
- goto next_menu;
+ continue;
sym->flags &= ~SYMBOL_WRITE;
/* If we cannot change the symbol - skip */
if (!sym_is_changeable(sym))
- goto next_menu;
+ continue;
/* If symbol equals to default value - skip */
if (strcmp(sym_get_string_value(sym), sym_get_string_default(sym)) == 0)
- goto next_menu;
+ continue;
/*
* If symbol is a choice value and equals to the
* default for a choice - skip.
- * But only if value is bool and equal to "y" and
- * choice is not "optional".
- * (If choice is "optional" then all values can be "n")
*/
- if (sym_is_choice_value(sym)) {
- struct symbol *cs;
+ choice = sym_get_choice_menu(sym);
+ if (choice) {
struct symbol *ds;
- cs = prop_get_symbol(sym_get_choice_prop(sym));
- ds = sym_choice_default(cs);
- if (!sym_is_optional(cs) && sym == ds) {
+ ds = sym_choice_default(choice->sym);
+ if (sym == ds) {
if ((sym->type == S_BOOLEAN) &&
sym_get_tristate_value(sym) == yes)
- goto next_menu;
+ continue;
}
}
print_symbol_for_dotconfig(out, sym);
}
-next_menu:
- if (menu->list != NULL) {
- menu = menu->list;
- }
- else if (menu->next != NULL) {
- menu = menu->next;
- } else {
- while ((menu = menu->parent)) {
- if (menu->next != NULL) {
- menu = menu->next;
- break;
- }
- }
- }
}
fclose(out);
return 0;
@@ -906,7 +873,7 @@ int conf_write(const char *name)
"# %s\n"
"#\n", str);
need_newline = false;
- } else if (!(sym->flags & SYMBOL_CHOICE) &&
+ } else if (!sym_is_choice(sym) &&
!(sym->flags & SYMBOL_WRITTEN)) {
sym_calc_value(sym);
if (!(sym->flags & SYMBOL_WRITE))
@@ -1028,7 +995,7 @@ static int conf_touch_deps(void)
for_all_symbols(sym) {
sym_calc_value(sym);
- if ((sym->flags & SYMBOL_NO_WRITE) || !sym->name)
+ if (sym_is_choice(sym))
continue;
if (sym->flags & SYMBOL_WRITE) {
if (sym->flags & SYMBOL_DEF_AUTO) {
diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c
index 7866167552..fcc190b67b 100644
--- a/scripts/kconfig/expr.c
+++ b/scripts/kconfig/expr.c
@@ -447,7 +447,7 @@ static struct expr *expr_join_or(struct expr *e1, struct expr *e2)
return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes);
}
}
- if (sym1->type == S_BOOLEAN && sym1 == sym2) {
+ if (sym1->type == S_BOOLEAN) {
if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) ||
(e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL))
return expr_alloc_symbol(&symbol_yes);
diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h
index f015883519..7c0c242318 100644
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -72,8 +72,7 @@ enum {
/*
* Represents a configuration symbol.
*
- * Choices are represented as a special kind of symbol and have the
- * SYMBOL_CHOICE bit set in 'flags'.
+ * Choices are represented as a special kind of symbol with null name.
*/
struct symbol {
/* link node for the hash table */
@@ -131,14 +130,11 @@ struct symbol {
#define SYMBOL_CONST 0x0001 /* symbol is const */
#define SYMBOL_CHECK 0x0008 /* used during dependency checking */
-#define SYMBOL_CHOICE 0x0010 /* start of a choice block (null name) */
#define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */
#define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */
-#define SYMBOL_OPTIONAL 0x0100 /* choice is optional - values can be 'n' */
#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_NO_WRITE 0x1000 /* Symbol for internal use only; it will not be written */
#define SYMBOL_CHECKED 0x2000 /* used during dependency checking */
#define SYMBOL_WARNED 0x8000 /* warning has been issued */
@@ -305,11 +301,6 @@ static inline int expr_is_yes(struct expr *e)
return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes);
}
-static inline int expr_is_no(struct expr *e)
-{
- return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no);
-}
-
#ifdef __cplusplus
}
#endif
diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c
index 9e52c7360e..e04dbafd3a 100644
--- a/scripts/kconfig/gconf.c
+++ b/scripts/kconfig/gconf.c
@@ -18,8 +18,6 @@
#include <unistd.h>
#include <time.h>
-//#define DEBUG
-
enum {
SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW
};
@@ -71,39 +69,6 @@ static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row);
static gchar **fill_row(struct menu *menu);
static void conf_changed(void);
-/* Helping/Debugging Functions */
-#ifdef DEBUG
-static const char *dbg_sym_flags(int val)
-{
- static char buf[256];
-
- bzero(buf, 256);
-
- if (val & SYMBOL_CONST)
- strcat(buf, "const/");
- if (val & SYMBOL_CHECK)
- strcat(buf, "check/");
- if (val & SYMBOL_CHOICE)
- strcat(buf, "choice/");
- if (val & SYMBOL_CHOICEVAL)
- strcat(buf, "choiceval/");
- if (val & SYMBOL_VALID)
- strcat(buf, "valid/");
- if (val & SYMBOL_OPTIONAL)
- strcat(buf, "optional/");
- if (val & SYMBOL_WRITE)
- strcat(buf, "write/");
- if (val & SYMBOL_CHANGED)
- strcat(buf, "changed/");
- if (val & SYMBOL_NO_WRITE)
- strcat(buf, "no_write/");
-
- buf[strlen(buf) - 1] = '\0';
-
- return buf;
-}
-#endif
-
static void replace_button_icon(GladeXML *xml, GdkDrawable *window,
GtkStyle *style, gchar *btn_name, gchar **xpm)
{
@@ -493,7 +458,7 @@ load_filename(GtkFileSelection * file_selector, gpointer user_data)
if (conf_read(fn))
text_insert_msg("Error", "Unable to load configuration !");
else
- display_tree(&rootmenu);
+ display_tree_part();
}
void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data)
@@ -1082,15 +1047,13 @@ static gchar **fill_row(struct menu *menu)
row[COL_NAME] = g_strdup(sym->name);
sym_calc_value(sym);
- sym->flags &= ~SYMBOL_CHANGED;
+ menu->flags &= ~MENU_CHANGED;
if (sym_is_choice(sym)) { // parse childs for getting final value
struct menu *child;
struct symbol *def_sym = sym_get_choice_value(sym);
struct menu *def_menu = NULL;
- row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
-
for (child = menu->list; child; child = child->next) {
if (menu_is_visible(child)
&& child->sym == def_sym)
@@ -1100,6 +1063,11 @@ static gchar **fill_row(struct menu *menu)
if (def_menu)
row[COL_VALUE] =
g_strdup(menu_get_prompt(def_menu));
+
+ if (sym_get_type(sym) == S_BOOLEAN) {
+ row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
+ return row;
+ }
}
if (sym->flags & SYMBOL_CHOICEVAL)
row[COL_BTNRAD] = GINT_TO_POINTER(TRUE);
@@ -1107,11 +1075,6 @@ static gchar **fill_row(struct menu *menu)
stype = sym_get_type(sym);
switch (stype) {
case S_BOOLEAN:
- if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE)
- row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
- if (sym_is_choice(sym))
- break;
- /* fall through */
case S_TRISTATE:
val = sym_get_tristate_value(sym);
switch (val) {
@@ -1268,12 +1231,6 @@ static void update_tree(struct menu *src, GtkTreeIter * dst)
else
menu2 = NULL; // force adding of a first child
-#ifdef DEBUG
- printf("%*c%s | %s\n", indent, ' ',
- menu1 ? menu_get_prompt(menu1) : "nil",
- menu2 ? menu_get_prompt(menu2) : "nil");
-#endif
-
if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) ||
(opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) ||
(opt_mode == OPT_ALL && !menu_get_prompt(child1))) {
@@ -1314,7 +1271,7 @@ static void update_tree(struct menu *src, GtkTreeIter * dst)
else
goto reparse; // next child
}
- } else if (sym && (sym->flags & SYMBOL_CHANGED)) {
+ } else if (sym && (child1->flags & MENU_CHANGED)) {
set_node(child2, menu1, fill_row(menu1));
}
@@ -1330,7 +1287,6 @@ static void update_tree(struct menu *src, GtkTreeIter * dst)
/* Display the whole tree (single/split/full view) */
static void display_tree(struct menu *menu)
{
- struct symbol *sym;
struct property *prop;
struct menu *child;
enum prop_type ptype;
@@ -1342,11 +1298,9 @@ static void display_tree(struct menu *menu)
for (child = menu->list; child; child = child->next) {
prop = child->prompt;
- sym = child->sym;
ptype = prop ? prop->type : P_UNKNOWN;
- if (sym)
- sym->flags &= ~SYMBOL_CHANGED;
+ menu->flags &= ~MENU_CHANGED;
if ((view_mode == SPLIT_VIEW)
&& !(child->flags & MENU_ROOT) && (tree == tree1))
@@ -1360,19 +1314,7 @@ static void display_tree(struct menu *menu)
(opt_mode == OPT_PROMPT && menu_has_prompt(child)) ||
(opt_mode == OPT_ALL && menu_get_prompt(child)))
place_node(child, fill_row(child));
-#ifdef DEBUG
- printf("%*c%s: ", indent, ' ', menu_get_prompt(child));
- printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : "");
- printf("%s", prop_get_type_name(ptype));
- printf(" | ");
- if (sym) {
- printf("%s", sym_type_name(sym->type));
- printf(" | ");
- printf("%s", dbg_sym_flags(sym->flags));
- printf("\n");
- } else
- printf("\n");
-#endif
+
if ((view_mode != FULL_VIEW) && (ptype == P_MENU)
&& (tree == tree2))
continue;
@@ -1405,6 +1347,8 @@ static void display_tree_part(void)
display_tree(current);
else if (view_mode == SPLIT_VIEW)
display_tree(browsed);
+ else if (view_mode == FULL_VIEW)
+ display_tree(&rootmenu);
gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
}
diff --git a/scripts/kconfig/lexer.l b/scripts/kconfig/lexer.l
index 89544c3a1a..8dd597c471 100644
--- a/scripts/kconfig/lexer.l
+++ b/scripts/kconfig/lexer.l
@@ -120,7 +120,6 @@ n [A-Za-z0-9_-]
"menuconfig" return T_MENUCONFIG;
"modules" return T_MODULES;
"on" return T_ON;
-"optional" return T_OPTIONAL;
"prompt" return T_PROMPT;
"range" return T_RANGE;
"select" return T_SELECT;
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h
index e7cc9e985c..64dfc354dd 100644
--- a/scripts/kconfig/lkc.h
+++ b/scripts/kconfig/lkc.h
@@ -79,6 +79,11 @@ void str_printf(struct gstr *gs, const char *fmt, ...);
char *str_get(struct gstr *gs);
/* menu.c */
+struct menu *menu_next(struct menu *menu, struct menu *root);
+#define menu_for_each_sub_entry(menu, root) \
+ for (menu = menu_next(root, root); menu; menu = menu_next(menu, root))
+#define menu_for_each_entry(menu) \
+ menu_for_each_sub_entry(menu, &rootmenu)
void _menu_init(void);
void menu_warn(struct menu *menu, const char *fmt, ...);
struct menu *menu_add_menu(void);
@@ -124,7 +129,8 @@ static inline struct symbol *sym_get_choice_value(struct symbol *sym)
static inline bool sym_is_choice(struct symbol *sym)
{
- return sym->flags & SYMBOL_CHOICE ? true : false;
+ /* A choice is a symbol with no name */
+ return sym->name == NULL;
}
static inline bool sym_is_choice_value(struct symbol *sym)
@@ -132,11 +138,6 @@ static inline bool sym_is_choice_value(struct symbol *sym)
return sym->flags & SYMBOL_CHOICEVAL ? true : false;
}
-static inline bool sym_is_optional(struct symbol *sym)
-{
- return sym->flags & SYMBOL_OPTIONAL ? true : false;
-}
-
static inline bool sym_has_value(struct symbol *sym)
{
return sym->flags & SYMBOL_DEF_USER ? true : false;
diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h
index 2807fa584c..d76aaf4ea1 100644
--- a/scripts/kconfig/lkc_proto.h
+++ b/scripts/kconfig/lkc_proto.h
@@ -34,6 +34,7 @@ bool sym_string_within_range(struct symbol *sym, const char *str);
bool sym_set_string_value(struct symbol *sym, const char *newval);
bool sym_is_changeable(struct symbol *sym);
struct property * sym_get_choice_prop(struct symbol *sym);
+struct menu *sym_get_choice_menu(struct symbol *sym);
const char * sym_get_string_value(struct symbol *sym);
const char * prop_get_type_name(enum prop_type type);
diff --git a/scripts/kconfig/lxdialog/util.c b/scripts/kconfig/lxdialog/util.c
index f18e2a89f6..964139c87f 100644
--- a/scripts/kconfig/lxdialog/util.c
+++ b/scripts/kconfig/lxdialog/util.c
@@ -17,22 +17,13 @@ struct dialog_info dlg;
static void set_mono_theme(void)
{
- dlg.screen.atr = A_NORMAL;
- dlg.shadow.atr = A_NORMAL;
- dlg.dialog.atr = A_NORMAL;
dlg.title.atr = A_BOLD;
- dlg.border.atr = A_NORMAL;
dlg.button_active.atr = A_REVERSE;
dlg.button_inactive.atr = A_DIM;
dlg.button_key_active.atr = A_REVERSE;
dlg.button_key_inactive.atr = A_BOLD;
dlg.button_label_active.atr = A_REVERSE;
- dlg.button_label_inactive.atr = A_NORMAL;
- dlg.inputbox.atr = A_NORMAL;
dlg.position_indicator.atr = A_BOLD;
- dlg.menubox.atr = A_NORMAL;
- dlg.menubox_border.atr = A_NORMAL;
- dlg.item.atr = A_NORMAL;
dlg.item_selected.atr = A_REVERSE;
dlg.tag.atr = A_BOLD;
dlg.tag_selected.atr = A_REVERSE;
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index c096909744..d6a61ca1a9 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -525,19 +525,12 @@ static void build_conf(struct menu *menu)
val = sym_get_tristate_value(sym);
if (sym_is_changeable(sym)) {
- switch (type) {
- case S_BOOLEAN:
- item_make("[%c]", val == no ? ' ' : '*');
- break;
- case S_TRISTATE:
- switch (val) {
- case yes: ch = '*'; break;
- case mod: ch = 'M'; break;
- default: ch = ' '; break;
- }
- item_make("<%c>", ch);
- break;
+ switch (val) {
+ case yes: ch = '*'; break;
+ case mod: ch = 'M'; break;
+ default: ch = ' '; break;
}
+ item_make("<%c>", ch);
item_set_tag('t');
item_set_data(menu);
} else {
@@ -548,15 +541,8 @@ static void build_conf(struct menu *menu)
item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
if (val == yes) {
- if (def_menu) {
- item_add_str(" (%s)", menu_get_prompt(def_menu));
- item_add_str(" --->");
- if (def_menu->list) {
- indent += 2;
- build_conf(def_menu);
- indent -= 2;
- }
- }
+ if (def_menu)
+ item_add_str(" (%s) --->", menu_get_prompt(def_menu));
return;
}
} else {
@@ -568,49 +554,43 @@ static void build_conf(struct menu *menu)
}
child_count++;
val = sym_get_tristate_value(sym);
- if (sym_is_choice_value(sym) && val == yes) {
- item_make(" ");
- item_set_tag(':');
+ switch (type) {
+ case S_BOOLEAN:
+ if (sym_is_changeable(sym))
+ item_make("[%c]", val == no ? ' ' : '*');
+ else
+ item_make("-%c-", val == no ? ' ' : '*');
+ item_set_tag('t');
item_set_data(menu);
- } else {
- switch (type) {
- case S_BOOLEAN:
- if (sym_is_changeable(sym))
- item_make("[%c]", val == no ? ' ' : '*');
- else
- item_make("-%c-", val == no ? ' ' : '*');
- item_set_tag('t');
- item_set_data(menu);
- break;
- case S_TRISTATE:
- switch (val) {
- case yes: ch = '*'; break;
- case mod: ch = 'M'; break;
- default: ch = ' '; break;
- }
- if (sym_is_changeable(sym)) {
- if (sym->rev_dep.tri == mod)
- item_make("{%c}", ch);
- else
- item_make("<%c>", ch);
- } else
- item_make("-%c-", ch);
- item_set_tag('t');
- item_set_data(menu);
- break;
- default:
- tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
- item_make("(%s)", sym_get_string_value(sym));
- tmp = indent - tmp + 4;
- if (tmp < 0)
- tmp = 0;
- item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
- (sym_has_value(sym) || !sym_is_changeable(sym)) ?
- "" : " (NEW)");
- item_set_tag('s');
- item_set_data(menu);
- goto conf_childs;
+ break;
+ case S_TRISTATE:
+ switch (val) {
+ case yes: ch = '*'; break;
+ case mod: ch = 'M'; break;
+ default: ch = ' '; break;
}
+ if (sym_is_changeable(sym)) {
+ if (sym->rev_dep.tri == mod)
+ item_make("{%c}", ch);
+ else
+ item_make("<%c>", ch);
+ } else
+ item_make("-%c-", ch);
+ item_set_tag('t');
+ item_set_data(menu);
+ break;
+ default:
+ tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
+ item_make("(%s)", sym_get_string_value(sym));
+ tmp = indent - tmp + 4;
+ if (tmp < 0)
+ tmp = 0;
+ item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
+ (sym_has_value(sym) || !sym_is_changeable(sym)) ?
+ "" : " (NEW)");
+ item_set_tag('s');
+ item_set_data(menu);
+ goto conf_childs;
}
item_add_str("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
(sym_has_value(sym) || !sym_is_changeable(sym)) ?
diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index 8b48a80e7e..eef9b63cdf 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -17,6 +17,27 @@ static const char nohelp_text[] = "There is no help available for this option.";
struct menu rootmenu;
static struct menu **last_entry_ptr;
+/**
+ * menu_next - return the next menu entry with depth-first traversal
+ * @menu: pointer to the current menu
+ * @root: root of the sub-tree to traverse. If NULL is given, the traveral
+ * continues until it reaches the end of the entire menu tree.
+ * return: the menu to visit next, or NULL when it reaches the end.
+ */
+struct menu *menu_next(struct menu *menu, struct menu *root)
+{
+ if (menu->list)
+ return menu->list;
+
+ while (menu != root && !menu->next)
+ menu = menu->parent;
+
+ if (menu == root)
+ return NULL;
+
+ return menu->next;
+}
+
void menu_warn(struct menu *menu, const char *fmt, ...)
{
va_list ap;
@@ -242,11 +263,9 @@ static void sym_check_prop(struct symbol *sym)
sym->name);
}
if (sym_is_choice(sym)) {
- struct property *choice_prop =
- sym_get_choice_prop(sym2);
+ struct menu *choice = sym_get_choice_menu(sym2);
- if (!choice_prop ||
- prop_get_symbol(choice_prop) != sym)
+ if (!choice || choice->sym != sym)
prop_warn(prop,
"choice default symbol '%s' is not contained in the choice",
sym2->name);
@@ -484,18 +503,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
menu->sym && !sym_is_choice_value(menu->sym)) {
current_entry = menu;
menu->sym->flags |= SYMBOL_CHOICEVAL;
- if (!menu->prompt)
- menu_warn(menu, "choice value must have a prompt");
- for (prop = menu->sym->prop; prop; prop = prop->next) {
- if (prop->type == P_DEFAULT)
- prop_warn(prop, "defaults for choice "
- "values not supported");
- if (prop->menu == menu)
- continue;
- if (prop->type == P_PROMPT &&
- prop->menu->parent->sym != sym)
- prop_warn(prop, "choice value used outside its choice group");
- }
/* Non-tristate choice values of tristate choices must
* depend on the choice being set to Y. The choice
* values' dependencies were propagated to their
@@ -570,15 +577,11 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
}
/*
- * For non-optional choices, add a reverse dependency (corresponding to
- * a select) of '<visibility> && m'. This prevents the user from
- * setting the choice mode to 'n' when the choice is visible.
- *
- * This would also work for non-choice symbols, but only non-optional
- * choices clear SYMBOL_OPTIONAL as of writing. Choices are implemented
- * as a type of symbol.
+ * For choices, add a reverse dependency (corresponding to a select) of
+ * '<visibility> && m'. This prevents the user from setting the choice
+ * mode to 'n' when the choice is visible.
*/
- if (sym && !sym_is_optional(sym) && parent->prompt) {
+ if (sym && sym_is_choice(sym) && parent->prompt) {
sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
expr_alloc_and(parent->prompt->visible.expr,
expr_alloc_symbol(&symbol_mod)));
diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c
index 9d22b0f319..e1cb09418c 100644
--- a/scripts/kconfig/nconf.c
+++ b/scripts/kconfig/nconf.c
@@ -826,26 +826,18 @@ static void build_conf(struct menu *menu)
val = sym_get_tristate_value(sym);
if (sym_is_changeable(sym)) {
- switch (type) {
- case S_BOOLEAN:
- item_make(menu, 't', "[%c]",
- val == no ? ' ' : '*');
+ switch (val) {
+ case yes:
+ ch = '*';
break;
- case S_TRISTATE:
- switch (val) {
- case yes:
- ch = '*';
- break;
- case mod:
- ch = 'M';
- break;
- default:
- ch = ' ';
- break;
- }
- item_make(menu, 't', "<%c>", ch);
+ case mod:
+ ch = 'M';
+ break;
+ default:
+ ch = ' ';
break;
}
+ item_make(menu, 't', "<%c>", ch);
} else {
item_make(menu, def_menu ? 't' : ':', " ");
}
@@ -853,16 +845,8 @@ static void build_conf(struct menu *menu)
item_add_str("%*c%s", indent + 1,
' ', menu_get_prompt(menu));
if (val == yes) {
- if (def_menu) {
- item_add_str(" (%s)",
- menu_get_prompt(def_menu));
- item_add_str(" --->");
- if (def_menu->list) {
- indent += 2;
- build_conf(def_menu);
- indent -= 2;
- }
- }
+ if (def_menu)
+ item_add_str(" (%s) --->", menu_get_prompt(def_menu));
return;
}
} else {
@@ -874,54 +858,46 @@ static void build_conf(struct menu *menu)
}
child_count++;
val = sym_get_tristate_value(sym);
- if (sym_is_choice_value(sym) && val == yes) {
- item_make(menu, ':', " ");
- } else {
- switch (type) {
- case S_BOOLEAN:
- if (sym_is_changeable(sym))
- item_make(menu, 't', "[%c]",
- val == no ? ' ' : '*');
- else
- item_make(menu, 't', "-%c-",
- val == no ? ' ' : '*');
+ switch (type) {
+ case S_BOOLEAN:
+ if (sym_is_changeable(sym))
+ item_make(menu, 't', "[%c]",
+ val == no ? ' ' : '*');
+ else
+ item_make(menu, 't', "-%c-",
+ val == no ? ' ' : '*');
+ break;
+ case S_TRISTATE:
+ switch (val) {
+ case yes:
+ ch = '*';
break;
- case S_TRISTATE:
- switch (val) {
- case yes:
- ch = '*';
- break;
- case mod:
- ch = 'M';
- break;
- default:
- ch = ' ';
- break;
- }
- if (sym_is_changeable(sym)) {
- if (sym->rev_dep.tri == mod)
- item_make(menu,
- 't', "{%c}", ch);
- else
- item_make(menu,
- 't', "<%c>", ch);
- } else
- item_make(menu, 't', "-%c-", ch);
+ case mod:
+ ch = 'M';
break;
default:
- tmp = 2 + strlen(sym_get_string_value(sym));
- item_make(menu, 's', " (%s)",
- sym_get_string_value(sym));
- tmp = indent - tmp + 4;
- if (tmp < 0)
- tmp = 0;
- item_add_str("%*c%s%s", tmp, ' ',
- menu_get_prompt(menu),
- (sym_has_value(sym) ||
- !sym_is_changeable(sym)) ? "" :
- " (NEW)");
- goto conf_childs;
+ ch = ' ';
+ break;
}
+ if (sym_is_changeable(sym)) {
+ if (sym->rev_dep.tri == mod)
+ item_make(menu, 't', "{%c}", ch);
+ else
+ item_make(menu, 't', "<%c>", ch);
+ } else
+ item_make(menu, 't', "-%c-", ch);
+ break;
+ default:
+ tmp = 2 + strlen(sym_get_string_value(sym));
+ item_make(menu, 's', " (%s)",
+ sym_get_string_value(sym));
+ tmp = indent - tmp + 4;
+ if (tmp < 0)
+ tmp = 0;
+ item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
+ (sym_has_value(sym) ||
+ !sym_is_changeable(sym)) ? "" : " (NEW)");
+ goto conf_childs;
}
item_add_str("%*c%s%s", indent + 1, ' ',
menu_get_prompt(menu),
diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y
index 7fb996612c..ff709001b1 100644
--- a/scripts/kconfig/parser.y
+++ b/scripts/kconfig/parser.y
@@ -30,6 +30,8 @@ static bool zconf_endtoken(const char *tokenname,
struct menu *current_menu, *current_entry;
+static bool inside_choice = false;
+
%}
%union
@@ -69,7 +71,6 @@ struct menu *current_menu, *current_entry;
%token T_MODULES
%token T_ON
%token T_OPEN_PAREN
-%token T_OPTIONAL
%token T_PLUS_EQUAL
%token T_PROMPT
%token T_RANGE
@@ -140,19 +141,25 @@ stmt_list_in_choice:
config_entry_start: T_CONFIG nonconst_symbol T_EOL
{
- $2->flags |= SYMBOL_OPTIONAL;
menu_add_entry($2);
printd(DEBUG_PARSE, "%s:%d:config %s\n", cur_filename, cur_lineno, $2->name);
};
config_stmt: config_entry_start config_option_list
{
+ if (inside_choice) {
+ if (!current_entry->prompt) {
+ fprintf(stderr, "%s:%d: error: choice member must have a prompt\n",
+ current_entry->filename, current_entry->lineno);
+ yynerrs++;
+ }
+ }
+
printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno);
};
menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL
{
- $2->flags |= SYMBOL_OPTIONAL;
menu_add_entry($2);
printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", cur_filename, cur_lineno, $2->name);
};
@@ -224,8 +231,8 @@ config_option: T_MODULES T_EOL
choice: T_CHOICE T_EOL
{
- struct symbol *sym = sym_lookup(NULL, SYMBOL_CHOICE);
- sym->flags |= SYMBOL_NO_WRITE;
+ struct symbol *sym = sym_lookup(NULL, 0);
+
menu_add_entry(sym);
menu_add_expr(P_CHOICE, NULL, NULL);
printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno);
@@ -240,10 +247,14 @@ choice_entry: choice choice_option_list
}
$$ = menu_add_menu();
+
+ inside_choice = true;
};
choice_end: end
{
+ inside_choice = false;
+
if (zconf_endtoken($1, "choice")) {
menu_end_menu();
printd(DEBUG_PARSE, "%s:%d:endchoice\n", cur_filename, cur_lineno);
@@ -272,12 +283,6 @@ choice_option: logic_type prompt_stmt_opt T_EOL
printd(DEBUG_PARSE, "%s:%d:type(%u)\n", cur_filename, cur_lineno, $1);
};
-choice_option: T_OPTIONAL T_EOL
-{
- current_entry->sym->flags |= SYMBOL_OPTIONAL;
- printd(DEBUG_PARSE, "%s:%d:optional\n", cur_filename, cur_lineno);
-};
-
choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL
{
menu_add_symbol(P_DEFAULT, $2, $3);
@@ -471,6 +476,38 @@ assign_val:
%%
+/**
+ * choice_check_sanity - check sanity of a choice member
+ *
+ * @menu: menu of the choice member
+ *
+ * Return: -1 if an error is found, 0 otherwise.
+ */
+static int choice_check_sanity(struct menu *menu)
+{
+ struct property *prop;
+ int ret = 0;
+
+ for (prop = menu->sym->prop; prop; prop = prop->next) {
+ if (prop->type == P_DEFAULT) {
+ fprintf(stderr, "%s:%d: error: %s",
+ prop->filename, prop->lineno,
+ "defaults for choice values not supported\n");
+ ret = -1;
+ }
+
+ if (prop->menu != menu && prop->type == P_PROMPT &&
+ prop->menu->parent != menu->parent) {
+ fprintf(stderr, "%s:%d: error: %s",
+ prop->filename, prop->lineno,
+ "choice value has a prompt outside its choice group\n");
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
void conf_parse(const char *name)
{
struct menu *menu;
@@ -517,20 +554,17 @@ void conf_parse(const char *name)
menu_finalize();
- menu = &rootmenu;
- while (menu) {
+ menu_for_each_entry(menu) {
+ struct menu *child;
+
if (menu->sym && sym_check_deps(menu->sym))
yynerrs++;
- if (menu->list) {
- menu = menu->list;
- continue;
+ if (menu->sym && sym_is_choice(menu->sym)) {
+ menu_for_each_sub_entry(child, menu)
+ if (child->sym && choice_check_sanity(child))
+ yynerrs++;
}
-
- while (!menu->next && menu->parent)
- menu = menu->parent;
-
- menu = menu->next;
}
if (yynerrs)
diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
index 67a5097781..0e439d3d48 100644
--- a/scripts/kconfig/symbol.c
+++ b/scripts/kconfig/symbol.c
@@ -81,6 +81,41 @@ struct property *sym_get_choice_prop(struct symbol *sym)
return NULL;
}
+/**
+ * sym_get_choice_menu - get the parent choice menu if present
+ *
+ * @sym: a symbol pointer
+ *
+ * Return: a choice menu if this function is called against a choice member.
+ */
+struct menu *sym_get_choice_menu(struct symbol *sym)
+{
+ struct menu *menu = NULL;
+ struct menu *m;
+
+ /*
+ * Choice members must have a prompt. Find a menu entry with a prompt,
+ * and assume it resides inside a choice block.
+ */
+ list_for_each_entry(m, &sym->menus, link)
+ if (m->prompt) {
+ menu = m;
+ break;
+ }
+
+ if (!menu)
+ return NULL;
+
+ do {
+ menu = menu->parent;
+ } while (menu && !menu->sym);
+
+ if (menu && menu->sym && sym_is_choice(menu->sym))
+ return menu;
+
+ return NULL;
+}
+
static struct property *sym_get_default_prop(struct symbol *sym)
{
struct property *prop;
@@ -155,13 +190,11 @@ static void sym_validate_range(struct symbol *sym)
static void sym_set_changed(struct symbol *sym)
{
- struct property *prop;
+ struct menu *menu;
sym->flags |= SYMBOL_CHANGED;
- for (prop = sym->prop; prop; prop = prop->next) {
- if (prop->menu)
- prop->menu->flags |= MENU_CHANGED;
- }
+ list_for_each_entry(menu, &sym->menus, link)
+ menu->flags |= MENU_CHANGED;
}
static void sym_set_all_changed(void)
@@ -469,10 +502,9 @@ void sym_calc_value(struct symbol *sym)
if (sym->flags & SYMBOL_CHANGED)
sym_set_changed(choice_sym);
}
- }
- if (sym->flags & SYMBOL_NO_WRITE)
sym->flags &= ~SYMBOL_WRITE;
+ }
if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES)
set_all_choice_values(sym);
@@ -829,7 +861,7 @@ struct symbol *sym_lookup(const char *name, int flags)
if (symbol->name &&
!strcmp(symbol->name, name) &&
(flags ? symbol->flags & flags
- : !(symbol->flags & (SYMBOL_CONST|SYMBOL_CHOICE))))
+ : !(symbol->flags & SYMBOL_CONST)))
return symbol;
}
new_name = xstrdup(name);
@@ -1174,16 +1206,18 @@ out:
static struct symbol *sym_check_choice_deps(struct symbol *choice)
{
- struct symbol *sym, *sym2;
- struct property *prop;
- struct expr *e;
+ struct menu *choice_menu, *menu;
+ struct symbol *sym2;
struct dep_stack stack;
dep_stack_insert(&stack, choice);
- prop = sym_get_choice_prop(choice);
- expr_list_for_each_sym(prop->expr, e, sym)
- sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
+ choice_menu = list_first_entry(&choice->menus, struct menu, link);
+
+ menu_for_each_sub_entry(menu, choice_menu) {
+ if (menu->sym)
+ menu->sym->flags |= SYMBOL_CHECK | SYMBOL_CHECKED;
+ }
choice->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
sym2 = sym_check_sym_deps(choice);
@@ -1191,14 +1225,17 @@ static struct symbol *sym_check_choice_deps(struct symbol *choice)
if (sym2)
goto out;
- expr_list_for_each_sym(prop->expr, e, sym) {
- sym2 = sym_check_sym_deps(sym);
+ menu_for_each_sub_entry(menu, choice_menu) {
+ if (!menu->sym)
+ continue;
+ sym2 = sym_check_sym_deps(menu->sym);
if (sym2)
break;
}
out:
- expr_list_for_each_sym(prop->expr, e, sym)
- sym->flags &= ~SYMBOL_CHECK;
+ menu_for_each_sub_entry(menu, choice_menu)
+ if (menu->sym)
+ menu->sym->flags &= ~SYMBOL_CHECK;
if (sym2 && sym_is_choice_value(sym2) &&
prop_get_symbol(sym_get_choice_prop(sym2)) == choice)
diff --git a/scripts/kconfig/tests/choice/Kconfig b/scripts/kconfig/tests/choice/Kconfig
index 0930eb65e9..8cdda40868 100644
--- a/scripts/kconfig/tests/choice/Kconfig
+++ b/scripts/kconfig/tests/choice/Kconfig
@@ -18,19 +18,6 @@ config BOOL_CHOICE1
endchoice
choice
- prompt "optional boolean choice"
- optional
- default OPT_BOOL_CHOICE1
-
-config OPT_BOOL_CHOICE0
- bool "choice 0"
-
-config OPT_BOOL_CHOICE1
- bool "choice 1"
-
-endchoice
-
-choice
prompt "tristate choice"
default TRI_CHOICE1
@@ -41,16 +28,3 @@ config TRI_CHOICE1
tristate "choice 1"
endchoice
-
-choice
- prompt "optional tristate choice"
- optional
- default OPT_TRI_CHOICE1
-
-config OPT_TRI_CHOICE0
- tristate "choice 0"
-
-config OPT_TRI_CHOICE1
- tristate "choice 1"
-
-endchoice
diff --git a/scripts/kconfig/tests/choice/__init__.py b/scripts/kconfig/tests/choice/__init__.py
index 4318fce059..05e1622200 100644
--- a/scripts/kconfig/tests/choice/__init__.py
+++ b/scripts/kconfig/tests/choice/__init__.py
@@ -6,8 +6,6 @@ The handling of 'choice' is a bit complicated part in Kconfig.
The behavior of 'y' choice is intuitive. If choice values are tristate,
the choice can be 'm' where each value can be enabled independently.
-Also, if a choice is marked as 'optional', the whole choice can be
-invisible.
"""
diff --git a/scripts/kconfig/tests/choice/allmod_expected_config b/scripts/kconfig/tests/choice/allmod_expected_config
index f1f5dcdb79..d1f5165174 100644
--- a/scripts/kconfig/tests/choice/allmod_expected_config
+++ b/scripts/kconfig/tests/choice/allmod_expected_config
@@ -1,9 +1,5 @@
CONFIG_MODULES=y
# CONFIG_BOOL_CHOICE0 is not set
CONFIG_BOOL_CHOICE1=y
-# CONFIG_OPT_BOOL_CHOICE0 is not set
-CONFIG_OPT_BOOL_CHOICE1=y
CONFIG_TRI_CHOICE0=m
CONFIG_TRI_CHOICE1=m
-CONFIG_OPT_TRI_CHOICE0=m
-CONFIG_OPT_TRI_CHOICE1=m
diff --git a/scripts/kconfig/tests/choice/allyes_expected_config b/scripts/kconfig/tests/choice/allyes_expected_config
index e5a062a115..8a76c18168 100644
--- a/scripts/kconfig/tests/choice/allyes_expected_config
+++ b/scripts/kconfig/tests/choice/allyes_expected_config
@@ -1,9 +1,5 @@
CONFIG_MODULES=y
# CONFIG_BOOL_CHOICE0 is not set
CONFIG_BOOL_CHOICE1=y
-# CONFIG_OPT_BOOL_CHOICE0 is not set
-CONFIG_OPT_BOOL_CHOICE1=y
# CONFIG_TRI_CHOICE0 is not set
CONFIG_TRI_CHOICE1=y
-# CONFIG_OPT_TRI_CHOICE0 is not set
-CONFIG_OPT_TRI_CHOICE1=y
diff --git a/scripts/kconfig/tests/choice/oldask0_expected_stdout b/scripts/kconfig/tests/choice/oldask0_expected_stdout
index b251bba969..d2257db464 100644
--- a/scripts/kconfig/tests/choice/oldask0_expected_stdout
+++ b/scripts/kconfig/tests/choice/oldask0_expected_stdout
@@ -3,8 +3,6 @@ boolean choice
1. choice 0 (BOOL_CHOICE0) (NEW)
> 2. choice 1 (BOOL_CHOICE1) (NEW)
choice[1-2?]:
-optional boolean choice [N/y/?] (NEW)
tristate choice [M/y/?] (NEW)
choice 0 (TRI_CHOICE0) [N/m/?] (NEW)
choice 1 (TRI_CHOICE1) [N/m/?] (NEW)
-optional tristate choice [N/m/y/?] (NEW)
diff --git a/scripts/kconfig/tests/choice/oldask1_config b/scripts/kconfig/tests/choice/oldask1_config
index b67bfe3c64..0f417856c8 100644
--- a/scripts/kconfig/tests/choice/oldask1_config
+++ b/scripts/kconfig/tests/choice/oldask1_config
@@ -1,2 +1 @@
# CONFIG_MODULES is not set
-CONFIG_OPT_BOOL_CHOICE0=y
diff --git a/scripts/kconfig/tests/choice/oldask1_expected_stdout b/scripts/kconfig/tests/choice/oldask1_expected_stdout
index c2125e9bf9..ffa20ad7f3 100644
--- a/scripts/kconfig/tests/choice/oldask1_expected_stdout
+++ b/scripts/kconfig/tests/choice/oldask1_expected_stdout
@@ -3,13 +3,7 @@ boolean choice
1. choice 0 (BOOL_CHOICE0) (NEW)
> 2. choice 1 (BOOL_CHOICE1) (NEW)
choice[1-2?]:
-optional boolean choice [Y/n/?] (NEW)
-optional boolean choice
-> 1. choice 0 (OPT_BOOL_CHOICE0)
- 2. choice 1 (OPT_BOOL_CHOICE1) (NEW)
-choice[1-2?]:
tristate choice
1. choice 0 (TRI_CHOICE0) (NEW)
> 2. choice 1 (TRI_CHOICE1) (NEW)
choice[1-2?]:
-optional tristate choice [N/y/?]