Patchwork [RFC] kconfig: implement select with values

login
register
mail settings
Submitter Stephen Rothwell
Date July 19, 2010, 6:37 p.m.
Message ID <20100720043706.3813715a.sfr@canb.auug.org.au>
Download mbox | patch
Permalink /patch/59218/
State Not Applicable
Headers show

Comments

Stephen Rothwell - July 19, 2010, 6:37 p.m.
This is a fairly brute force approach to allowing a Kconfig "select"
to specify an excplicit value for the selected config sybmol.

The syntax of select is changed to:

	"select" <symbol> [<expr>] ["if" expr]

The approach taken is to add a list of <value, dependency expression>
pairs to the symbol and check them whenever the reverse depends (from
a current-style select) are checked.

Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
 Documentation/kbuild/kconfig-language.txt |    7 +-
 scripts/kconfig/expr.h                    |    9 +
 scripts/kconfig/lkc.h                     |    1 +
 scripts/kconfig/menu.c                    |   70 +++++-
 scripts/kconfig/symbol.c                  |   88 +++++++-
 scripts/kconfig/zconf.tab.c_shipped       |  352 +++++++++++++++--------------
 scripts/kconfig/zconf.y                   |   17 ++-
 7 files changed, 356 insertions(+), 188 deletions(-)

This is a work in progress, though it is possible to set values.  I have
not done a great deal of testing.

I would like comments on the approach (and the actual code as the kconfig
code is pretty dense :-)).

This is built on top of the kbuild tree that is in linux-next
(git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild-2.6.git#for-next)
merged with Linus' tree of a few days ago (commit
2f7989efd4398d92b8adffce2e07dd043a0895fe "Merge
master.kernel.org:/home/rmk/linux-2.6-arm").
Sam Ravnborg - July 26, 2010, 10:17 p.m.
On Tue, Jul 20, 2010 at 04:37:06AM +1000, Stephen Rothwell wrote:
> This is a fairly brute force approach to allowing a Kconfig "select"
> to specify an excplicit value for the selected config sybmol.
> 
> The syntax of select is changed to:
> 
> 	"select" <symbol> [<expr>] ["if" expr]
> 
> The approach taken is to add a list of <value, dependency expression>
> pairs to the symbol and check them whenever the reverse depends (from
> a current-style select) are checked.

I do not see the need for this feature.
I have read most of the postings in the defconfig battle and
I saw Linus' select suggestion.

But we can do almost all of this today (or tomorrow).

Consider following Kconfig file:

    config USB_SUPPORT
            def_bool n

    config NET
            def_bool y

    source arch/x86/Kconfig

We use the feature that we can specify the same
config option several times.
And the first default value that is "visible"
will be used.

So it allows us to enable and disable any option.

And if we for example want to set a specific LOG_BUF_SHIFT
we use the same trick:

    config LOG_BUF_SHIFT
            int
            default 14


Now in the original suggestion of Linus he used:

    KBUILD_KCONFIG=Mykconfig make allnoconfig

And "allnoconfig" would make the modified defaults useless.

But the soon-to-be-introduced alldefconfig is more sensible.

alldefconfig uses default values for everything.

So with alldefconfig I think we have all the features we need
to create sensible default configs in the Kconfig language.

And therefore I do not see the need for the extension to select.
For the same reason I did not look closer at the implementation.

	Sam
Grant Likely - July 26, 2010, 10:47 p.m.
Hi Sam,

On Mon, Jul 26, 2010 at 4:17 PM, Sam Ravnborg <sam@ravnborg.org> wrote:
> On Tue, Jul 20, 2010 at 04:37:06AM +1000, Stephen Rothwell wrote:
>> This is a fairly brute force approach to allowing a Kconfig "select"
>> to specify an excplicit value for the selected config sybmol.
>>
>> The syntax of select is changed to:
>>
>>       "select" <symbol> [<expr>] ["if" expr]
>>
>> The approach taken is to add a list of <value, dependency expression>
>> pairs to the symbol and check them whenever the reverse depends (from
>> a current-style select) are checked.
>
> I do not see the need for this feature.
> I have read most of the postings in the defconfig battle and
> I saw Linus' select suggestion.
>
> But we can do almost all of this today (or tomorrow).
>
> Consider following Kconfig file:
>
>    config USB_SUPPORT
>            def_bool n
>
>    config NET
>            def_bool y
>
>    source arch/x86/Kconfig
>
> We use the feature that we can specify the same
> config option several times.
> And the first default value that is "visible"
> will be used.
>
> So it allows us to enable and disable any option.
>
> And if we for example want to set a specific LOG_BUF_SHIFT
> we use the same trick:
>
>    config LOG_BUF_SHIFT
>            int
>            default 14

Cute.  I didn't know this was possible.  I'll give it a try and see
how it works for me.  Do override config options also pickup
select/depends constraints applied by later definitions?  I think that
would be necessary to kick out warnings when a defconfig selection
isn't actually achievable.

g.

>
>
> Now in the original suggestion of Linus he used:
>
>    KBUILD_KCONFIG=Mykconfig make allnoconfig
>
> And "allnoconfig" would make the modified defaults useless.
>
> But the soon-to-be-introduced alldefconfig is more sensible.
>
> alldefconfig uses default values for everything.

I basically used defconfig in the prototype that I posted.  I think it
works well for me, and I've got it integrated into the build targets
(just like Stephen's earlier patch) without having to do a
KBUILD_KCONFIG trick.

g.
Sam Ravnborg - July 27, 2010, 6:45 a.m.
> 
> Cute.  I didn't know this was possible.  I'll give it a try and see
> how it works for me.  Do override config options also pickup
> select/depends constraints applied by later definitions?  I think that
> would be necessary to kick out warnings when a defconfig selection
> isn't actually achievable.

kconfig allows one to add more properties to a config
symbol by defining the symbol again.

So:
config FOO
	bool
	prompt "foo prompt"
	default y
	help
	  Help text

Is the same as:
config FOO
	bool

config FOO
	prompt "foo prompt"

config FOO
	default y

config FOO
	help
	  help text

The abvoe four lines can be located in different files.

And likewise kconfig allows the same property to be
specified twice or more.

So it is OK to say:

config BAR
	tristate "bar prompt"
	default m
	default y

Here kconfig just picks the first default is see.

And the example in my original mail uses the feature
that we can specify several defaults - and kconfig uses the first.


For choices the same is possible but then you need to use
a named choice - something that no one does today in the kernel.

choice CHOICE_X86_CPU
	default M386

endchoice

Here we change the default to M386 which becomes the
selected if we use "alldefconfig".

[Note: It does not work until we use named choices in arch/x86/Kconfig.cpu]

> 
> > Now in the original suggestion of Linus he used:
> >
> >    KBUILD_KCONFIG=Mykconfig make allnoconfig
> >
> > And "allnoconfig" would make the modified defaults useless.
> >
> > But the soon-to-be-introduced alldefconfig is more sensible.
> >
> > alldefconfig uses default values for everything.
> 
> I basically used defconfig in the prototype that I posted.  I think it
> works well for me, and I've got it integrated into the build targets
> (just like Stephen's earlier patch) without having to do a
> KBUILD_KCONFIG trick.

"defconfig" with an empty file gives same config as "alldefconfig" so
it makes sense. But we wanted to drop the use of defconfig files
in favour of files with Kconfig syntax.
So alldefconfig is just a better fit here.

[The above btw. also makes "savedefconfig" less usefull].

	Sam

Patch

diff --git a/Documentation/kbuild/kconfig-language.txt b/Documentation/kbuild/kconfig-language.txt
index b472e4e..b1fb180 100644
--- a/Documentation/kbuild/kconfig-language.txt
+++ b/Documentation/kbuild/kconfig-language.txt
@@ -95,14 +95,15 @@  applicable everywhere (see syntax).
 	bool "foo"
 	default y
 
-- reverse dependencies: "select" <symbol> ["if" <expr>]
+- reverse dependencies: "select" <symbol> [<expr>] ["if" <expr>]
   While normal dependencies reduce the upper limit of a symbol (see
   below), reverse dependencies can be used to force a lower limit of
   another symbol. The value of the current menu symbol is used as the
   minimal value <symbol> can be set to. If <symbol> is selected multiple
   times, the limit is set to the largest selection.
-  Reverse dependencies can only be used with boolean or tristate
-  symbols.
+  Reverse dependencies without the optional <expr> can only be used with
+  boolean or tristate symbols. If the optional <expr> is supplied,
+  the <symbol> will be set to that value if possible.
   Note:
 	select should be used with care. select will force
 	a symbol to a value without visiting the dependencies.
diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h
index 75a31e4..7217c6d 100644
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -56,6 +56,13 @@  struct expr_value {
 	tristate tri;
 };
 
+struct expr_select_value {
+	struct expr *expr;
+	tristate tri;
+	struct expr *value;
+	struct expr_select_value *next;
+};
+
 struct symbol_value {
 	void *val;
 	tristate tri;
@@ -85,6 +92,7 @@  struct symbol {
 	struct property *prop;
 	struct expr_value dir_dep;
 	struct expr_value rev_dep;
+	struct expr_select_value *val_dep;
 };
 
 #define for_all_symbols(i, sym) for (i = 0; i < SYMBOL_HASHSIZE; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER)
@@ -146,6 +154,7 @@  struct property {
 	                            * P_PROMPT, P_DEFAULT, P_MENU, P_COMMENT */
 	struct file *file;         /* what file was this property defined */
 	int lineno;                /* what lineno was this property defined */
+	struct expr *value;	   /* the optional P_SELECT value */
 };
 
 #define for_all_properties(sym, st, tok) \
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h
index ce6549c..5176657 100644
--- a/scripts/kconfig/lkc.h
+++ b/scripts/kconfig/lkc.h
@@ -95,6 +95,7 @@  struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *e
 struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep);
 void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep);
 void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep);
+void menu_add_select(struct symbol *sym, struct expr *value, struct expr *dep);
 void menu_add_option(int token, char *arg);
 void menu_finalize(struct menu *parent);
 void menu_set_type(int type);
diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index 1179989..f1211f3 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -161,6 +161,14 @@  void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
 	menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep);
 }
 
+void menu_add_select(struct symbol *sym, struct expr *value, struct expr *dep)
+{
+	struct property *p;
+
+	p = menu_add_prop(P_SELECT, NULL, expr_alloc_symbol(sym), dep);
+	p->value = value;
+}
+
 void menu_add_option(int token, char *arg)
 {
 	struct property *prop;
@@ -207,13 +215,22 @@  static void sym_check_prop(struct symbol *sym)
 				prop_warn(prop,
 				    "config symbol '%s' uses select, but is "
 				    "not boolean or tristate", sym->name);
-			else if (sym2->type != S_UNKNOWN &&
+			else if (prop->value == NULL &&
+				 sym2->type != S_UNKNOWN &&
 			         sym2->type != S_BOOLEAN &&
 			         sym2->type != S_TRISTATE)
 				prop_warn(prop,
-				    "'%s' has wrong type. 'select' only "
-				    "accept arguments of boolean and "
-				    "tristate type", sym2->name);
+				    "'%s' has wrong type. 'select' without a "
+				    "value only accepts arguments of boolean "
+				    "and tristate type", sym2->name);
+			else if (prop->value != NULL &&
+				 (sym2->type == S_INT ||
+				  sym2->type == S_HEX ||
+				  sym2->type == S_STRING) &&
+				 prop->value->type != E_SYMBOL)
+				prop_warn(prop,
+				    "select value for config symbol '%s'"
+				    " must be a single symbol", sym2->name);
 			break;
 		case P_RANGE:
 			if (sym->type != S_INT && sym->type != S_HEX)
@@ -229,6 +246,25 @@  static void sym_check_prop(struct symbol *sym)
 	}
 }
 
+static void finalize_select(struct symbol *sym, struct property *prop,
+		struct expr *dep)
+{
+	struct symbol *es = prop_get_symbol(prop);
+	struct expr_select_value *esv;
+
+	if (prop->value) {
+		esv = malloc(sizeof *esv);
+		esv->expr = expr_alloc_and(expr_alloc_symbol(sym),
+				expr_copy(dep));
+		esv->value = prop->value;
+		esv->next = es->val_dep;
+		es->val_dep = esv;
+	} else {
+		es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
+			expr_alloc_and(expr_alloc_symbol(sym), expr_copy(dep)));
+	}
+}
+
 void menu_finalize(struct menu *parent)
 {
 	struct menu *menu, *last_menu;
@@ -279,11 +315,8 @@  void menu_finalize(struct menu *parent)
 				if (menu->sym && menu->sym->type != S_TRISTATE)
 					dep = expr_trans_bool(dep);
 				prop->visible.expr = dep;
-				if (prop->type == P_SELECT) {
-					struct symbol *es = prop_get_symbol(prop);
-					es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
-							expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
-				}
+				if (prop->type == P_SELECT)
+					finalize_select(menu->sym, prop, dep);
 			}
 		}
 		for (menu = parent->list; menu; menu = menu->next)
@@ -389,9 +422,15 @@  void menu_finalize(struct menu *parent)
 	}
 
 	if (sym && !sym_is_optional(sym) && parent->prompt) {
+		struct expr_select_value *esv;
+
 		sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
 				expr_alloc_and(parent->prompt->visible.expr,
 					expr_alloc_symbol(&symbol_mod)));
+		for (esv = sym->val_dep; esv; esv = esv->next)
+			esv->expr = expr_alloc_or(esv->expr,
+				expr_alloc_and(parent->prompt->visible.expr,
+					expr_alloc_symbol(&symbol_mod)));
 	}
 }
 
@@ -509,6 +548,7 @@  void get_symbol_str(struct gstr *r, struct symbol *sym)
 {
 	bool hit;
 	struct property *prop;
+	struct expr_select_value *esv;
 
 	if (sym && sym->name) {
 		str_printf(r, "Symbol: %s [=%s]\n", sym->name,
@@ -533,6 +573,11 @@  void get_symbol_str(struct gstr *r, struct symbol *sym)
 		} else
 			str_printf(r, " && ");
 		expr_gstr_print(prop->expr, r);
+		if (prop->value) {
+			str_printf(r, " (value=");
+			expr_gstr_print(prop->value, r);
+			str_printf(r, ")");
+		}
 	}
 	if (hit)
 		str_append(r, "\n");
@@ -541,6 +586,13 @@  void get_symbol_str(struct gstr *r, struct symbol *sym)
 		expr_gstr_print(sym->rev_dep.expr, r);
 		str_append(r, "\n");
 	}
+	for (esv = sym->val_dep; esv; esv = esv->next) {
+		str_append(r, "  Selected by: ");
+		expr_gstr_print(esv->expr, r);
+		str_append(r, " with value: ");
+		expr_gstr_print(esv->value, r);
+		str_append(r, "\n");
+	}
 	str_append(r, "\n\n");
 }
 
diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
index c127fa3..8fbfc15 100644
--- a/scripts/kconfig/symbol.c
+++ b/scripts/kconfig/symbol.c
@@ -190,6 +190,7 @@  static void sym_calc_visibility(struct symbol *sym)
 {
 	struct property *prop;
 	tristate tri;
+	struct expr_select_value *esv;
 
 	/* any prompt visible? */
 	tri = no;
@@ -224,6 +225,15 @@  static void sym_calc_visibility(struct symbol *sym)
 		sym->rev_dep.tri = tri;
 		sym_set_changed(sym);
 	}
+	for (esv = sym->val_dep; esv; esv = esv->next) {
+		tri = expr_calc_value(esv->expr);
+		if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
+			tri = yes;
+		if (esv->tri != tri) {
+			esv->tri = tri;
+			sym_set_changed(sym);
+		}
+	}
 }
 
 static struct symbol *sym_calc_choice(struct symbol *sym)
@@ -268,6 +278,8 @@  void sym_calc_value(struct symbol *sym)
 	struct symbol_value newval, oldval;
 	struct property *prop;
 	struct expr *e;
+	struct expr_select_value *esv;
+	int got_sel_val;
 
 	if (!sym)
 		return;
@@ -321,6 +333,9 @@  void sym_calc_value(struct symbol *sym)
 			}
 			if (sym->rev_dep.tri != no)
 				sym->flags |= SYMBOL_WRITE;
+			for (esv = sym->val_dep; esv; esv = esv->next)
+				if (esv->tri != no)
+					sym->flags |= SYMBOL_WRITE;
 			if (!sym_is_choice(sym)) {
 				prop = sym_get_default_prop(sym);
 				if (prop) {
@@ -330,15 +345,34 @@  void sym_calc_value(struct symbol *sym)
 				}
 			}
 		calc_newval:
-			if (sym->dir_dep.tri == no && sym->rev_dep.tri != no) {
-				fprintf(stderr, "warning: (");
-				expr_fprint(sym->rev_dep.expr, stderr);
-				fprintf(stderr, ") selects %s which has unmet direct dependencies (",
-					sym->name);
-				expr_fprint(sym->dir_dep.expr, stderr);
-				fprintf(stderr, ")\n");
+			if (sym->dir_dep.tri == no) {
+				if (sym->rev_dep.tri != no) {
+					fprintf(stderr, "warning: (");
+					expr_fprint(sym->rev_dep.expr, stderr);
+					fprintf(stderr, ") selects %s which has unmet direct dependencies (",
+						sym->name);
+					expr_fprint(sym->dir_dep.expr, stderr);
+					fprintf(stderr, ")\n");
+				}
+				for (esv = sym->val_dep; esv; esv = esv->next) {
+					if ((esv->tri != no) &&
+					    (expr_calc_value(esv->value) != no)) {
+						fprintf(stderr, "warning: (");
+						expr_fprint(esv->expr, stderr);
+						fprintf(stderr, ") selects %s (with value ",
+							sym->name);
+						expr_fprint(esv->value, stderr);
+						fprintf(stderr, ") which has unmet direct dependencies (");
+						expr_fprint(sym->dir_dep.expr, stderr);
+						fprintf(stderr, ")\n");
+					}
+				}
 			}
 			newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri);
+			for (esv = sym->val_dep; esv; esv = esv->next)
+				if (esv->tri != no)
+					newval.tri = EXPR_OR(newval.tri,
+						expr_calc_value(esv->value));
 		}
 		if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN)
 			newval.tri = yes;
@@ -353,6 +387,23 @@  void sym_calc_value(struct symbol *sym)
 				break;
 			}
 		}
+		got_sel_val = 0;
+		for (esv = sym->val_dep; esv; esv = esv->next) {
+			if (esv->tri != no) {
+				struct symbol *ss = esv->value->left.sym;
+
+				if (got_sel_val) {
+					/* warn of more than one value selected */
+				} else {
+					sym->flags |= SYMBOL_WRITE;
+					sym_calc_value(ss);
+					newval.val = ss->curr.val;
+					got_sel_val = 1;
+				}
+			}
+		}
+		if (got_sel_val)
+			break;
 		prop = sym_get_default_prop(sym);
 		if (prop) {
 			struct symbol *ds = prop_get_symbol(prop);
@@ -432,6 +483,8 @@  void sym_set_all_changed(void)
 bool sym_tristate_within_range(struct symbol *sym, tristate val)
 {
 	int type = sym_get_type(sym);
+	struct expr_select_value *esv;
+	tristate tri;
 
 	if (sym->visible == no)
 		return false;
@@ -441,11 +494,14 @@  bool sym_tristate_within_range(struct symbol *sym, tristate val)
 
 	if (type == S_BOOLEAN && val == mod)
 		return false;
-	if (sym->visible <= sym->rev_dep.tri)
+	tri = sym->rev_dep.tri;
+	for (esv = sym->val_dep; esv; esv = esv->next)
+		tri = EXPR_OR(tri, esv->tri);
+	if (sym->visible <= tri)
 		return false;
 	if (sym_is_choice_value(sym) && sym->visible == yes)
 		return val == yes;
-	return val >= sym->rev_dep.tri && val <= sym->visible;
+	return val >= tri && val <= sym->visible;
 }
 
 bool sym_set_tristate_value(struct symbol *sym, tristate val)
@@ -666,7 +722,13 @@  const char *sym_get_string_value(struct symbol *sym)
 
 bool sym_is_changable(struct symbol *sym)
 {
-	return sym->visible > sym->rev_dep.tri;
+	tristate tri = sym->rev_dep.tri;
+	struct expr_select_value *esv;
+
+	for (esv = sym->val_dep; esv; esv = esv->next)
+		tri = EXPR_OR(tri, esv->tri);
+
+	return sym->visible > tri;
 }
 
 static unsigned strhash(const char *s)
@@ -819,10 +881,16 @@  static struct symbol *sym_check_sym_deps(struct symbol *sym)
 {
 	struct symbol *sym2;
 	struct property *prop;
+	struct expr_select_value *esv;
 
 	sym2 = sym_check_expr_deps(sym->rev_dep.expr);
 	if (sym2)
 		return sym2;
+	for (esv = sym->val_dep; esv; esv = esv->next) {
+		sym2 = sym_check_expr_deps(esv->expr);
+		if (sym2)
+			return sym2;
+	}
 
 	for (prop = sym->prop; prop; prop = prop->next) {
 		if (prop->type == P_CHOICE || prop->type == P_SELECT)
diff --git a/scripts/kconfig/zconf.tab.c_shipped b/scripts/kconfig/zconf.tab.c_shipped
index 32a9eef..307bf23 100644
--- a/scripts/kconfig/zconf.tab.c_shipped
+++ b/scripts/kconfig/zconf.tab.c_shipped
@@ -419,16 +419,16 @@  union yyalloc
 /* YYFINAL -- State number of the termination state.  */
 #define YYFINAL  3
 /* YYLAST -- Last index in YYTABLE.  */
-#define YYLAST   259
+#define YYLAST   268
 
 /* YYNTOKENS -- Number of terminals.  */
 #define YYNTOKENS  35
 /* YYNNTS -- Number of nonterminals.  */
 #define YYNNTS  46
 /* YYNRULES -- Number of rules.  */
-#define YYNRULES  110
+#define YYNRULES  111
 /* YYNRULES -- Number of states.  */
-#define YYNSTATES  180
+#define YYNSTATES  183
 
 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
 #define YYUNDEFTOK  2
@@ -480,14 +480,14 @@  static const yytype_uint16 yyprhs[] =
       28,    33,    37,    39,    41,    43,    45,    47,    49,    51,
       53,    55,    57,    59,    61,    63,    67,    70,    74,    77,
       81,    84,    85,    88,    91,    94,    97,   100,   103,   107,
-     112,   117,   122,   128,   132,   133,   137,   138,   141,   145,
-     148,   150,   154,   155,   158,   161,   164,   167,   170,   175,
-     179,   182,   187,   188,   191,   195,   197,   201,   202,   205,
-     208,   211,   215,   218,   220,   224,   225,   228,   231,   234,
-     238,   242,   245,   248,   251,   252,   255,   258,   261,   266,
-     267,   270,   272,   274,   277,   280,   283,   285,   288,   289,
-     292,   294,   298,   302,   306,   309,   313,   317,   319,   321,
-     322
+     112,   117,   122,   128,   134,   138,   139,   143,   144,   147,
+     151,   154,   156,   160,   161,   164,   167,   170,   173,   176,
+     181,   185,   188,   193,   194,   197,   201,   203,   207,   208,
+     211,   214,   217,   221,   224,   226,   230,   231,   234,   237,
+     240,   244,   248,   251,   254,   257,   258,   261,   264,   267,
+     272,   273,   276,   278,   280,   283,   286,   289,   291,   294,
+     295,   298,   300,   304,   308,   312,   315,   319,   323,   325,
+     327,   328
 };
 
 /* YYRHS -- A `-1'-separated list of the rules' RHS.  */
@@ -505,27 +505,27 @@  static const yytype_int8 yyrhs[] =
       -1,    45,    72,    -1,    45,    70,    -1,    45,    40,    -1,
       45,    30,    -1,    19,    73,    30,    -1,    18,    74,    77,
       30,    -1,    20,    78,    77,    30,    -1,    21,    25,    77,
-      30,    -1,    22,    79,    79,    77,    30,    -1,    23,    48,
-      30,    -1,    -1,    48,    25,    49,    -1,    -1,    33,    74,
-      -1,     7,    80,    30,    -1,    50,    54,    -1,    75,    -1,
-      51,    56,    52,    -1,    -1,    54,    55,    -1,    54,    72,
-      -1,    54,    70,    -1,    54,    30,    -1,    54,    40,    -1,
-      18,    74,    77,    30,    -1,    19,    73,    30,    -1,    17,
-      30,    -1,    20,    25,    77,    30,    -1,    -1,    56,    39,
-      -1,    14,    78,    76,    -1,    75,    -1,    57,    60,    58,
-      -1,    -1,    60,    39,    -1,    60,    64,    -1,    60,    53,
-      -1,     4,    74,    30,    -1,    61,    71,    -1,    75,    -1,
-      62,    65,    63,    -1,    -1,    65,    39,    -1,    65,    64,
-      -1,    65,    53,    -1,     6,    74,    30,    -1,     9,    74,
-      30,    -1,    67,    71,    -1,    12,    30,    -1,    69,    13,
-      -1,    -1,    71,    72,    -1,    71,    30,    -1,    71,    40,
-      -1,    16,    24,    78,    30,    -1,    -1,    74,    77,    -1,
-      25,    -1,    26,    -1,     5,    30,    -1,     8,    30,    -1,
-      15,    30,    -1,    30,    -1,    76,    30,    -1,    -1,    14,
-      78,    -1,    79,    -1,    79,    33,    79,    -1,    79,    27,
-      79,    -1,    29,    78,    28,    -1,    34,    78,    -1,    78,
-      31,    78,    -1,    78,    32,    78,    -1,    25,    -1,    26,
-      -1,    -1,    25,    -1
+      30,    -1,    21,    25,    78,    77,    30,    -1,    22,    79,
+      79,    77,    30,    -1,    23,    48,    30,    -1,    -1,    48,
+      25,    49,    -1,    -1,    33,    74,    -1,     7,    80,    30,
+      -1,    50,    54,    -1,    75,    -1,    51,    56,    52,    -1,
+      -1,    54,    55,    -1,    54,    72,    -1,    54,    70,    -1,
+      54,    30,    -1,    54,    40,    -1,    18,    74,    77,    30,
+      -1,    19,    73,    30,    -1,    17,    30,    -1,    20,    25,
+      77,    30,    -1,    -1,    56,    39,    -1,    14,    78,    76,
+      -1,    75,    -1,    57,    60,    58,    -1,    -1,    60,    39,
+      -1,    60,    64,    -1,    60,    53,    -1,     4,    74,    30,
+      -1,    61,    71,    -1,    75,    -1,    62,    65,    63,    -1,
+      -1,    65,    39,    -1,    65,    64,    -1,    65,    53,    -1,
+       6,    74,    30,    -1,     9,    74,    30,    -1,    67,    71,
+      -1,    12,    30,    -1,    69,    13,    -1,    -1,    71,    72,
+      -1,    71,    30,    -1,    71,    40,    -1,    16,    24,    78,
+      30,    -1,    -1,    74,    77,    -1,    25,    -1,    26,    -1,
+       5,    30,    -1,     8,    30,    -1,    15,    30,    -1,    30,
+      -1,    76,    30,    -1,    -1,    14,    78,    -1,    79,    -1,
+      79,    33,    79,    -1,    79,    27,    79,    -1,    29,    78,
+      28,    -1,    34,    78,    -1,    78,    31,    78,    -1,    78,
+      32,    78,    -1,    25,    -1,    26,    -1,    -1,    25,    -1
 };
 
 /* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
@@ -535,14 +535,14 @@  static const yytype_uint16 yyrline[] =
      117,   121,   125,   125,   125,   125,   125,   125,   125,   129,
      130,   131,   132,   133,   134,   138,   139,   145,   153,   159,
      167,   177,   179,   180,   181,   182,   183,   184,   187,   195,
-     201,   211,   217,   223,   226,   228,   239,   240,   245,   254,
-     259,   267,   270,   272,   273,   274,   275,   276,   279,   285,
-     296,   302,   312,   314,   319,   327,   335,   338,   340,   341,
-     342,   347,   354,   359,   367,   370,   372,   373,   374,   377,
-     385,   392,   399,   405,   412,   414,   415,   416,   419,   427,
-     429,   434,   435,   438,   439,   440,   444,   445,   448,   449,
-     452,   453,   454,   455,   456,   457,   458,   461,   462,   465,
-     466
+     201,   211,   217,   223,   229,   232,   234,   245,   246,   251,
+     260,   265,   273,   276,   278,   279,   280,   281,   282,   285,
+     291,   302,   308,   318,   320,   325,   333,   341,   344,   346,
+     347,   348,   353,   360,   365,   373,   376,   378,   379,   380,
+     383,   391,   398,   405,   411,   418,   420,   421,   422,   425,
+     433,   435,   440,   441,   444,   445,   446,   450,   451,   454,
+     455,   458,   459,   460,   461,   462,   463,   464,   467,   468,
+     471,   472
 };
 #endif
 
@@ -590,14 +590,14 @@  static const yytype_uint8 yyr1[] =
       37,    37,    38,    38,    38,    38,    38,    38,    38,    39,
       39,    39,    39,    39,    39,    40,    40,    41,    42,    43,
       44,    45,    45,    45,    45,    45,    45,    45,    46,    46,
-      46,    46,    46,    47,    48,    48,    49,    49,    50,    51,
-      52,    53,    54,    54,    54,    54,    54,    54,    55,    55,
-      55,    55,    56,    56,    57,    58,    59,    60,    60,    60,
-      60,    61,    62,    63,    64,    65,    65,    65,    65,    66,
-      67,    68,    69,    70,    71,    71,    71,    71,    72,    73,
-      73,    74,    74,    75,    75,    75,    76,    76,    77,    77,
-      78,    78,    78,    78,    78,    78,    78,    79,    79,    80,
-      80
+      46,    46,    46,    46,    47,    48,    48,    49,    49,    50,
+      51,    52,    53,    54,    54,    54,    54,    54,    54,    55,
+      55,    55,    55,    56,    56,    57,    58,    59,    60,    60,
+      60,    60,    61,    62,    63,    64,    65,    65,    65,    65,
+      66,    67,    68,    69,    70,    71,    71,    71,    71,    72,
+      73,    73,    74,    74,    75,    75,    75,    76,    76,    77,
+      77,    78,    78,    78,    78,    78,    78,    78,    79,    79,
+      80,    80
 };
 
 /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
@@ -607,14 +607,14 @@  static const yytype_uint8 yyr2[] =
        4,     3,     1,     1,     1,     1,     1,     1,     1,     1,
        1,     1,     1,     1,     1,     3,     2,     3,     2,     3,
        2,     0,     2,     2,     2,     2,     2,     2,     3,     4,
-       4,     4,     5,     3,     0,     3,     0,     2,     3,     2,
-       1,     3,     0,     2,     2,     2,     2,     2,     4,     3,
-       2,     4,     0,     2,     3,     1,     3,     0,     2,     2,
-       2,     3,     2,     1,     3,     0,     2,     2,     2,     3,
-       3,     2,     2,     2,     0,     2,     2,     2,     4,     0,
-       2,     1,     1,     2,     2,     2,     1,     2,     0,     2,
-       1,     3,     3,     3,     2,     3,     3,     1,     1,     0,
-       1
+       4,     4,     5,     5,     3,     0,     3,     0,     2,     3,
+       2,     1,     3,     0,     2,     2,     2,     2,     2,     4,
+       3,     2,     4,     0,     2,     3,     1,     3,     0,     2,
+       2,     2,     3,     2,     1,     3,     0,     2,     2,     2,
+       3,     3,     2,     2,     2,     0,     2,     2,     2,     4,
+       0,     2,     1,     1,     2,     2,     2,     1,     2,     0,
+       2,     1,     3,     3,     3,     2,     3,     3,     1,     1,
+       0,     1
 };
 
 /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
@@ -622,31 +622,32 @@  static const yytype_uint8 yyr2[] =
    means the default is an error.  */
 static const yytype_uint8 yydefact[] =
 {
-       3,     0,     0,     1,     0,     0,     0,     0,     0,   109,
+       3,     0,     0,     1,     0,     0,     0,     0,     0,   110,
        0,     0,     0,     0,     0,     0,    12,    16,    13,    14,
       18,    15,    17,     0,    19,     0,     4,    31,    22,    31,
-      23,    52,    62,     5,    67,    20,    84,    75,     6,    24,
-      84,    21,     8,    11,    91,    92,     0,     0,    93,     0,
-     110,     0,    94,     0,     0,     0,   107,   108,     0,     0,
-       0,   100,    95,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,    96,     7,    71,    79,    48,    80,    27,
-      29,     0,   104,     0,     0,    64,     0,     0,     9,    10,
-       0,     0,     0,     0,    89,     0,     0,     0,    44,     0,
-      37,    36,    32,    33,     0,    35,    34,     0,     0,    89,
-       0,    56,    57,    53,    55,    54,    63,    51,    50,    68,
-      70,    66,    69,    65,    86,    87,    85,    76,    78,    74,
-      77,    73,    97,   103,   105,   106,   102,   101,    26,    82,
-       0,    98,     0,    98,    98,    98,     0,     0,     0,    83,
-      60,    98,     0,    98,     0,     0,     0,    38,    90,     0,
-       0,    98,    46,    43,    25,     0,    59,     0,    88,    99,
-      39,    40,    41,     0,     0,    45,    58,    61,    42,    47
+      23,    53,    63,     5,    68,    20,    85,    76,     6,    24,
+      85,    21,     8,    11,    92,    93,     0,     0,    94,     0,
+     111,     0,    95,     0,     0,     0,   108,   109,     0,     0,
+       0,   101,    96,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,    97,     7,    72,    80,    49,    81,    27,
+      29,     0,   105,     0,     0,    65,     0,     0,     9,    10,
+       0,     0,     0,     0,    90,     0,     0,     0,    45,     0,
+      37,    36,    32,    33,     0,    35,    34,     0,     0,    90,
+       0,    57,    58,    54,    56,    55,    64,    52,    51,    69,
+      71,    67,    70,    66,    87,    88,    86,    77,    79,    75,
+      78,    74,    98,   104,   106,   107,   103,   102,    26,    83,
+       0,    99,     0,    99,    99,    99,     0,     0,     0,    84,
+      61,    99,     0,    99,     0,     0,     0,    38,    91,     0,
+       0,    99,    99,    47,    44,    25,     0,    60,     0,    89,
+     100,    39,    40,    41,     0,     0,     0,    46,    59,    62,
+      42,    43,    48
 };
 
 /* YYDEFGOTO[NTERM-NUM].  */
 static const yytype_int16 yydefgoto[] =
 {
       -1,     1,     2,    25,    26,   101,    27,    28,    29,    30,
-      65,   102,   103,   147,   175,    31,    32,   117,    33,    67,
+      65,   102,   103,   147,   177,    31,    32,   117,    33,    67,
      113,    68,    34,   121,    35,    69,    36,    37,   129,    38,
       71,    39,    40,    41,   104,   105,    70,   106,   142,   143,
       42,    74,   156,    60,    61,    51
@@ -661,65 +662,67 @@  static const yytype_int16 yypact[] =
       33,    -1,    27,    40,    -3,    38,   -80,   -80,   -80,   -80,
      -80,   -80,   -80,    71,   -80,    77,   -80,   -80,   -80,   -80,
      -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
-     -80,   -80,   -80,   -80,   -80,   -80,    57,    61,   -80,    63,
-     -80,    76,   -80,    87,   101,   133,   -80,   -80,    -3,    -3,
-     195,    -6,   -80,   136,   149,    39,   104,    65,   150,     5,
-     194,     5,   167,   -80,   176,   -80,   -80,   -80,   -80,   -80,
-     -80,    68,   -80,    -3,    -3,   176,    72,    72,   -80,   -80,
-     177,   187,    78,    -1,    -1,    -3,   196,    72,   -80,   222,
-     -80,   -80,   -80,   -80,   221,   -80,   -80,   205,    -1,    -1,
-     211,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,    57,    63,   -80,    76,
+     -80,    87,   -80,   101,   115,   128,   -80,   -80,    -3,    -3,
+     209,    -6,   -80,   137,   162,    39,   104,    65,   208,     5,
+     196,     5,   169,   -80,   166,   -80,   -80,   -80,   -80,   -80,
+     -80,    68,   -80,    -3,    -3,   166,    72,    72,   -80,   -80,
+     168,   178,    78,    -1,    -1,    -3,   184,    72,   -80,   219,
+     -80,   -80,   -80,   -80,   211,   -80,   -80,   195,    -1,    -1,
+     205,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
      -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,
-     -80,   -80,   -80,   -80,   206,   -80,   -80,   -80,   -80,   -80,
-      -3,   223,   209,   223,   197,   223,    72,     7,   210,   -80,
-     -80,   223,   212,   223,   201,    -3,   213,   -80,   -80,   214,
-     215,   223,   208,   -80,   -80,   216,   -80,   217,   -80,   113,
-     -80,   -80,   -80,   218,    -1,   -80,   -80,   -80,   -80,   -80
+     -80,   -80,   -80,   -80,   199,   -80,   -80,   -80,   -80,   -80,
+      -3,   220,   206,   220,   201,   130,    72,     7,   217,   -80,
+     -80,   220,   218,   220,   212,    -3,   221,   -80,   -80,   222,
+     223,   201,   220,   216,   -80,   -80,   224,   -80,   225,   -80,
+     150,   -80,   -80,   -80,   226,   227,    -1,   -80,   -80,   -80,
+     -80,   -80,   -80
 };
 
 /* YYPGOTO[NTERM-NUM].  */
 static const yytype_int16 yypgoto[] =
 {
-     -80,   -80,   -80,   -80,   122,   -34,   -80,   -80,   -80,   -80,
-     220,   -80,   -80,   -80,   -80,   -80,   -80,   -80,    59,   -80,
-     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   125,
-     -80,   -80,   -80,   -80,   -80,   183,   219,    22,   142,    -5,
-     147,   192,    69,   -54,   -79,   -80
+     -80,   -80,   -80,   -80,    92,   -34,   -80,   -80,   -80,   -80,
+     229,   -80,   -80,   -80,   -80,   -80,   -80,   -80,    59,   -80,
+     -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   -80,   124,
+     -80,   -80,   -80,   -80,   -80,   183,   228,    22,   151,    -5,
+      97,   202,    84,   -54,   -79,   -80
 };
 
 /* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
    positive, shift that token.  If negative, reduce the rule which
    number is the opposite.  If zero, do what YYDEFACT says.
    If YYTABLE_NINF, syntax error.  */
-#define YYTABLE_NINF -82
+#define YYTABLE_NINF -83
 static const yytype_int16 yytable[] =
 {
       46,    47,     3,    49,    81,    82,    53,   136,   137,     6,
        7,     8,     9,    10,    11,    12,    13,    43,   146,    14,
       15,    86,    56,    57,    44,    45,    58,    87,    48,   134,
-     135,    59,   162,   112,    50,    24,   125,   163,   125,   -28,
+     135,    59,   163,   112,    50,    24,   125,   164,   125,   -28,
       90,   144,   -28,   -28,   -28,   -28,   -28,   -28,   -28,   -28,
      -28,    91,    54,   -28,   -28,    92,   -28,    93,    94,    95,
-      96,    97,    98,    52,    99,    55,    90,   161,    62,   100,
-     -49,   -49,    63,   -49,   -49,   -49,   -49,    91,    64,   -49,
-     -49,    92,   107,   108,   109,   110,   154,    73,   141,   115,
-      99,    75,   126,    76,   126,   111,   133,    56,    57,    83,
-      84,   169,   140,   151,   -30,    90,    77,   -30,   -30,   -30,
-     -30,   -30,   -30,   -30,   -30,   -30,    91,    78,   -30,   -30,
+      96,    97,    98,    52,    99,    55,    90,   162,    62,   100,
+     -50,   -50,    63,   -50,   -50,   -50,   -50,    91,    64,   -50,
+     -50,    92,   107,   108,   109,   110,   154,    73,   141,   115,
+      99,   161,   126,    75,   126,   111,   133,    56,    57,    83,
+      84,   170,   140,   151,   -30,    90,    76,   -30,   -30,   -30,
+     -30,   -30,   -30,   -30,   -30,   -30,    91,    77,   -30,   -30,
       92,   -30,    93,    94,    95,    96,    97,    98,   120,    99,
-     128,    79,    -2,     4,   100,     5,     6,     7,     8,     9,
-      10,    11,    12,    13,    83,    84,    14,    15,    16,    17,
-      18,    19,    20,    21,    22,     7,     8,    23,    10,    11,
-      12,    13,    24,    80,    14,    15,    88,   -81,    90,   179,
-     -81,   -81,   -81,   -81,   -81,   -81,   -81,   -81,   -81,    89,
-      24,   -81,   -81,    92,   -81,   -81,   -81,   -81,   -81,   -81,
-     116,   119,    99,   127,   122,    90,   130,   124,   -72,   -72,
-     -72,   -72,   -72,   -72,   -72,   -72,   132,   138,   -72,   -72,
-      92,   155,   158,   159,   160,   118,   123,   139,   131,    99,
-     165,   145,   167,   148,   124,    73,    83,    84,    83,    84,
-     173,   168,    83,    84,   149,   150,   153,   155,    84,   157,
-     164,   174,   166,   170,   171,   172,   176,   177,   178,    66,
-     114,   152,    85,     0,     0,     0,     0,     0,     0,    72
+     128,    78,    -2,     4,   100,     5,     6,     7,     8,     9,
+      10,    11,    12,    13,   155,    79,    14,    15,    16,    17,
+      18,    19,    20,    21,    22,    56,    57,    23,    80,    58,
+     116,   119,    24,   127,    59,   118,   123,    88,   131,   -82,
+      90,   182,   -82,   -82,   -82,   -82,   -82,   -82,   -82,   -82,
+     -82,    83,    84,   -82,   -82,    92,   -82,   -82,   -82,   -82,
+     -82,   -82,    89,   122,    99,   130,   132,    90,   138,   124,
+     -73,   -73,   -73,   -73,   -73,   -73,   -73,   -73,   139,   145,
+     -73,   -73,    92,     7,     8,   155,    10,    11,    12,    13,
+     148,    99,    14,    15,   149,   150,   124,   158,   159,   160,
+     153,    84,    83,    84,   155,   166,   157,   168,    24,    73,
+      83,    84,   169,    83,    84,   174,   175,   165,   167,   176,
+     114,   171,   172,   173,   178,   179,   180,   181,    66,     0,
+     152,     0,    85,     0,     0,     0,     0,     0,    72
 };
 
 static const yytype_int16 yycheck[] =
@@ -733,23 +736,24 @@  static const yytype_int16 yycheck[] =
       21,    22,    23,    30,    25,    25,     1,   146,    30,    30,
        5,     6,     1,     8,     9,    10,    11,    12,     1,    14,
       15,    16,    17,    18,    19,    20,   140,    30,    93,    67,
-      25,    30,    70,    30,    72,    30,    28,    25,    26,    31,
+      25,   145,    70,    30,    72,    30,    28,    25,    26,    31,
       32,   155,    24,   108,     0,     1,    30,     3,     4,     5,
        6,     7,     8,     9,    10,    11,    12,    30,    14,    15,
       16,    17,    18,    19,    20,    21,    22,    23,    69,    25,
       71,    30,     0,     1,    30,     3,     4,     5,     6,     7,
-       8,     9,    10,    11,    31,    32,    14,    15,    16,    17,
-      18,    19,    20,    21,    22,     5,     6,    25,     8,     9,
-      10,    11,    30,    30,    14,    15,    30,     0,     1,   174,
-       3,     4,     5,     6,     7,     8,     9,    10,    11,    30,
-      30,    14,    15,    16,    17,    18,    19,    20,    21,    22,
-      68,    69,    25,    71,    69,     1,    71,    30,     4,     5,
-       6,     7,     8,     9,    10,    11,    30,    30,    14,    15,
-      16,    14,   143,   144,   145,    68,    69,    30,    71,    25,
-     151,    25,   153,     1,    30,    30,    31,    32,    31,    32,
-     161,    30,    31,    32,    13,    30,    25,    14,    32,    30,
-      30,    33,    30,    30,    30,    30,    30,    30,    30,    29,
-      67,   109,    60,    -1,    -1,    -1,    -1,    -1,    -1,    40
+       8,     9,    10,    11,    14,    30,    14,    15,    16,    17,
+      18,    19,    20,    21,    22,    25,    26,    25,    30,    29,
+      68,    69,    30,    71,    34,    68,    69,    30,    71,     0,
+       1,   176,     3,     4,     5,     6,     7,     8,     9,    10,
+      11,    31,    32,    14,    15,    16,    17,    18,    19,    20,
+      21,    22,    30,    69,    25,    71,    30,     1,    30,    30,
+       4,     5,     6,     7,     8,     9,    10,    11,    30,    25,
+      14,    15,    16,     5,     6,    14,     8,     9,    10,    11,
+       1,    25,    14,    15,    13,    30,    30,   143,   144,   145,
+      25,    32,    31,    32,    14,   151,    30,   153,    30,    30,
+      31,    32,    30,    31,    32,   161,   162,    30,    30,    33,
+      67,    30,    30,    30,    30,    30,    30,    30,    29,    -1,
+     109,    -1,    60,    -1,    -1,    -1,    -1,    -1,    40
 };
 
 /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
@@ -772,8 +776,9 @@  static const yytype_uint8 yystos[] =
       64,    75,    30,    28,    78,    78,    79,    79,    30,    30,
       24,    74,    73,    74,    78,    25,    79,    48,     1,    13,
       30,    74,    73,    25,    78,    14,    77,    30,    77,    77,
-      77,    79,    25,    30,    30,    77,    30,    77,    30,    78,
-      30,    30,    30,    77,    33,    49,    30,    30,    30,    74
+      77,    78,    79,    25,    30,    30,    77,    30,    77,    30,
+      78,    30,    30,    30,    77,    77,    33,    49,    30,    30,
+      30,    30,    74
 };
 
 #define yyerrok		(yyerrstatus = 0)
@@ -1719,7 +1724,7 @@  yyreduce:
   case 41:
 
     {
-	menu_add_symbol(P_SELECT, sym_lookup((yyvsp[(2) - (4)].string), 0), (yyvsp[(3) - (4)].expr));
+	menu_add_select(sym_lookup((yyvsp[(2) - (4)].string), 0), NULL, (yyvsp[(3) - (4)].expr));
 	printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
 ;}
     break;
@@ -1727,12 +1732,20 @@  yyreduce:
   case 42:
 
     {
+	menu_add_select(sym_lookup((yyvsp[(2) - (5)].string), 0), (yyvsp[(3) - (5)].expr), (yyvsp[(4) - (5)].expr));
+	printd(DEBUG_PARSE, "%s:%d:select with value\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 43:
+
+    {
 	menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,(yyvsp[(2) - (5)].symbol), (yyvsp[(3) - (5)].symbol)), (yyvsp[(4) - (5)].expr));
 	printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
 ;}
     break;
 
-  case 45:
+  case 46:
 
     {
 	struct kconf_id *id = kconf_id_lookup((yyvsp[(2) - (3)].string), strlen((yyvsp[(2) - (3)].string)));
@@ -1744,17 +1757,17 @@  yyreduce:
 ;}
     break;
 
-  case 46:
+  case 47:
 
     { (yyval.string) = NULL; ;}
     break;
 
-  case 47:
+  case 48:
 
     { (yyval.string) = (yyvsp[(2) - (2)].string); ;}
     break;
 
-  case 48:
+  case 49:
 
     {
 	struct symbol *sym = sym_lookup((yyvsp[(2) - (3)].string), SYMBOL_CHOICE);
@@ -1765,14 +1778,14 @@  yyreduce:
 ;}
     break;
 
-  case 49:
+  case 50:
 
     {
 	(yyval.menu) = menu_add_menu();
 ;}
     break;
 
-  case 50:
+  case 51:
 
     {
 	if (zconf_endtoken((yyvsp[(1) - (1)].id), T_CHOICE, T_ENDCHOICE)) {
@@ -1782,7 +1795,7 @@  yyreduce:
 ;}
     break;
 
-  case 58:
+  case 59:
 
     {
 	menu_add_prompt(P_PROMPT, (yyvsp[(2) - (4)].string), (yyvsp[(3) - (4)].expr));
@@ -1790,7 +1803,7 @@  yyreduce:
 ;}
     break;
 
-  case 59:
+  case 60:
 
     {
 	if ((yyvsp[(1) - (3)].id)->stype == S_BOOLEAN || (yyvsp[(1) - (3)].id)->stype == S_TRISTATE) {
@@ -1803,7 +1816,7 @@  yyreduce:
 ;}
     break;
 
-  case 60:
+  case 61:
 
     {
 	current_entry->sym->flags |= SYMBOL_OPTIONAL;
@@ -1811,7 +1824,7 @@  yyreduce:
 ;}
     break;
 
-  case 61:
+  case 62:
 
     {
 	if ((yyvsp[(1) - (4)].id)->stype == S_UNKNOWN) {
@@ -1823,7 +1836,7 @@  yyreduce:
 ;}
     break;
 
-  case 64:
+  case 65:
 
     {
 	printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
@@ -1833,7 +1846,7 @@  yyreduce:
 ;}
     break;
 
-  case 65:
+  case 66:
 
     {
 	if (zconf_endtoken((yyvsp[(1) - (1)].id), T_IF, T_ENDIF)) {
@@ -1843,7 +1856,7 @@  yyreduce:
 ;}
     break;
 
-  case 71:
+  case 72:
 
     {
 	menu_add_entry(NULL);
@@ -1852,14 +1865,14 @@  yyreduce:
 ;}
     break;
 
-  case 72:
+  case 73:
 
     {
 	(yyval.menu) = menu_add_menu();
 ;}
     break;
 
-  case 73:
+  case 74:
 
     {
 	if (zconf_endtoken((yyvsp[(1) - (1)].id), T_MENU, T_ENDMENU)) {
@@ -1869,7 +1882,7 @@  yyreduce:
 ;}
     break;
 
-  case 79:
+  case 80:
 
     {
 	printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), (yyvsp[(2) - (3)].string));
@@ -1877,7 +1890,7 @@  yyreduce:
 ;}
     break;
 
-  case 80:
+  case 81:
 
     {
 	menu_add_entry(NULL);
@@ -1886,14 +1899,14 @@  yyreduce:
 ;}
     break;
 
-  case 81:
+  case 82:
 
     {
 	menu_end_entry();
 ;}
     break;
 
-  case 82:
+  case 83:
 
     {
 	printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
@@ -1901,14 +1914,14 @@  yyreduce:
 ;}
     break;
 
-  case 83:
+  case 84:
 
     {
 	current_entry->help = (yyvsp[(2) - (2)].string);
 ;}
     break;
 
-  case 88:
+  case 89:
 
     {
 	menu_add_dep((yyvsp[(3) - (4)].expr));
@@ -1916,84 +1929,84 @@  yyreduce:
 ;}
     break;
 
-  case 90:
+  case 91:
 
     {
 	menu_add_prompt(P_PROMPT, (yyvsp[(1) - (2)].string), (yyvsp[(2) - (2)].expr));
 ;}
     break;
 
-  case 93:
+  case 94:
 
     { (yyval.id) = (yyvsp[(1) - (2)].id); ;}
     break;
 
-  case 94:
+  case 95:
 
     { (yyval.id) = (yyvsp[(1) - (2)].id); ;}
     break;
 
-  case 95:
+  case 96:
 
     { (yyval.id) = (yyvsp[(1) - (2)].id); ;}
     break;
 
-  case 98:
+  case 99:
 
     { (yyval.expr) = NULL; ;}
     break;
 
-  case 99:
+  case 100:
 
     { (yyval.expr) = (yyvsp[(2) - (2)].expr); ;}
     break;
 
-  case 100:
+  case 101:
 
     { (yyval.expr) = expr_alloc_symbol((yyvsp[(1) - (1)].symbol)); ;}
     break;
 
-  case 101:
+  case 102:
 
     { (yyval.expr) = expr_alloc_comp(E_EQUAL, (yyvsp[(1) - (3)].symbol), (yyvsp[(3) - (3)].symbol)); ;}
     break;
 
-  case 102:
+  case 103:
 
     { (yyval.expr) = expr_alloc_comp(E_UNEQUAL, (yyvsp[(1) - (3)].symbol), (yyvsp[(3) - (3)].symbol)); ;}
     break;
 
-  case 103:
+  case 104:
 
     { (yyval.expr) = (yyvsp[(2) - (3)].expr); ;}
     break;
 
-  case 104:
+  case 105:
 
     { (yyval.expr) = expr_alloc_one(E_NOT, (yyvsp[(2) - (2)].expr)); ;}
     break;
 
-  case 105:
+  case 106:
 
     { (yyval.expr) = expr_alloc_two(E_OR, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;}
     break;
 
-  case 106:
+  case 107:
 
     { (yyval.expr) = expr_alloc_two(E_AND, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;}
     break;
 
-  case 107:
+  case 108:
 
     { (yyval.symbol) = sym_lookup((yyvsp[(1) - (1)].string), 0); free((yyvsp[(1) - (1)].string)); ;}
     break;
 
-  case 108:
+  case 109:
 
     { (yyval.symbol) = sym_lookup((yyvsp[(1) - (1)].string), SYMBOL_CONST); free((yyvsp[(1) - (1)].string)); ;}
     break;
 
-  case 109:
+  case 110:
 
     { (yyval.string) = NULL; ;}
     break;
@@ -2387,6 +2400,15 @@  static void print_symbol(FILE *out, struct menu *menu)
 		case P_SELECT:
 			fputs( "  select ", out);
 			expr_fprint(prop->expr, out);
+			if (prop->value) {
+				fputs(" (value=", out);
+				expr_fprint(prop->value, out);
+				fputc(')', out);
+			}
+			if (!expr_is_yes(prop->visible.expr)) {
+				fputs(" if ", out);
+				expr_fprint(prop->visible.expr, out);
+			}
 			fputc('\n', out);
 			break;
 		case P_RANGE:
diff --git a/scripts/kconfig/zconf.y b/scripts/kconfig/zconf.y
index 23dfd3b..5f2a413 100644
--- a/scripts/kconfig/zconf.y
+++ b/scripts/kconfig/zconf.y
@@ -210,10 +210,16 @@  config_option: T_DEFAULT expr if_expr T_EOL
 
 config_option: T_SELECT T_WORD if_expr T_EOL
 {
-	menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3);
+	menu_add_select(sym_lookup($2, 0), NULL, $3);
 	printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
 };
 
+config_option: T_SELECT T_WORD expr if_expr T_EOL
+{
+	menu_add_select(sym_lookup($2, 0), $3, $4);
+	printd(DEBUG_PARSE, "%s:%d:select with value\n", zconf_curname(), zconf_lineno());
+};
+
 config_option: T_RANGE symbol symbol if_expr T_EOL
 {
 	menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4);
@@ -642,6 +648,15 @@  static void print_symbol(FILE *out, struct menu *menu)
 		case P_SELECT:
 			fputs( "  select ", out);
 			expr_fprint(prop->expr, out);
+			if (prop->value) {
+				fputs(" (value=", out);
+				expr_fprint(prop->value, out);
+				fputc(')', out);
+			}
+			if (!expr_is_yes(prop->visible.expr)) {
+				fputs(" if ", out);
+				expr_fprint(prop->visible.expr, out);
+			}
 			fputc('\n', out);
 			break;
 		case P_RANGE: