=== modified file 'include/grub/normal.h' --- include/grub/normal.h 2009-01-31 09:15:43 +0000 +++ include/grub/normal.h 2009-01-31 17:47:55 +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 2009-01-31 09:15:43 +0000 +++ normal/menu.c 2009-01-31 17:47:55 +0000 @@ -244,8 +244,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; @@ -272,8 +272,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) @@ -311,6 +311,44 @@ 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. If no entry number can + be found, returns -1. */ +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) { @@ -343,7 +381,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; @@ -362,7 +400,7 @@ print_entries (menu, first, offset); grub_refresh (); - timeout = get_timeout (); + timeout = grub_menu_get_timeout (); if (timeout > 0) print_timeout (timeout, offset, 0); @@ -370,7 +408,7 @@ while (1) { int c; - timeout = get_timeout (); + timeout = grub_menu_get_timeout (); if (timeout > 0) { @@ -380,7 +418,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); } @@ -498,12 +536,12 @@ offset += GRUB_TERM_NUM_ENTRIES; if (offset > menu->size - 1 || - offset > GRUB_TERM_NUM_ENTRIES - 1) + offset > GRUB_TERM_NUM_ENTRIES - 1) { offset = menu->size - first - 1; } if (offset > GRUB_TERM_NUM_ENTRIES) - { + { first += offset - GRUB_TERM_NUM_ENTRIES + 1; offset = GRUB_TERM_NUM_ENTRIES - 1; } @@ -561,6 +599,74 @@ 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); +} + +/* Callbacks for grub_menu_execute_with_fallback() for the text menu. */ + +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_text_menu (grub_menu_t menu, int nested) { @@ -568,7 +674,6 @@ { int boot_entry; grub_menu_entry_t e; - int fallback_entry; boot_entry = run_menu (menu, nested); if (boot_entry < 0) @@ -581,31 +686,7 @@ 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;