diff -Naur grub-1.99/README.SUBST_PATCH grub-1.99-new/README.SUBST_PATCH
--- grub-1.99/README.SUBST_PATCH	1970-01-01 01:00:00.000000000 +0100
+++ grub-1.99-new/README.SUBST_PATCH	2012-02-28 20:20:02.000000000 +0100
@@ -0,0 +1,149 @@
+Package:     GRUB 1.99
+Patch:       grub-1.99-andreas_vogel_menu_and_subst.patch
+Created:     28.2.2012
+Author:      Andreas Vogel
+Description: 
+
+This patch enhances GRUB 1.99 by the following features:
+
+*) All changes made by this patch are supposed to be backward compatible.
+
+*) New menuentry option -S | --silent=0|1 (hiding the terminal box)
+   When a menuentry is a submenu or when a menuentry is created with the option --silent, then
+   the terminal box is not shown when this menuentry is executed.
+
+*) Reexecution of the menu definition after a menuentry execution
+   After executing a menuentry and when the execution didn't end with a booting action,
+   the menu definition will be reexecuted before showing the menu again.
+   The reason is to be able to have dynamic menus using the values of environment variables.
+
+*) New menuentry option -l | --label=STRING
+   When having dynamic menuentries (e.g. localized entry names), it's quite hard to
+   set the 'default' variable to the entry title.
+   Therefore with the new menuentry --label it is possible to specify the user visible title
+   for a menuentry. If this option isn't set, the menuentry title will be used in order to maintain
+   backward compatibility.
+
+*) New menuentry option -H | --hidden=0|1
+   If the --hidden option is set for a menuentry, the menuentry will not be visible. The
+   activation of the menuentry is only possible by use of the key defined with --hotkey.
+
+*) New menuentry option -E | --enable=0|1
+   A disabled menuentry is shown but cannot be executed. In case the menuentry is a submenu, the
+   submenu will not open if the menuentry is disabled.
+   It's the responsibility of the output module to show an adequate representation of disabled
+   menu entries.
+
+*) Variable substitutions in strings
+   This patch introduces a new general variable substitution function grub_subst() which sunstitutes
+   environment variables in a string. The variable syntax is similar to BASH variable handling.
+   Usage:
+      $x or ${x}  expands to x
+      ${x:-y}     expands to x if x is set and nonempty, otherwise expands to y
+      ${x:=y}     expands to x if x is set and nonempty, otherwise expands to y and sets environment variable x
+      ${x:+y}     expands to x if x is unset or empty, otherwise expands to y
+   The use of single quotes and double quotes is supported. Single characaters may be escaped by backlash.
+
+*) Enhanced hotkey aliases
+   The list of possible hotkey aliases has been extended to cover all possible keys. Case is now
+   insignificant when comparing key alias names.
+   It is now possible to specify any combination of the SHIFT and CTRL modififiers. The order of the
+   modifiers is irelevant.
+   Examples: 
+      "F1" "<SHIFT> F1" "<CTRL> <SHIFT> F1"
+
+*) Bug fix: normal/main.c: grub_normal_free_menu() didn't free all memory
+
+*) Variable substitution for labels defined in theme for gfxmenu
+   Using the new grub_subst() function, all labels in gfxmenu are subject to variable substitution.
+   Therefore it's possible to create labels showing the value of an environment variable. As an example
+   it would be now possible to create a themed debugging screen showing the values of the basic environment
+   variables.
+
+*) Variable substitution in view title defined in theme for gfxmenu
+
+*) Variable substitutions for list entry names in gfxmenu and in text mode menu
+   All menuentry titels/labels are subject to variable substition in order to be able to implement
+   dynamic menue labels.
+   As the menuentry definitions are created as shell functions, unquoted variables are already substituted
+   when the command is executed. Therefore attention is needed because of quoting.
+   Example:
+       menuentry entry1 --silent 1 --label="Set gfxterm to 800x600 (current: \$gfxterm)" {
+           set gfxterm=800x600
+       }
+
+Open items:
+
+*) The option parsing method implemented in lib/arg.c has IMHO a design problem.
+   When defining an option which takes an optional argument by specifying GRUB_ARG_OPTION_OPTIONAL, there
+   will be an ambiguity in a certain situation.
+   Let's assume the option --silent is defined with the flag GRUB_ARG_OPTION_OPTIONAL and the type ARG_TYPE_INT.
+   Given the command line:
+       myprog --silent /dev/sda
+   will fail because /dev/sda, which is meant to be just an argument to myprog, is treated to be the value for the
+   --silent option.
+   This is because the option parsing methods are able to handle both formats: '--silent=1' and '--silent 1'
+   As a consequence, options taking an optional argument can only be used if they are followed either by another option
+   or the string '--' which denotes the end of options. Although this is a working workaround I'm not sure
+   if this is really the way it should be.
+
+Summary:
+
+This patch make it possible to implement customizable menus by using environment variables.
+It is now possible to create customisation menus, e.g. to set the language, screen resolution,
+custom boot options, etc. without the need to hardcode them into different menuentries.
+
+Hidden menus are not shown on the screen and they are opened by hotkeys, e.g. to open the 
+settings menu tree or to open the help system menu tree.
+
+Exported environment variables are not only exported into the environment of a menuentry execution
+context, they are furthermore exported back into the callers environment. In other means exported environment
+variables are global for the menu system.
+
+Complete example:
+
+##########################################################################
+##  GRUB 1.99 configuration file
+##########################################################################
+set theme=$prefix/theme/theme.txt
+set kbd=de
+export kbd theme
+
+menuentry linux --label "TEST: Boot linux (kbd=\$kbd) [CTRL-b]" --hotkey '<CTRL> b' {
+    echo
+    echo "Booting linux with keyboard='$kbd'!"
+    echo
+    echo "Press <RETURN> to continue..."
+    read
+}
+
+menuentry disabled --label "TEST: Disabled entry" --enable 0 {
+    echo "Ooops! Why can we run a disabled menu entry??"
+    echo "Press <RETURN> to continue..."
+    read
+}
+
+function refresh_kbd_menulabels {
+    set curLabel="(*)"
+    if [ "$kbd" = de ] ; then set curDE=$curLabel ; else set curDE="" ; fi
+    if [ "$kbd" = us ] ; then set curUS=$curLabel ; else set curUS="" ; fi
+    set default="$kbd"
+}
+
+## Hidden settings menu... press CTRL-S to open it
+submenu settings --label "TEST: Settings... [CTRL-s]" --hidden 1 --hotkey '<CTRL> s' {
+    submenu keyboard --label 'TEST: Keyboard layout...' {
+        refresh_kbd_menulabels
+
+        menuentry de --label 'German keyboard $curDE' --silent 1 {
+            set kbd=de
+        }
+        menuentry us --label 'English keyboard $curUS' --silent 1 {
+            set kbd=us
+        }
+    }
+}
+
+##########################################################################
+##  END of configuration file
+##########################################################################
diff -Naur grub-1.99/grub-core/Makefile.core.def grub-1.99-new/grub-core/Makefile.core.def
--- grub-1.99/grub-core/Makefile.core.def	2011-04-11 23:14:59.000000000 +0200
+++ grub-1.99-new/grub-core/Makefile.core.def	2012-02-26 21:55:31.000000000 +0100
@@ -1052,6 +1052,7 @@
   common = gfxmenu/gui_util.c;
   common = gfxmenu/gui_string_util.c;
   common = gfxmenu/named_colors.c;
+  common = lib/subst.c;
 };
 
 module = {
@@ -1260,6 +1261,8 @@
   common = script/lexer.c;
   common = script/argv.c;
 
+  common = lib/subst.c;
+
   common = commands/menuentry.c;
 
   common = unidata.c;
@@ -1329,6 +1332,7 @@
 module = {
   name = gfxterm;
   common = term/gfxterm.c;
+  common = lib/subst.c;
   enable = videomodules;
 };
 
diff -Naur grub-1.99/grub-core/commands/legacycfg.c grub-1.99-new/grub-core/commands/legacycfg.c
--- grub-1.99/grub-core/commands/legacycfg.c	2011-04-11 22:11:38.000000000 +0200
+++ grub-1.99-new/grub-core/commands/legacycfg.c	2012-02-28 12:13:01.000000000 +0100
@@ -123,8 +123,8 @@
 		return grub_errno;
 	      }
 	    args[0] = oldname;
-	    grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, NULL,
-					entrysrc, 0);
+	    grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, NULL, NULL,
+					entrysrc, 0, 0, 0, 1);
 	    grub_free (args);
 	    entrysrc[0] = 0;
 	    grub_free (oldname);
@@ -174,7 +174,7 @@
 	  return grub_errno;
 	}
       args[0] = entryname;
-      grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, NULL, entrysrc, 0);
+      grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, NULL, NULL, entrysrc, 0, 0, 0, 1);
       grub_free (args);
     }
 
diff -Naur grub-1.99/grub-core/commands/menuentry.c grub-1.99-new/grub-core/commands/menuentry.c
--- grub-1.99/grub-core/commands/menuentry.c	2011-05-14 01:16:59.000000000 +0200
+++ grub-1.99-new/grub-core/commands/menuentry.c	2012-02-28 16:51:01.000000000 +0100
@@ -25,53 +25,147 @@
 #include <grub/i18n.h>
 #include <grub/normal.h>
 
+enum options
+  {
+    O_LABEL = 0,
+    O_CLASS,
+    O_USERS,
+    O_HOTKEY,
+    O_SOURCE,
+    O_SILENT,
+    O_HIDDEN,
+    O_ENABLE,
+ };
+
 static const struct grub_arg_option options[] =
   {
-    {"class", 1, GRUB_ARG_OPTION_REPEATABLE,
-     N_("Menu entry type."), "STRING", ARG_TYPE_STRING},
-    {"users", 2, 0,
-     N_("Users allowed to boot this entry."), "USERNAME", ARG_TYPE_STRING},
-    {"hotkey", 3, 0,
-     N_("Keyboard key for this entry."), "KEY", ARG_TYPE_STRING},
-    {"source", 4, 0,
-     N_("Menu entry definition as a string."), "STRING", ARG_TYPE_STRING},
+    {"label", 'l', 0,
+              N_("Label for this menu entry."), "STRING", ARG_TYPE_STRING},
+    {"class", 'c', GRUB_ARG_OPTION_REPEATABLE,
+              N_("Menu entry type."), "STRING", ARG_TYPE_STRING},
+    {"users", 'u', 0,
+              N_("Users allowed to boot this entry."), "USERNAME", ARG_TYPE_STRING},
+    {"hotkey", 'h', 0,
+              N_("Keyboard key for this entry."), "KEY", ARG_TYPE_STRING},
+    {"source", 's', 0,
+              N_("Menu entry definition as a string."), "STRING", ARG_TYPE_STRING},
+    {"silent", 'S', 0,
+              N_("If set to 1, terminal box is not shown when executing entry."), "[0|1]", ARG_TYPE_INT},
+    {"hidden", 'H', 0,
+              N_("If set to 1, entry is not shown in menu and can only be executed via hotkey."), "[0|1]", ARG_TYPE_INT},
+    {"enable", 'E', 0,
+              N_("If set to 0, entry is shown but cannot be operated."), "[0|1]", ARG_TYPE_INT},
     {0, 0, 0, 0, 0, 0}
   };
 
+
 static struct
 {
   char *name;
   int key;
 } hotkey_aliases[] =
   {
-    {"backspace", '\b'},
-    {"tab", '\t'},
-    {"delete", GRUB_TERM_KEY_DC},
-    {"insert", GRUB_TERM_KEY_INSERT},
-    {"f1", GRUB_TERM_KEY_F1},
-    {"f2", GRUB_TERM_KEY_F2},
-    {"f3", GRUB_TERM_KEY_F3},
-    {"f4", GRUB_TERM_KEY_F4},
-    {"f5", GRUB_TERM_KEY_F5},
-    {"f6", GRUB_TERM_KEY_F6},
-    {"f7", GRUB_TERM_KEY_F7},
-    {"f8", GRUB_TERM_KEY_F8},
-    {"f9", GRUB_TERM_KEY_F9},
-    {"f10", GRUB_TERM_KEY_F10},
-    {"f11", GRUB_TERM_KEY_F11},
-    {"f12", GRUB_TERM_KEY_F12},
+    { "backspace", '\b'},
+    { "tab",       '\t'},
+    { "delete",    GRUB_TERM_KEY_DC },
+    { "insert",    GRUB_TERM_KEY_INSERT },
+    { "up",        GRUB_TERM_KEY_UP },
+    { "down",      GRUB_TERM_KEY_DOWN },
+    { "left",      GRUB_TERM_KEY_LEFT },
+    { "right",     GRUB_TERM_KEY_RIGHT },
+    { "prev",      GRUB_TERM_KEY_PPAGE },
+    { "next",      GRUB_TERM_KEY_NPAGE },
+    { "home",      GRUB_TERM_KEY_HOME },
+    { "end",       GRUB_TERM_KEY_END },
+    { "f1",        GRUB_TERM_KEY_F1 },
+    { "f2",        GRUB_TERM_KEY_F2 },
+    { "f3",        GRUB_TERM_KEY_F3 },
+    { "f4",        GRUB_TERM_KEY_F4 },
+    { "f5",        GRUB_TERM_KEY_F5 },
+    { "f6",        GRUB_TERM_KEY_F6 },
+    { "f7",        GRUB_TERM_KEY_F7 },
+    { "f8",        GRUB_TERM_KEY_F8 },
+    { "f9",        GRUB_TERM_KEY_F9 },
+    { "f10",       GRUB_TERM_KEY_F10 },
+    { "f11",       GRUB_TERM_KEY_F11 },
+    { "f12",       GRUB_TERM_KEY_F12 },
   };
 
+/* Translate a string into a GRUB_TERM key.
+**
+** The given string can be one of the predefined key names (aliases),
+** otherwise the first character of the given str is taken as key.
+** Comparison with alias names are always case independent.
+*/
+static int
+StringToKey (const char *string)
+{
+    int key = 0;
+    int i;
+    char *str, *ptr, *keystr;
+
+    if (string == 0 || *string == 0)
+        return 0;
+
+    if ((str = grub_strdup (string)) == 0)
+        return 0;
+    
+#define STRING_CTRL  "<CTRL>"
+#define STRING_SHIFT "<SHIFT>"
+
+    if ((ptr = grub_strstr (str, STRING_CTRL)) != 0)
+    {
+        for (i = 0; i < grub_strlen (STRING_CTRL); i++)
+            *(ptr + i) = ' ';
+        key |= GRUB_TERM_CTRL;
+    }
+    if ((ptr = grub_strstr (str, STRING_SHIFT)) != 0)
+    {
+        for (i = 0; i < grub_strlen (STRING_SHIFT); i++)
+            *(ptr + i) = ' ';
+        key |= GRUB_TERM_SHIFT;
+    }
+
+    /* remove/zero trailing spaces */
+    for (ptr = str + grub_strlen (str) - 1; ptr >= str && *ptr == ' '; ptr--)
+        *ptr = 0;
+
+    /* skip leading spaces */
+    for (ptr = str; *ptr == ' '; ptr++) /*NOTHING*/;
+
+    /* the remaining chars are the real key string */
+    keystr = ptr;
+
+    /* check if the key string is an alias name */
+    for (i = 0; i < ARRAY_SIZE (hotkey_aliases); i++)
+        if (grub_strcasecmp (keystr, hotkey_aliases[i].name) == 0)
+        {
+            key |= hotkey_aliases[i].key;
+            break;
+        }
+
+    /* no alias name matched, so use the first char for the key */
+    if (i == ARRAY_SIZE (hotkey_aliases))
+        key |= keystr[0];
+
+    grub_free (str);
+
+    return key;
+}
+
 /* Add a menu entry to the current menu context (as given by the environment
    variable data slot `menu').  As the configuration file is read, the script
    parser calls this when a menu entry is to be created.  */
 grub_err_t
-grub_normal_add_menu_entry (int argc, const char **args, char **classes,
+grub_normal_add_menu_entry (int argc, const char **args, 
+                const char *label,
+                char **classes,
 			    const char *users, const char *hotkey,
 			    const char *prefix, const char *sourcecode,
-			    int submenu)
+			    int submenu, int silent, int hidden, int enabled)
 {
   int menu_hotkey = 0;
+  char *menu_label = NULL;
   char **menu_args = NULL;
   char *menu_users = NULL;
   char *menu_title = NULL;
@@ -85,7 +179,10 @@
   if (! menu)
     return grub_error (GRUB_ERR_MENU, "no menu context");
 
-  last = &menu->entry_list;
+  if (hidden)
+      last = &menu->hidden_entry_list;
+  else
+      last = &menu->entry_list;
 
   menu_sourcecode = grub_xasprintf ("%s%s", prefix ?: "", sourcecode);
   if (! menu_sourcecode)
@@ -116,17 +213,7 @@
     }
 
   if (hotkey)
-    {
-      unsigned i;
-      for (i = 0; i < ARRAY_SIZE (hotkey_aliases); i++)
-	if (grub_strcmp (hotkey, hotkey_aliases[i].name) == 0)
-	  {
-	    menu_hotkey = hotkey_aliases[i].key;
-	    break;
-	  }
-      if (i == ARRAY_SIZE (hotkey_aliases))
-	menu_hotkey = hotkey[0];
-    }
+    menu_hotkey = StringToKey (hotkey);
 
   if (! argc)
     {
@@ -138,6 +225,10 @@
   if (! menu_title)
     goto fail;
 
+  menu_label = (label) ? grub_strdup (label) : grub_strdup (menu_title);
+  if (! menu_label)
+    goto fail;
+
   /* Save argc, args to pass as parameters to block arg later. */
   menu_args = grub_malloc (sizeof (char*) * (argc + 1));
   if (! menu_args)
@@ -163,6 +254,7 @@
     goto fail;
 
   (*last)->title = menu_title;
+  (*last)->label = menu_label;
   (*last)->hotkey = menu_hotkey;
   (*last)->classes = menu_classes;
   if (menu_users)
@@ -172,8 +264,14 @@
   (*last)->args = menu_args;
   (*last)->sourcecode = menu_sourcecode;
   (*last)->submenu = submenu;
+  (*last)->silent = silent;
+  (*last)->enabled = enabled;
+
+  if (hidden)
+      menu->hidden_size++;
+  else
+      menu->size++;
 
-  menu->size++;
   return GRUB_ERR_NONE;
 
  fail:
@@ -195,6 +293,7 @@
 
   grub_free (menu_users);
   grub_free (menu_title);
+  grub_free (menu_label);
   return grub_errno;
 }
 
@@ -247,19 +346,25 @@
   if (! argc)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing arguments");
 
-  if (ctxt->state[3].set && ctxt->script)
+  if (ctxt->state[O_SOURCE].set && ctxt->script)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "multiple menuentry definitions");
 
-  if (! ctxt->state[3].set && ! ctxt->script)
+  if (! ctxt->state[O_SOURCE].set && ! ctxt->script)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "no menuentry definition");
 
   if (! ctxt->script)
-    return grub_normal_add_menu_entry (argc, (const char **) args,
-				       (ctxt->state[0].set ? ctxt->state[0].args
-					: NULL), ctxt->state[1].arg,
-				       ctxt->state[2].arg, 0,
-				       ctxt->state[3].arg,
-				       ctxt->extcmd->cmd->name[0] == 's');
+    return grub_normal_add_menu_entry (
+      argc, (const char **) args,
+      ctxt->state[O_LABEL].arg,
+      ctxt->state[O_CLASS].set ? ctxt->state[O_CLASS].args : NULL,
+      ctxt->state[O_USERS].arg,
+      ctxt->state[O_HOTKEY].arg,
+      NULL, ctxt->state[O_SOURCE].arg,
+      ctxt->extcmd->cmd->name[0] == 's',
+      ctxt->state[O_SILENT].set ? (ctxt->state[O_SILENT].arg ? ctxt->state[O_SILENT].arg[0] == '1' : 1) : 0,
+      ctxt->state[O_HIDDEN].set ? (ctxt->state[O_HIDDEN].arg ? ctxt->state[O_HIDDEN].arg[0] == '1' : 1) : 0,
+      ctxt->state[O_ENABLE].set ? (ctxt->state[O_ENABLE].arg ? ctxt->state[O_ENABLE].arg[0] != '0' : 1) : 1
+    );
 
   src = args[argc - 1];
   args[argc - 1] = NULL;
@@ -272,14 +377,23 @@
   if (! prefix)
     return grub_errno;
 
-  r = grub_normal_add_menu_entry (argc - 1, (const char **) args,
-				  ctxt->state[0].args, ctxt->state[1].arg,
-				  ctxt->state[2].arg, prefix, src + 1,
-				  ctxt->extcmd->cmd->name[0] == 's');
+  r = grub_normal_add_menu_entry (
+      argc - 1, (const char **) args,
+      ctxt->state[O_LABEL].arg,
+      ctxt->state[O_CLASS].set ? ctxt->state[O_CLASS].args : NULL,
+      ctxt->state[O_USERS].arg,
+      ctxt->state[O_HOTKEY].arg,
+      prefix, src + 1,
+      ctxt->extcmd->cmd->name[0] == 's',
+      ctxt->state[O_SILENT].set ? (ctxt->state[O_SILENT].arg ? ctxt->state[O_SILENT].arg[0] == '1' : 1) : 0,
+      ctxt->state[O_HIDDEN].set ? (ctxt->state[O_HIDDEN].arg ? ctxt->state[O_HIDDEN].arg[0] == '1' : 1) : 0,
+      ctxt->state[O_ENABLE].set ? (ctxt->state[O_ENABLE].arg ? ctxt->state[O_ENABLE].arg[0] != '0' : 1) : 1
+  );
 
   src[len - 1] = ch;
   args[argc - 1] = src;
   grub_free (prefix);
+
   return r;
 }
 
diff -Naur grub-1.99/grub-core/gfxmenu/gui_label.c grub-1.99-new/grub-core/gfxmenu/gui_label.c
--- grub-1.99/grub-core/gfxmenu/gui_label.c	2010-12-01 15:45:43.000000000 +0100
+++ grub-1.99-new/grub-core/gfxmenu/gui_label.c	2012-02-26 12:05:15.000000000 +0100
@@ -22,6 +22,7 @@
 #include <grub/gui.h>
 #include <grub/font.h>
 #include <grub/gui_string_util.h>
+#include <grub/subst.h>
 
 static const char *align_options[] =
 {
@@ -89,28 +90,34 @@
   if (!grub_video_have_common_points (region, &self->bounds))
     return;
 
+  char *tsubst = grub_subst (self->text);
+
   /* Calculate the starting x coordinate.  */
   int left_x;
   if (self->align == align_left)
     left_x = 0;
   else if (self->align == align_center)
     left_x = ((self->bounds.width
-               - grub_font_get_string_width (self->font, self->text))
+               - grub_font_get_string_width (self->font, tsubst))
              ) / 2;
   else if (self->align == align_right)
     left_x = (self->bounds.width
-              - grub_font_get_string_width (self->font, self->text));
+              - grub_font_get_string_width (self->font, tsubst));
   else
+  {  
+    grub_free (tsubst);
     return;   /* Invalid alignment.  */
+  }
 
   grub_video_rect_t vpsave;
   grub_gui_set_viewport (&self->bounds, &vpsave);
-  grub_font_draw_string (self->text,
+  grub_font_draw_string (tsubst,
                          self->font,
                          grub_gui_map_color (self->color),
                          left_x,
                          grub_font_get_ascent (self->font));
   grub_gui_restore_viewport (&vpsave);
+  grub_free (tsubst);
 }
 
 static void
@@ -144,8 +151,10 @@
 static void
 label_get_minimal_size (void *vself, unsigned *width, unsigned *height)
 {
+  char *tsubst;
   grub_gui_label_t self = vself;
-  *width = grub_font_get_string_width (self->font, self->text);
+  tsubst = grub_subst (self->text);
+  *width = grub_font_get_string_width (self->font, tsubst);
   *height = (grub_font_get_ascent (self->font)
              + grub_font_get_descent (self->font));
 }
diff -Naur grub-1.99/grub-core/gfxmenu/gui_list.c grub-1.99-new/grub-core/gfxmenu/gui_list.c
--- grub-1.99/grub-core/gfxmenu/gui_list.c	2011-04-19 00:40:46.000000000 +0200
+++ grub-1.99-new/grub-core/gfxmenu/gui_list.c	2012-02-27 08:27:42.000000000 +0100
@@ -23,6 +23,7 @@
 #include <grub/gui_string_util.h>
 #include <grub/gfxmenu_view.h>
 #include <grub/gfxwidgets.h>
+#include <grub/subst.h>
 
 struct grub_gui_list_impl
 {
@@ -263,8 +264,16 @@
                                 item_top + (item_height - self->icon_height) / 2,
                                 0, 0, self->icon_width, self->icon_height);
 
-      const char *item_title =
-        grub_menu_get_entry (self->view->menu, menu_index)->title;
+      char *item_label = grub_subst (grub_menu_get_entry (self->view->menu, menu_index)->label);
+      int nofree = 0;
+      if (item_label == 0)
+          item_label = grub_strdup (grub_menu_get_entry (self->view->menu, menu_index)->title);
+      if (item_label == 0)
+      {
+          item_label = "";
+          nofree = 1;
+      }
+      
       grub_font_t font =
         (is_selected && self->selected_item_font
          ? self->selected_item_font
@@ -273,13 +282,16 @@
         ((is_selected && self->selected_item_color_set)
          ? self->selected_item_color
          : self->item_color);
-      grub_font_draw_string (item_title,
+      grub_font_draw_string (item_label,
                              font,
                              grub_gui_map_color (text_color),
                              sel_leftpad + self->icon_width + icon_text_space,
                              (item_top + (item_height - (ascent + descent))
                               / 2 + ascent));
 
+      if (! nofree)
+          grub_free (item_label);
+
       item_top += item_height + item_vspace;
     }
   grub_video_set_viewport (oviewport.x,
diff -Naur grub-1.99/grub-core/gfxmenu/view.c grub-1.99-new/grub-core/gfxmenu/view.c
--- grub-1.99/grub-core/gfxmenu/view.c	2010-12-01 15:45:43.000000000 +0100
+++ grub-1.99-new/grub-core/gfxmenu/view.c	2012-02-27 07:55:55.000000000 +0100
@@ -36,6 +36,7 @@
 #include <grub/gfxmenu_view.h>
 #include <grub/gui_string_util.h>
 #include <grub/icon_manager.h>
+#include <grub/subst.h>
 
 static void
 init_terminal (grub_gfxmenu_view_t view);
@@ -143,15 +144,20 @@
   if (! view->title_text)
     return;
 
+  char *title_text = grub_subst (view->title_text);
+  if (title_text == 0)
+    return;
+
   /* Center the title. */
   int title_width = grub_font_get_string_width (view->title_font,
-                                                view->title_text);
+                                                title_text);
   int x = (view->screen.width - title_width) / 2;
   int y = 40 + grub_font_get_ascent (view->title_font);
-  grub_font_draw_string (view->title_text,
+  grub_font_draw_string (title_text,
                          view->title_font,
                          grub_gui_map_color (view->title_color),
                          x, y);
+  grub_free (title_text);
 }
 
 struct progress_value_data
@@ -399,13 +405,11 @@
 {
   grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata;
 
-  char *s = grub_malloc (100 + grub_strlen (entry->title));
-  if (!s)
-    return;
-
-  grub_sprintf (s, "Booting '%s'", entry->title);
+  char *label = entry->label ? grub_subst (entry->label) : grub_strdup (entry->title);
+  char *s = grub_xasprintf ("Booting '%s'", label ? label : "");
   set_progress_message (view, s);
   grub_free (s);
+  grub_free (label);
   grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
   grub_video_swap_buffers ();
   if (view->double_repaint)
@@ -417,13 +421,11 @@
 {
   grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata;
 
-  char *s = grub_malloc (100 + grub_strlen (entry->title));
-  if (!s)
-    return;
-
-  grub_sprintf (s, "Falling back to '%s'", entry->title);
+  char *label = entry->label ? grub_subst (entry->label) : grub_strdup (entry->title);
+  char *s = grub_xasprintf ("Falling back to '%s'", label ? label : "");
   set_progress_message (view, s);
   grub_free (s);
+  grub_free (label);
   grub_gfxmenu_view_redraw (view, &view->progress_message_frame);
   grub_video_swap_buffers ();
   if (view->double_repaint)
diff -Naur grub-1.99/grub-core/lib/subst.c grub-1.99-new/grub-core/lib/subst.c
--- grub-1.99/grub-core/lib/subst.c	1970-01-01 01:00:00.000000000 +0100
+++ grub-1.99-new/grub-core/lib/subst.c	2012-02-26 22:00:00.000000000 +0100
@@ -0,0 +1,462 @@
+/*
+** subst.c - substitute variables in a string by GRUB2 environment variables
+**
+** Copyright (C) 2012 Andreas Vogel
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+** copies of the Software, and to permit persons to whom the Software is
+** furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+** THE SOFTWARE.
+*/
+
+#ifdef STANDALONE
+#    include <stdio.h>
+#    include <stdlib.h>
+#    include <stdarg.h>
+#    include <string.h>
+#    include "compat.h"
+#    include "subst.h"
+#else
+#    include <grub/types.h>
+#    include <grub/misc.h>
+#    include <grub/mm.h>
+#    include <grub/err.h>
+#    include <grub/env.h>
+#    include <grub/subst.h>
+#endif
+
+static void
+Debug (char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+#ifdef DEBUG
+    vfprintf (stderr, fmt, ap);
+#endif
+    va_end (ap);
+}
+
+static char *
+find_matching (char *str, char c)
+{
+    char *ptr;
+
+    Debug ("find_matching(%s,%c)\n", str, c);
+
+    if (str == 0 || *str == 0)
+        return 0;
+
+    ptr = str;
+    while (*ptr)
+    {
+        Debug ("find_matching(%s,%c): search: '%c' in '%s'\n", ptr, c, c, ptr);
+
+        if (*ptr == c)
+        {
+            Debug ("find_matching(%s,%c): found: '%c' in '%s'\n", ptr, c, c, ptr);
+            return ptr;
+        }
+        else if (*ptr == '\\')
+        {
+            ptr++;
+            if (*ptr)
+                ptr++;
+        }
+        else if (*ptr == '\'')
+        {
+            if ((ptr = find_matching (ptr + 1, '\'')) == 0)
+                return 0;
+            ptr++;
+        }
+        else if (*ptr == '"')
+        {
+            if ((ptr = find_matching (ptr + 1, '"')) == 0)
+                return 0;
+            ptr++;
+        }
+        else if (*ptr == '$' && *(ptr + 1) == '{')
+        {
+            if ((ptr = find_matching (ptr + 2, '}')) == 0)
+                return 0;
+            ptr++;
+        }
+        else if (*ptr == '$' && *(ptr + 1) == '(')
+        {
+            if ((ptr = find_matching (ptr + 2, ')')) == 0)
+                return 0;
+            ptr++;
+        }
+        else
+        {
+            ptr++;
+        }
+    }
+    return 0;
+}
+
+static char *
+string_add (char **result, const char *str)
+{
+    char *tmpstr;
+
+    if (result == 0)
+        return 0;
+
+    if (str == 0 || (*result != 0 && *str == 0))
+        return *result;
+
+    if ((tmpstr = grub_xasprintf ("%s%s", (*result == 0) ? "" : *result, str)) == 0)
+        return 0;
+
+    grub_free (*result);
+    *result = tmpstr;
+
+    return *result;
+}
+
+static char *
+string_add2 (char **result, char *start, char *end)
+{
+    char *retval;
+    char prev = *end;
+
+    *end = 0;
+    retval = string_add (result, start);
+    *end = prev;
+
+    return retval;
+}
+
+
+
+static char *
+check_for_escape (char **result, char *scp)
+{
+    if (*result == 0)
+    {
+        grub_free (*result);
+        *result = 0;
+    }
+
+    /* handle escapes: '\<c>' --> '<c>' */
+    if (*scp == '\\' && *(scp + 1) != 0)
+    {
+        if ((*result = grub_xasprintf ("%c", *(scp + 1))) != 0)
+        {
+            scp += 2;
+            return scp;
+        }
+    }
+    return 0;
+}
+
+static char *
+check_for_squote (char **result, char *scp)
+{
+    if (*result == 0)
+    {
+        grub_free (*result);
+        *result = 0;
+    }
+
+    if (*scp == '\'')
+    {
+        char *start = scp + 1;
+        char *end = find_matching (start, '\'');
+        char prev;
+
+        if (end == 0)
+            end = start + grub_strlen (start);
+
+        prev = *end;
+        *end = 0;
+        *result = grub_xasprintf ("%s", start);
+        *end = prev;
+
+        if (*result != 0)
+        {
+            scp = end;
+            if (*scp)
+                scp++;
+            return scp;
+        }
+    }
+    return 0;
+}
+
+static char *
+check_for_dquote (char **result, char *scp)
+{
+    if (*result == 0)
+    {
+        grub_free (*result);
+        *result = 0;
+    }
+
+    if (*scp == '"')
+    {
+        char *start = scp + 1;
+        char *end = find_matching (start, '"');
+
+        if (end == 0)
+        {
+            end = start + grub_strlen (start);
+            *result = grub_xasprintf ("%s", start);
+        }
+        else
+        {
+            char *sub;
+            char prev = *end;
+
+            *end = 0;
+            if ((sub = grub_subst (start)) != 0)
+            {
+                *result = grub_xasprintf ("%s", sub);
+                grub_free (sub);
+            }
+            *end = prev;
+        }
+
+        if (*result != 0)
+        {
+            scp = end;
+            if (*scp)
+                scp++;
+            return scp;
+        }
+    }
+    return 0;
+}
+
+static char *
+check_for_dollar (char **result, char *scp)
+{
+    int term_chars = 0;
+    int eval_subst = 0;
+    char *vstart = scp + 1;
+    char *vend;
+    char *var_value = 0;
+
+    if (*result == 0)
+    {
+        grub_free (*result);
+        *result = 0;
+    }
+
+    if (*scp != '$')
+        return 0;
+
+    if (*vstart == '{')
+    {
+        vstart++;
+        vend = find_matching (vstart, '}');
+        term_chars = 1;
+    }
+    else if (*vstart == '(')
+    {
+        vstart++;
+        vend = find_matching (vstart, ')');
+        term_chars = 1;
+        eval_subst = 1;
+    }
+    else
+    {
+        vend = vstart;
+        /* a variable name has alphanumeric chars and underline */
+        while (*vend && (grub_isalnum (*vend) || *vend == '_'))
+            vend++;
+    }
+
+    if (vend == 0)
+        return 0;
+
+    /* var name length needs to be >= 1 (handle '${}') */
+    if (vend - vstart >= 1)
+    {
+        if (eval_subst != 0)
+        {
+            /* handle '$(...)' */
+            char *str = vstart;
+            char prev = *vend;
+            char *sub_value;
+
+            *vend = 0;
+            sub_value = grub_subst (str);
+            *vend = prev;
+
+            var_value = grub_subst (sub_value);
+
+            grub_free (sub_value);
+        }
+        else
+        {
+            char *var_name = vstart;
+            char prev = *vend;
+            char *s;
+
+            *vend = 0;
+            if ((s = grub_strstr (var_name, ":-")) != 0 
+                || (s = grub_strstr (var_name, ":=")) != 0
+                || (s = grub_strstr (var_name, ":+")) != 0
+            )
+            {
+                *s = 0;
+                if ((var_value = grub_env_get (var_name)) != 0)
+                {
+                    /* we found the environment variable */
+                    if (*(s + 1) == '+')
+                    {
+                        /* this is the ':+' case: substitute the expanded alternative value */
+                        var_value = grub_subst (s + 2);
+                    }
+                    else
+                        var_value = grub_strdup (var_value);
+                }
+                else
+                {
+                    /* we couldn't find the environment variable */
+                    if (*(s + 1) == '+')
+                    {
+                        /* this is the ':+' case: expand nothing */
+                        var_value = 0;
+                    }
+                    else
+                    {
+                        /* this is the ':-' or ':=' case: substitute the expanded alternative value */
+                        var_value = grub_subst (s + 2);
+                    }
+
+                    /* additionally set the environment variable to the default value if ':=' is given */
+                    if (*(s + 1) == '=')
+                        grub_env_set (var_name, (var_value == 0) ? "" : var_value);
+                }
+                *s = ':';
+            }
+            else
+            {
+                if ((var_value = grub_env_get (var_name)) != 0)
+                    var_value = grub_strdup (var_value);
+                else
+                    var_value = 0;
+            }
+            *vend = prev;
+        }
+    }
+
+    *result = grub_xasprintf ("%s", (var_value == 0) ? "" : var_value);
+
+    grub_free (var_value);
+
+    if (*result != 0)
+    {
+        scp = vend + term_chars;
+        return scp;
+    }
+
+    return 0;
+}
+
+
+/*
+**  Substitute variables in the given string with the value of the equally named
+**  environment/context variables and return the result as a dynamically allocated string.
+**  The caller of this function is responsible to free the string later on.
+**    +) If the given string is the null pointer, the null pointer will be returned. In all other cases, even
+**       in case of errors, a fresh allocated string is returned. In case of errors, e.g. environment var not found,
+**       an empty string is returned.
+**    +) Variables are to be in the form '$var' or '${var}'.
+**    +) Any occurence of '$(<string>)' will be substituted with the expansion of the expanded arg '<string>'.
+**    +) Like in BASH it's possible to specify '${var:-defval}'. If var is unset or empty
+**       defval will be substituted, otherwise the expansion of var will be substituted.
+**    +) Like in BASH it's possible to specify '${var:=defval}'. If var is unset or empty
+**       defval will be substituted and var will be set to defval, otherwise the expansion of var will be substituted.
+**    +) Like in BASH it's possible to specify '${var:+altval}'. If var is unset or empty
+**       nothing will be substututed, otherwise the expansion of altval will be substituted.
+**    +) Escapes are handled: '\<c>' --> '<c>'
+**    +) Text in single quotes doesn't get expanded, just the single quotes are removed.
+**    +) Text in double quotes get's expanded, the quotes are removed.
+*/
+char *
+grub_subst (const char *string)
+{
+    char *result = 0;
+    char *scp;
+    char *lcp;
+    char *copy_string;
+
+    if (string == 0)
+        return grub_strdup ("");
+
+    /* performance shortcut: return an alloacted empty string if string is an empty string */
+    if (*string == 0)
+        return grub_strdup ("");
+
+    /* we do need a copy of the string to work with */
+    if ((copy_string = grub_strdup (string)) == 0)
+        return grub_strdup ("");
+
+    scp = lcp = copy_string;
+    while (*scp)
+    {
+        char *new_scp = 0;
+        char *substr = 0;
+
+        if (new_scp == 0)
+            new_scp = check_for_escape (&substr, scp);
+        if (new_scp == 0)
+            new_scp = check_for_squote (&substr, scp);
+        if (new_scp == 0)
+            new_scp = check_for_dquote (&substr, scp);
+        if (new_scp == 0)
+            new_scp = check_for_dollar (&substr, scp);
+
+        if (new_scp != 0)
+        {
+            /* add unhandled input chars */
+            if (lcp < scp)
+                string_add2 (&result, lcp, scp);
+            /* add substitution */
+            string_add (&result, substr);
+            grub_free (substr);
+            scp = lcp = new_scp;
+        }
+        else
+            scp++;
+    }
+
+    /* add unhandled input chars */
+    if (lcp < scp)
+        string_add2 (&result, lcp, scp);
+
+    grub_free (copy_string);
+
+    return result;
+}
+
+
+char *
+grub_subst_v (const char *string, char **r_var)
+{
+    char *result = grub_subst (string);
+    if (r_var != 0)
+    {
+        grub_free (*r_var);
+        *r_var = result;
+    }
+    return result;
+}
+
diff -Naur grub-1.99/grub-core/normal/context.c grub-1.99-new/grub-core/normal/context.c
--- grub-1.99/grub-core/normal/context.c	2011-02-12 08:24:20.000000000 +0100
+++ grub-1.99-new/grub-core/normal/context.c	2012-02-28 15:42:22.000000000 +0100
@@ -123,14 +123,28 @@
   for (i = 0; i < HASHSZ; i++)
     {
       struct grub_env_var *p, *q;
+      struct grub_env_context *tmp_context;
 
       for (p = grub_current_context->vars[i]; p; p = q)
-	{
-	  q = p->next;
-          grub_free (p->name);
-	  grub_free (p->value);
-	  grub_free (p);
-	}
+      {
+        if (p->global)
+        {
+          /* re-export all global variables into the calling/previous context */
+          tmp_context = grub_current_context;
+          grub_current_context = grub_current_context->prev;
+          if (grub_env_set (p->name, p->value) == GRUB_ERR_NONE)
+          {
+            grub_env_export (p->name);
+            grub_register_variable_hook (p->name, p->read_hook, p->write_hook);
+          }
+          grub_current_context = tmp_context;
+        }
+
+        q = p->next;
+        grub_free (p->name);
+        grub_free (p->value);
+        grub_free (p);
+      }
     }
 
   /* Restore the previous context.  */
@@ -170,6 +184,12 @@
       
       *last = menu->entry_list;
       menu2->size += menu->size;
+
+      last = &menu2->hidden_entry_list;
+      while (*last)
+          last = &(*last)->next;
+      *last = menu->hidden_entry_list;
+      menu2->hidden_size += menu->hidden_size;
     }
 
   grub_extractor_level--;
diff -Naur grub-1.99/grub-core/normal/main.c grub-1.99-new/grub-core/normal/main.c
--- grub-1.99/grub-core/normal/main.c	2011-04-11 22:04:03.000000000 +0200
+++ grub-1.99-new/grub-core/normal/main.c	2012-02-28 09:05:30.000000000 +0100
@@ -128,16 +128,39 @@
 void
 grub_normal_free_menu (grub_menu_t menu)
 {
-  grub_menu_entry_t entry = menu->entry_list;
+  grub_menu_entry_t entry_lists[] = { menu->entry_list, menu->hidden_entry_list };
+  int i;
 
-  while (entry)
+  for (i = 0; i < ARRAY_SIZE (entry_lists); i++)
+  {
+    grub_menu_entry_t entry = entry_lists[i];
+
+    /* TODO FIXME: this code should be a function inside menuentry.c as the allocation
+    ** of dynamic memory for a menu entry is done there in a quite specific way (e.g. entry->classes).
+    */
+    while (entry)
     {
-      grub_menu_entry_t next_entry = entry->next;
+        grub_menu_entry_t next_entry = entry->next;
+
+        {
+            int i;
+
+            for (i = 0; entry->classes && entry->classes[i].name; i++)
+                grub_free (entry->classes[i].name);
+            grub_free (entry->classes);
+
+            for (i = 0; entry->args && entry->args[i]; i++)
+                grub_free (entry->args[i]);
+            grub_free (entry->args);
+        }
 
-      grub_free ((void *) entry->title);
-      grub_free ((void *) entry->sourcecode);
-      entry = next_entry;
+        grub_free (entry->users);
+        grub_free (entry->title);
+        grub_free (entry->label);
+        grub_free (entry->sourcecode);
+        entry = next_entry;
     }
+  }
 
   grub_free (menu);
   grub_env_unset_menu ();
diff -Naur grub-1.99/grub-core/normal/menu.c grub-1.99-new/grub-core/normal/menu.c
--- grub-1.99/grub-core/normal/menu.c	2011-04-19 15:57:59.000000000 +0200
+++ grub-1.99-new/grub-core/normal/menu.c	2012-02-28 19:23:39.000000000 +0100
@@ -32,6 +32,7 @@
 #include <grub/script_sh.h>
 #include <grub/gfxterm.h>
 #include <grub/dl.h>
+#include <grub/subst.h>
 
 /* Time to delay after displaying an error message about a default/fallback
    entry failing to boot.  */
@@ -67,9 +68,18 @@
 grub_menu_get_entry (grub_menu_t menu, int no)
 {
   grub_menu_entry_t e;
+  grub_menu_entry_t list;
 
-  for (e = menu->entry_list; e && no > 0; e = e->next, no--)
-    ;
+  if (no >= GRUB_MENU_HIDDEN_BASE_IDX)
+  {
+    no -= GRUB_MENU_HIDDEN_BASE_IDX;
+    list = menu->hidden_entry_list;
+  }
+  else
+    list = menu->entry_list;
+
+  for (e = list; e && no > 0; e = e->next, no--)
+    /*NOTHING*/;
 
   return e;
 }
@@ -165,6 +175,10 @@
   char *optr, *buf, *oldchosen = NULL, *olddefault = NULL;
   const char *ptr, *chosen, *def;
   grub_size_t sz = 0;
+  int finish;
+
+  if (!entry->enabled)
+    return;
 
   if (entry->restricted)
     err = grub_auth_check_authentication (entry->users);
@@ -246,24 +260,47 @@
     grub_env_set ("default", ptr + 1);
   else
     grub_env_unset ("default");
-  grub_script_execute_sourcecode (entry->sourcecode, entry->argc, entry->args);
 
-  if (errs_before != grub_err_printed_errors)
-    grub_wait_after_message ();
+  for (finish = 0; !finish; /*nothing*/)
+  {
+    grub_script_execute_sourcecode (entry->sourcecode, entry->argc, entry->args);
 
-  if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
-    /* Implicit execution of boot, only if something is loaded.  */
-    grub_command_execute ("boot", 0, 0);
+    if (errs_before != grub_err_printed_errors)
+      grub_wait_after_message ();
 
-  if (entry->submenu)
+    if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
+    {
+      /* Implicit execution of boot, only if something is loaded.  */
+      grub_command_execute ("boot", 0, 0);
+      finish = 1;
+    }
+
+    if (entry->submenu)
     {
       if (menu && menu->size)
-	{
-	  grub_show_menu (menu, 1, auto_boot);
-	  grub_normal_free_menu (menu);
-	}
+	  {
+        grub_env_unset ("grub_reload_menu");
+	    grub_show_menu (menu, 1, auto_boot);
+	    grub_normal_free_menu (menu);
+
+        if (grub_env_get ("grub_reload_menu"))
+        {
+          /* We're supposed to reload the menu and show it again.
+          ** Delete the actual menu and rerun this loop...
+          */
+          if ((menu = grub_zalloc (sizeof (*menu))))
+          {
+            grub_env_set_menu (menu);
+            continue;
+          }
+        }
+	  }
+
       grub_env_context_close ();
     }
+    finish = 1;
+  }
+
   if (oldchosen)
     grub_env_set ("chosen", oldchosen);
   else
@@ -288,6 +325,9 @@
 {
   int fallback_entry;
 
+  if (!entry->enabled)
+    return;
+
   callback->notify_booting (entry, callback_data);
 
   grub_menu_execute_entry (entry, 1);
@@ -548,12 +588,18 @@
 	}
 
       if (timeout == 0)
-	{
-	  grub_env_unset ("timeout");
-          *auto_boot = 1;
-	  menu_fini ();
-	  return default_entry;
-	}
+      {
+        grub_menu_entry_t e = grub_menu_get_entry (menu, default_entry);
+
+        grub_env_unset ("timeout");
+        *auto_boot = 1;
+
+        if (!e || e->enabled)
+        {
+          menu_fini ();
+          return default_entry;
+        }
+      }
 
       if (grub_checkkey () >= 0 || timeout < 0)
 	{
@@ -618,11 +664,19 @@
 	    case '\r':
 	    case GRUB_TERM_KEY_RIGHT:
 	    case GRUB_TERM_CTRL | 'f':
-	      menu_fini ();
+          {  
+		    grub_menu_entry_t e = grub_menu_get_entry (menu, current_entry);
+            if (e && e->enabled)
+            {
+              menu_fini ();
               *auto_boot = 0;
-	      return current_entry;
+              return current_entry;
+            }
+          }
+          break;
 
 	    case '\e':
+	    case GRUB_TERM_KEY_LEFT:
 	      if (nested)
 		{
 		  menu_fini ();
@@ -645,24 +699,46 @@
 	      goto refresh;
 
 	    default:
-	      {
-		grub_menu_entry_t entry;
-		int i;
-		for (i = 0, entry = menu->entry_list; i < menu->size;
-		     i++, entry = entry->next)
-		  if (entry->hotkey == c)
-		    {
+	    {
+		  grub_menu_entry_t entry;
+		  int i, found = -1;
+
+          /* Search the entry list for an entry having the input char as hotkey. */
+		  for (i = 0, entry = menu->entry_list; entry && i < menu->size; i++, entry = entry->next)
+		    if (entry->hotkey == c)
+            {
+              found = i;
+              break;
+            }
+
+          if (found < 0)
+          {
+            /* Search the hidden entry list for an entry having the input char as hotkey. */
+		    for (i = 0, entry = menu->hidden_entry_list; entry && i < menu->hidden_size; i++, entry = entry->next)
+		      if (entry->hotkey == c)
+              {
+                found = i + GRUB_MENU_HIDDEN_BASE_IDX;
+                break;
+              }
+          }
+
+          if (found >= 0)
+		  {
+		    grub_menu_entry_t e = grub_menu_get_entry (menu, found);
+            if (e && e->enabled)
+            {
 		      menu_fini ();
 		      *auto_boot = 0;
-		      return i;
-		    }
-	      }
-	      break;
+		      return found;
+            }
+		  }
 	    }
+	    break;
+      }
 	}
-    }
+  }
 
-  /* Never reach here.  */
+  /*NOTREACHED*/
   return -1;
 }
 
@@ -671,9 +747,14 @@
 notify_booting (grub_menu_entry_t entry,
 		void *userdata __attribute__((unused)))
 {
+  /* use the expanded label first and, if unset, use the expanded title */
+  char *label = entry->label ? grub_subst (entry->label) : grub_subst (entry->title);
+
   grub_printf ("  ");
-  grub_printf_ (N_("Booting \'%s\'"), entry->title);
+  grub_printf_ (N_("Booting \'%s\'"), label ? label : "");
   grub_printf ("\n\n");
+
+  grub_free (label);
 }
 
 /* Callback invoked when a default menu entry executed because of a timeout
@@ -683,10 +764,15 @@
 notify_fallback (grub_menu_entry_t entry,
 		 void *userdata __attribute__((unused)))
 {
+  /* use the expanded label first and, if unset, use the expanded title */
+  char *label = entry->label ? grub_subst (entry->label) : grub_subst (entry->title);
+
   grub_printf ("\n   ");
-  grub_printf_ (N_("Falling back to \'%s\'"), entry->title);
+  grub_printf_ (N_("Falling back to \'%s\'"), label ? label : "");
   grub_printf ("\n\n");
   grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
+
+  grub_free (label);
 }
 
 /* Callback invoked when a menu entry has failed and there is no remaining
@@ -730,7 +816,8 @@
       if (! e)
 	continue; /* Menu is empty.  */
 
-      grub_cls ();
+      if (!e->submenu && !e->silent)
+        grub_cls ();
 
       if (auto_boot)
 	grub_menu_execute_with_fallback (menu, e, autobooted,
@@ -739,6 +826,15 @@
 	grub_menu_execute_entry (e, 0);
       if (autobooted)
 	break;
+
+      if (nested && (grub_errno != GRUB_ERR_NONE || !grub_loader_is_loaded ()))
+      {
+          /* Indicate reload of the current submenu in case we're not
+          ** booting.
+          */
+          grub_env_set ("grub_reload_menu", "1");
+          break;
+      }
     }
 
   return GRUB_ERR_NONE;
diff -Naur grub-1.99/grub-core/normal/menu_text.c grub-1.99-new/grub-core/normal/menu_text.c
--- grub-1.99/grub-core/normal/menu_text.c	2011-04-10 12:11:43.000000000 +0200
+++ grub-1.99-new/grub-core/normal/menu_text.c	2012-02-26 23:05:28.000000000 +0100
@@ -27,6 +27,7 @@
 #include <grub/menu_viewer.h>
 #include <grub/i18n.h>
 #include <grub/charset.h>
+#include <grub/subst.h>
 
 static grub_uint8_t grub_color_menu_normal;
 static grub_uint8_t grub_color_menu_highlight;
@@ -198,25 +199,30 @@
 	     struct grub_term_output *term)
 {
   int x;
-  const char *title;
+  char *label;
   grub_size_t title_len;
   grub_ssize_t len;
   grub_uint32_t *unicode_title;
   grub_ssize_t i;
   grub_uint8_t old_color_normal, old_color_highlight;
 
-  title = entry ? entry->title : "";
-  title_len = grub_strlen (title);
+  label = entry ? (entry->label ? grub_subst (entry->label) : grub_strdup (entry->title)) : grub_strdup ("");
+
+  title_len = grub_strlen (label);
   unicode_title = grub_malloc (title_len * sizeof (*unicode_title));
   if (! unicode_title)
+  {
+    grub_free (label);
     /* XXX How to show this error?  */
     return;
+  }
 
   len = grub_utf8_to_ucs4 (unicode_title, title_len,
-                           (grub_uint8_t *) title, -1, 0);
+                           (grub_uint8_t *) label, -1, 0);
   if (len < 0)
     {
       /* It is an invalid sequence.  */
+      grub_free (label);
       grub_free (unicode_title);
       return;
     }
@@ -285,6 +291,7 @@
 
   grub_term_setcolor (term, old_color_normal, old_color_highlight);
   grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
+  grub_free (label);
   grub_free (unicode_title);
 }
 
diff -Naur grub-1.99/include/grub/menu.h grub-1.99-new/include/grub/menu.h
--- grub-1.99/include/grub/menu.h	2011-01-10 19:56:05.000000000 +0100
+++ grub-1.99-new/include/grub/menu.h	2012-02-28 11:56:51.000000000 +0100
@@ -32,6 +32,9 @@
   /* The title name.  */
   const char *title;
 
+  /* The label.  */
+  const char *label;
+
   /* If set means not everybody is allowed to boot this entry.  */
   int restricted;
 
@@ -55,6 +58,9 @@
 
   int submenu;
 
+  int silent;
+  int enabled;
+
   /* The next element.  */
   struct grub_menu_entry *next;
 };
@@ -68,9 +74,18 @@
 
   /* The list of menu entries.  */
   grub_menu_entry_t entry_list;
+
+  /* The number of hidden menu entries. */
+  int hidden_size;
+
+  /* The list of hidden menu entries. */
+  grub_menu_entry_t hidden_entry_list;
 };
 typedef struct grub_menu *grub_menu_t;
 
+/* Menu indexes for hidden menu entries start at this number */
+#define GRUB_MENU_HIDDEN_BASE_IDX 10000
+
 /* Callback structure menu viewers can use to provide user feedback when
    default entries are executed, possibly including fallback entries.  */
 typedef struct grub_menu_execute_callback
diff -Naur grub-1.99/include/grub/normal.h grub-1.99-new/include/grub/normal.h
--- grub-1.99/include/grub/normal.h	2011-04-10 12:11:43.000000000 +0200
+++ grub-1.99-new/include/grub/normal.h	2012-02-28 14:46:42.000000000 +0100
@@ -122,10 +122,10 @@
 extern int grub_extractor_level;
 
 grub_err_t
-grub_normal_add_menu_entry (int argc, const char **args, char **classes,
+grub_normal_add_menu_entry (int argc, const char **args, const char *label, char **classes,
 			    const char *users, const char *hotkey,
 			    const char *prefix, const char *sourcecode,
-			    int submenu);
+			    int submenu, int silent, int hidden, int enabled);
 
 grub_err_t
 grub_normal_set_password (const char *user, const char *password);
diff -Naur grub-1.99/include/grub/subst.h grub-1.99-new/include/grub/subst.h
--- grub-1.99/include/grub/subst.h	1970-01-01 01:00:00.000000000 +0100
+++ grub-1.99-new/include/grub/subst.h	2012-02-26 21:59:34.000000000 +0100
@@ -0,0 +1,32 @@
+/*
+** subst.c - substitute variables in a string by GRUB2 environment variables
+**
+** Copyright (C) 2012 Andreas Vogel
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+** copies of the Software, and to permit persons to whom the Software is
+** furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+** THE SOFTWARE.
+*/
+
+#ifndef GRUB_SUBST_HEADER
+#define GRUB_SUBST_HEADER 1
+
+char *grub_subst (const char *value);
+
+char *grub_subst_v (const char *value, char **r_var);
+
+#endif
