This patch adds support for multiple fallback entries.  The entry
numbers in the 'fallback' environment variable should be
space-separated.

Regards,
Colin
2008-08-30  Colin D Bennett <[EMAIL PROTECTED]>

        Allow multiple fallback entries.

        * include/grub/normal.h (grub_menu_execute_callback_t): New type.
        (grub_menu_execute_with_fallback): New prototype.
        (grub_menu_get_timeout): New prototype.
        (grub_menu_set_timeout): New prototype.

        * normal/menu.c (get_timeout): Renamed to grub_menu_get_timeout.
        (grub_menu_get_timeout): Renamed from get_timeout, and made global.
        (set_timeout): Renamed to grub_menu_set_timeout.
        (grub_menu_set_timeout): Renamed from set_timeout, and made global.
        (get_and_remove_first_entry_number): New function.
        (run_menu): Call grub_menu_get_timeout and grub_menu_set_timeout
        instead of the old function names.
        (grub_menu_execute_with_fallback): New function.
        (notify_booting): New function.
        (notify_fallback): New function.
        (notify_execution_failure): New function.
        (execution_callback): New struct definition.
        (show_menu): Use grub_menu_execute_with_fallback instead of directly
        handling the fallback here.
=== modified file 'include/grub/normal.h'
--- include/grub/normal.h	2008-08-31 00:41:28 +0000
+++ include/grub/normal.h	2008-08-31 05:37:12 +0000
@@ -98,10 +98,24 @@
 
 extern struct grub_menu_viewer grub_normal_terminal_menu_viewer;
 
+typedef struct grub_menu_execute_callback
+{
+  void (*notify_booting) (void *userdata, grub_menu_entry_t entry);
+  void (*notify_fallback) (void *userdata, grub_menu_entry_t entry);
+  void (*notify_failure) (void *userdata);
+} 
+*grub_menu_execute_callback_t;
+
 void grub_enter_normal_mode (const char *config);
 void grub_normal_execute (const char *config, int nested);
+void grub_menu_execute_with_fallback (grub_menu_t menu,
+                                      grub_menu_entry_t entry,
+                                      grub_menu_execute_callback_t callback,
+                                      void *callback_data);
 void grub_menu_entry_run (grub_menu_entry_t entry);
 void grub_menu_execute_entry(grub_menu_entry_t entry);
+int grub_menu_get_timeout (void);
+void grub_menu_set_timeout (int timeout);
 void grub_cmdline_run (int nested);
 int grub_cmdline_get (const char *prompt, char cmdline[], unsigned max_len,
 		      int echo_char, int readline);

=== modified file 'normal/menu.c'
--- normal/menu.c	2008-08-31 00:41:28 +0000
+++ normal/menu.c	2008-08-31 05:37:12 +0000
@@ -242,8 +242,8 @@
 
 /* Return the current timeout. If the variable "timeout" is not set or
    invalid, return -1.  */
-static int
-get_timeout (void)
+int
+grub_menu_get_timeout (void)
 {
   char *val;
   int timeout;
@@ -270,8 +270,8 @@
 }
 
 /* Set current timeout in the variable "timeout".  */
-static void
-set_timeout (int timeout)
+void
+grub_menu_set_timeout (int timeout)
 {
   /* Ignore TIMEOUT if it is zero, because it will be unset really soon.  */
   if (timeout > 0)
@@ -309,6 +309,43 @@
   return entry;
 }
 
+/* Get the first entry number from the variable NAME, which is a 
+   space-separated list of nonnegative integers.  The entry number which
+   is returned is stripped from the value of NAME.  */
+static int
+get_and_remove_first_entry_number (const char *name)
+{
+  char *val;
+  char *tail;
+  int entry;
+  
+  val = grub_env_get (name);
+  if (! val)
+    return -1;
+  
+  grub_error_push ();
+  
+  entry = (int) grub_strtoul (val, &tail, 0);
+
+  if (grub_errno == GRUB_ERR_NONE)
+    {
+      /* Skip whitespace to find the next digit.  */
+      while (*tail && grub_isspace (*tail))
+	tail++;
+      grub_env_set (name, tail);
+    }
+  else
+    {
+      grub_env_unset (name);
+      grub_errno = GRUB_ERR_NONE;
+      entry = -1;
+    }
+
+  grub_error_pop ();
+  
+  return entry;
+}
+
 static void
 print_timeout (int timeout, int offset, int second_stage)
 {
@@ -341,7 +378,7 @@
     default_entry = 0;
 
   /* If timeout is 0, drawing is pointless (and ugly).  */
-  if (get_timeout () == 0)
+  if (grub_menu_get_timeout () == 0)
     return default_entry;
 
   offset = default_entry;
@@ -360,7 +397,7 @@
   print_entries (menu, first, offset);
   grub_refresh ();
 
-  timeout = get_timeout ();
+  timeout = grub_menu_get_timeout ();
 
   if (timeout > 0)
     print_timeout (timeout, offset, 0);
@@ -368,7 +405,7 @@
   while (1)
     {
       int c;
-      timeout = get_timeout ();
+      timeout = grub_menu_get_timeout ();
       
       if (timeout > 0)
 	{
@@ -378,7 +415,7 @@
 	  if (current_time - saved_time >= 1000)
 	    {
 	      timeout--;
-	      set_timeout (timeout);
+	      grub_menu_set_timeout (timeout);
 	      saved_time = current_time;
 	      print_timeout (timeout, offset, 1);
 	    }
@@ -508,6 +545,72 @@
     grub_command_execute ("boot", 0);
 }
 
+/* Execute ENTRY from the menu MENU, falling back to entries specified
+   in the environment variable "fallback" if it fails.  CALLBACK is a 
+   pointer to a struct of function pointers which are used to allow the
+   caller provide feedback to the user.  */
+void
+grub_menu_execute_with_fallback (grub_menu_t menu,
+				 grub_menu_entry_t entry,
+				 grub_menu_execute_callback_t callback,
+				 void *callback_data)
+{
+  int fallback_entry;
+
+  callback->notify_booting (callback_data, entry);
+
+  grub_menu_execute_entry (entry);
+
+  /* Deal with fallback entries.  */
+  while ((fallback_entry = get_and_remove_first_entry_number ("fallback"))
+	 >= 0)
+    {
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+
+      entry = get_entry (menu, fallback_entry);
+      callback->notify_fallback (callback_data, entry);
+      grub_menu_execute_entry (entry);
+    }
+
+  if (grub_errno != GRUB_ERR_NONE)
+    callback->notify_failure (callback_data);
+}
+
+static void
+notify_booting (void *userdata __attribute__((unused)),
+		grub_menu_entry_t entry)
+{
+  grub_printf ("  Booting \'%s\'\n\n", entry->title);
+}
+
+static void
+notify_fallback (void *userdata __attribute__((unused)),
+		 grub_menu_entry_t entry)
+{
+  grub_printf ("\n  Falling back to \'%s\'\n\n", entry->title);
+  grub_millisleep (2000);
+}
+
+static void
+notify_execution_failure (void *userdata __attribute__((unused)))
+{
+  if (grub_errno != GRUB_ERR_NONE)
+    {
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+
+      grub_wait_after_message ();
+    }
+}
+
+static struct grub_menu_execute_callback execution_callback = 
+{
+  .notify_booting = notify_booting,
+  .notify_fallback = notify_fallback,
+  .notify_failure = notify_execution_failure
+};
+
 static grub_err_t
 show_menu (grub_menu_t menu, int nested)
 {
@@ -515,8 +618,7 @@
     {
       int boot_entry;
       grub_menu_entry_t e;
-      int fallback_entry;
-      
+
       boot_entry = run_menu (menu, nested);
       if (boot_entry < 0)
 	break;
@@ -524,35 +626,11 @@
       e = get_entry (menu, boot_entry);
       if (! e)
 	continue; /* Menu is empty.  */
-	
+
       grub_cls ();
       grub_setcursor (1);
 
-      grub_printf ("  Booting \'%s\'\n\n", e->title);
-  
-      grub_menu_execute_entry (e);
-
-      /* Deal with a fallback entry.  */
-      /* FIXME: Multiple fallback entries like GRUB Legacy.  */
-      fallback_entry = get_entry_number ("fallback");
-      if (fallback_entry >= 0)
-	{
-	  grub_print_error ();
-	  grub_errno = GRUB_ERR_NONE;
-	  
-	  e = get_entry (menu, fallback_entry);
-	  grub_env_unset ("fallback");
-	  grub_printf ("\n  Falling back to \'%s\'\n\n", e->title);
-	  grub_menu_execute_entry (e);
-	}
-
-      if (grub_errno != GRUB_ERR_NONE)
-	{
-	  grub_print_error ();
-	  grub_errno = GRUB_ERR_NONE;
-
-	  grub_wait_after_message ();
-	}
+      grub_menu_execute_with_fallback (menu, e, &execution_callback, 0);
     }
 
   return GRUB_ERR_NONE;

Attachment: signature.asc
Description: PGP signature

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to