| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 2000,2001,2002,2004,2005 Free Software Foundation, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| #include <shared.h> |
| #include <term.h> |
| |
| #ifdef SUPPORT_GRAPHICS |
| extern int disable_space_highlight; |
| #endif /* SUPPORT_GRAPHICS */ |
| |
| #ifdef GRUB_UTIL |
| |
| grub_jmp_buf restart_env; |
| |
| /* The preset_menu variable is referenced by asm.S */ |
| # if defined(PRESET_MENU_STRING) |
| const char *preset_menu = PRESET_MENU_STRING; |
| # elif defined(SUPPORT_DISKLESS) |
| /* Execute the command "bootp" automatically. */ |
| const char *preset_menu = "bootp\n"; |
| #else /* ! SUPPORT_DISKLESS */ |
| /* The preset_menu variable is always available. This is for the loader, which |
| * loads GRUB, to define a new preset menu before transferring control to GRUB. |
| */ |
| const char *preset_menu = 0; |
| # endif /* ! SUPPORT_DISKLESS */ |
| #else /* ! GRUB_UTIL */ |
| /* preset_menu is defined in asm.S */ |
| #endif /* GRUB_UTIL */ |
| |
| static unsigned long preset_menu_offset; |
| |
| static int |
| open_preset_menu (void) |
| { |
| #ifdef GRUB_UTIL |
| /* Unless the user explicitly requests to use the preset menu, |
| always opening the preset menu fails in the grub shell. */ |
| if (! use_preset_menu) |
| return 0; |
| #endif /* GRUB_UTIL */ |
| |
| preset_menu_offset = 0; |
| return preset_menu != 0; |
| } |
| |
| static int |
| read_from_preset_menu (char *buf, int max_len) |
| { |
| int len; |
| |
| if (preset_menu == 0) |
| return 0; |
| |
| len = grub_strlen (preset_menu + preset_menu_offset); |
| |
| if (len > max_len) |
| len = max_len; |
| |
| grub_memmove (buf, preset_menu + preset_menu_offset, len); |
| preset_menu_offset += len; |
| |
| return len; |
| } |
| |
| #ifdef GRUB_UTIL |
| #undef DISP_UL //218 |
| #undef DISP_UR //191 |
| #undef DISP_LL //192 |
| #undef DISP_LR //217 |
| #undef DISP_HORIZ //196 |
| #undef DISP_VERT //179 |
| #undef DISP_LEFT //0x1b |
| #undef DISP_RIGHT //0x1a |
| #undef DISP_UP //0x18 |
| #undef DISP_DOWN //0x19 |
| |
| #define DISP_UL ACS_ULCORNER /* 0xDA , '+' */ |
| #define DISP_UR ACS_URCORNER /* 0xBF , '+' */ |
| #define DISP_LL ACS_LLCORNER /* 0xC0 , '+' */ |
| #define DISP_LR ACS_LRCORNER /* 0xD9 , '+' */ |
| #define DISP_HORIZ ACS_HLINE /* 0xC4 , '-' */ |
| #define DISP_VERT ACS_VLINE /* 0xB3 , '|' */ |
| #define DISP_LEFT ACS_LARROW /* 0x1B , '<' */ |
| #define DISP_RIGHT ACS_RARROW /* 0x1A , '>' */ |
| #define DISP_UP ACS_UARROW /* 0x18 , '^' */ |
| #define DISP_DOWN ACS_DARROW /* 0x19 , 'v' */ |
| #endif |
| |
| /* line start */ |
| #define MENU_BOX_X 2 |
| |
| /* line width */ |
| #define MENU_BOX_W 76 |
| |
| /* first line number */ |
| #define MENU_BOX_Y 2 |
| |
| /* window height */ |
| #define MENU_BOX_H (current_term->max_lines - 8) |
| |
| /* line end */ |
| #define MENU_BOX_E (MENU_BOX_X + MENU_BOX_W) |
| |
| /* window bottom */ |
| #define MENU_BOX_B (MENU_BOX_Y + MENU_BOX_H) |
| |
| static long temp_entryno; |
| static char * *titles; /* title array, point to 256 strings. */ |
| |
| static char * |
| get_entry (char *list, int num) |
| { |
| int i; |
| |
| if (list == (char *)titles) |
| return titles[num]; |
| |
| for (i = 0; i < num; i++) |
| { |
| while (*(list++)); |
| } |
| |
| return list; |
| } |
| |
| /* Print an entry in a line of the menu box. */ |
| static void |
| print_entry (int y, int highlight, char *entry) |
| { |
| int x; |
| unsigned char c = (entry ? (unsigned char)*entry : 0); |
| |
| if (current_term->setcolorstate) |
| current_term->setcolorstate (highlight ? COLOR_STATE_HIGHLIGHT : COLOR_STATE_NORMAL); |
| |
| // if (highlight && current_term->setcolorstate) |
| // current_term->setcolorstate (COLOR_STATE_HIGHLIGHT); |
| |
| gotoxy (MENU_BOX_X - 1, y); |
| #ifdef SUPPORT_GRAPHICS |
| disable_space_highlight = 1; |
| #endif /* SUPPORT_GRAPHICS */ |
| grub_putchar (' '); |
| #ifdef SUPPORT_GRAPHICS |
| disable_space_highlight = 0; |
| #endif /* SUPPORT_GRAPHICS */ |
| for (x = MENU_BOX_X; x < MENU_BOX_E; x++) |
| { |
| if (c && x <= MENU_BOX_W) |
| { |
| if (x == MENU_BOX_W) |
| grub_putchar (DISP_RIGHT); |
| else |
| { |
| while (c && (c <= 0x0F)) |
| c = *(++entry); |
| if (! c) |
| goto space_no_highlight; |
| grub_putchar (c); |
| c = *(++entry); |
| } |
| } |
| else |
| { |
| space_no_highlight: |
| #ifdef SUPPORT_GRAPHICS |
| if (! disable_space_highlight) |
| disable_space_highlight = 1; |
| #endif /* SUPPORT_GRAPHICS */ |
| grub_putchar (' '); |
| } |
| } |
| #ifdef SUPPORT_GRAPHICS |
| disable_space_highlight = 0; |
| #endif /* SUPPORT_GRAPHICS */ |
| //gotoxy (MENU_BOX_E - 1, y); /* XXX: Why? */ |
| |
| if (current_term->setcolorstate) |
| current_term->setcolorstate (COLOR_STATE_STANDARD); |
| } |
| |
| /* Print entries in the menu box. */ |
| static void |
| print_entries (int first, int entryno, char *menu_entries) |
| { |
| int i; |
| int main_menu = (menu_entries == (char *)titles); |
| |
| if (current_term->setcolorstate) |
| current_term->setcolorstate (COLOR_STATE_NORMAL); |
| |
| gotoxy (MENU_BOX_E, MENU_BOX_Y); |
| |
| if (first) |
| grub_putchar (DISP_UP); |
| else |
| grub_putchar (DISP_VERT); |
| |
| if (main_menu || *menu_entries) |
| menu_entries = get_entry (menu_entries, first); |
| |
| for (i = 0; i < MENU_BOX_H/*size*/; i++) |
| { |
| print_entry (MENU_BOX_Y + i, entryno == i, menu_entries); |
| |
| if (main_menu) |
| { |
| if (menu_entries) |
| { |
| if ((++first) < 256) |
| menu_entries = titles[first]; |
| else |
| menu_entries = 0; |
| } |
| continue; |
| } |
| if (*menu_entries) |
| while (*(menu_entries++)); |
| } |
| |
| gotoxy (MENU_BOX_E, MENU_BOX_Y - 1 + MENU_BOX_H/*size*/); |
| |
| if (current_term->setcolorstate) |
| current_term->setcolorstate (COLOR_STATE_NORMAL); |
| |
| if (menu_entries && *menu_entries) |
| grub_putchar (DISP_DOWN); |
| else |
| grub_putchar (DISP_VERT); |
| |
| if (current_term->setcolorstate) |
| current_term->setcolorstate (COLOR_STATE_STANDARD); |
| |
| gotoxy (MENU_BOX_E, MENU_BOX_Y + entryno); /* XXX: Why? */ |
| } |
| |
| static void |
| print_entries_raw (int size, int first, char *menu_entries) |
| { |
| int i; |
| char *p; |
| |
| for (i = 0; i < MENU_BOX_W; i++) |
| grub_putchar ('-'); |
| grub_putchar ('\n'); |
| |
| for (i = first; i < size; i++) |
| { |
| p = get_entry (menu_entries, i); |
| grub_printf ("%02d: %s\n", i, (((*p) & 0xF0) ? p : ++p)); |
| } |
| |
| for (i = 0; i < MENU_BOX_W; i++) |
| grub_putchar ('-'); |
| grub_putchar ('\n'); |
| } |
| |
| |
| static void |
| print_border (int y, int size) |
| { |
| int i; |
| |
| if (current_term->setcolorstate) |
| current_term->setcolorstate (COLOR_STATE_NORMAL); |
| |
| gotoxy (MENU_BOX_X - 2, y); |
| |
| grub_putchar (DISP_UL); |
| for (i = 0; i < MENU_BOX_W + 1; i++) |
| grub_putchar (DISP_HORIZ); |
| grub_putchar (DISP_UR); |
| |
| i = 1; |
| while (1) |
| { |
| gotoxy (MENU_BOX_X - 2, y + i); |
| |
| if (i > size) |
| break; |
| |
| grub_putchar (DISP_VERT); |
| gotoxy (MENU_BOX_E, y + i); |
| grub_putchar (DISP_VERT); |
| |
| i++; |
| } |
| |
| grub_putchar (DISP_LL); |
| for (i = 0; i < MENU_BOX_W + 1; i++) |
| grub_putchar (DISP_HORIZ); |
| grub_putchar (DISP_LR); |
| |
| if (current_term->setcolorstate) |
| current_term->setcolorstate (COLOR_STATE_STANDARD); |
| } |
| |
| extern int commandline_func (char *arg1, int flags); |
| extern int errnum_func (char *arg1, int flags); |
| extern int checkrange_func (char *arg1, int flags); |
| |
| /* Run an entry from the script SCRIPT. HEAP is used for the |
| command-line buffer. If an error occurs, return non-zero, otherwise |
| return zero. */ |
| static int |
| run_script (char *script, char *heap) |
| { |
| char *old_entry = 0; |
| char *cur_entry = script; |
| struct builtin *builtin = 0; |
| char *arg; |
| grub_error_t errnum_old; |
| |
| /* Initialize the data. */ |
| //saved_drive = boot_drive; |
| //saved_partition = install_partition; |
| current_drive = GRUB_INVALID_DRIVE; |
| count_lines = -1; |
| |
| /* Initialize the data for the builtin commands. */ |
| kernel_type = KERNEL_TYPE_NONE; |
| errnum = 0; |
| |
| while (1) |
| { |
| if (errnum && errorcheck) |
| break; |
| |
| errnum_old = errnum; |
| /* Copy the first string in CUR_ENTRY to HEAP. */ |
| errnum = ERR_NONE; |
| old_entry = cur_entry; |
| while (*cur_entry++) |
| ; |
| |
| grub_memmove (heap, old_entry, (int) cur_entry - (int) old_entry); |
| if (! *heap) |
| { |
| /* If there is no more command in SCRIPT... */ |
| |
| /* If any kernel is not loaded, just exit successfully. */ |
| if (kernel_type == KERNEL_TYPE_NONE) |
| return 0; /* return to main menu. */ |
| |
| /* Otherwise, the command boot is run implicitly. */ |
| grub_memmove (heap, "boot", 5); |
| } |
| |
| /* Find a builtin. */ |
| builtin = find_command (heap); |
| if (! builtin) |
| { |
| grub_printf ("%s\n", old_entry); |
| continue; |
| } |
| /* now command echoing is considered no use for a successful command. */ |
| // if (! (builtin->flags & BUILTIN_NO_ECHO)) |
| // grub_printf ("%s\n", old_entry); |
| |
| /* If BUILTIN cannot be run in the command-line, skip it. */ |
| if (! (builtin->flags & BUILTIN_CMDLINE)) |
| { |
| errnum = ERR_UNRECOGNIZED; |
| continue; |
| } |
| |
| /* Invalidate the cache, because the user may exchange removable |
| disks. */ |
| buf_drive = -1; |
| |
| if ((builtin->func) == errnum_func || (builtin->func) == checkrange_func) |
| errnum = errnum_old; |
| |
| /* find && and || */ |
| |
| for (arg = skip_to (0, heap); *arg != 0; arg = skip_to (0, arg)) |
| { |
| struct builtin *builtin1; |
| int ret; |
| char *arg1; |
| arg1 = arg; |
| if (*arg == '&' && arg[1] == '&' && (arg[2] == ' ' || arg[2] == '\t')) |
| { |
| /* handle the AND operator */ |
| arg = skip_to (0, arg); |
| builtin1 = find_command (arg); |
| if (! builtin1 || ! (builtin1->flags & BUILTIN_CMDLINE)) |
| { |
| errnum = ERR_UNRECOGNIZED; |
| goto next; |
| } |
| |
| *arg1 = 0; |
| ret = (builtin->func) (skip_to (1, heap), BUILTIN_SCRIPT); |
| *arg1 = '&'; |
| if (ret) |
| { |
| arg = skip_to (1, arg); |
| if ((builtin1->func) != errnum_func && (builtin1->func) != checkrange_func) |
| errnum = 0; |
| (builtin1->func) (arg, BUILTIN_SCRIPT); |
| } else |
| errnum = 0; |
| goto next; |
| } else if (*arg == '|' && arg[1] == '|' && (arg[2] == ' ' || arg[2] == '\t')) |
| { |
| /* handle the OR operator */ |
| arg = skip_to (0, arg); |
| builtin1 = find_command (arg); |
| if (! builtin1 || ! (builtin1->flags & BUILTIN_CMDLINE)) |
| { |
| errnum = ERR_UNRECOGNIZED; |
| goto next; |
| } |
| |
| *arg1 = 0; |
| ret = (builtin->func) (skip_to (1, heap), BUILTIN_SCRIPT); |
| *arg1 = '|'; |
| if (! ret) |
| { |
| arg = skip_to (1, arg); |
| if ((builtin1->func) != errnum_func && (builtin1->func) != checkrange_func) |
| errnum = 0; |
| (builtin1->func) (arg, BUILTIN_SCRIPT); |
| } else |
| errnum = 0; |
| goto next; |
| } |
| } |
| |
| /* Run BUILTIN->FUNC. */ |
| arg = (builtin->func) == commandline_func ? heap : skip_to (1, heap); |
| (builtin->func) (arg, BUILTIN_SCRIPT); |
| next: |
| if (! *old_entry) /* HEAP holds the implicit BOOT command */ |
| break; |
| } /* while (1) */ |
| |
| kernel_type = KERNEL_TYPE_NONE; |
| |
| /* If a fallback entry is defined, don't prompt a user's intervention. */ |
| |
| if (fallback_entryno < 0) |
| { |
| if (! (builtin->flags & BUILTIN_NO_ECHO)) |
| grub_printf ("%s\n", heap); |
| print_error (); |
| |
| grub_printf ("\nPress any key to continue..."); |
| (void) getkey (); |
| } |
| |
| errnum = ERR_NONE; |
| return 1; /* use fallback. */ |
| } |
| |
| static int fallbacked_entries; |
| static int old_c; |
| static int old_c_count; |
| static int old_c_count_end; |
| |
| static void |
| run_menu (char *menu_entries, char *config_entries, int num_entries, char *heap, int entryno) |
| { |
| int c, time1, time2 = -1, first_entry = 0; |
| char *cur_entry = 0; |
| // struct term_entry *prev_term = NULL; |
| |
| |
| /* |
| * Main loop for menu UI. |
| */ |
| |
| restart: |
| /* Dumb terminal always use all entries for display |
| invariant for TERM_DUMB: first_entry == 0 */ |
| if (! (current_term->flags & TERM_DUMB)) |
| { |
| if (entryno > MENU_BOX_H - 1) |
| { |
| first_entry += entryno - (MENU_BOX_H - 1); |
| entryno = MENU_BOX_H - 1; |
| } |
| } |
| |
| /* If the timeout was expired or wasn't set, force to show the menu |
| interface. */ |
| if (grub_timeout < 0) |
| show_menu = 1; |
| |
| /* If SHOW_MENU is false, don't display the menu until ESC is pressed. */ |
| if (! show_menu) |
| { |
| /* Get current time. */ |
| while ((time1 = getrtsecs ()) == 0xFF) |
| ; |
| |
| while (1) |
| { |
| /* Check if ESC is pressed. */ |
| if (checkkey () != -1 && ASCII_CHAR (getkey ()) == '\e') |
| { |
| grub_timeout = -1; |
| show_menu = 1; |
| break; |
| } |
| |
| /* If GRUB_TIMEOUT is expired, boot the default entry. */ |
| if (grub_timeout >=0 |
| && (time1 = getrtsecs ()) != time2 |
| && time1 != 0xFF) |
| { |
| if (grub_timeout <= 0) |
| { |
| grub_timeout = -1; |
| goto boot_entry; |
| } |
| |
| time2 = time1; |
| grub_timeout--; |
| |
| /* Print a message. */ |
| grub_printf ("\rPress `ESC' to enter the menu... %d ", |
| grub_timeout); |
| } |
| } |
| } |
| |
| /* Only display the menu if the user wants to see it. */ |
| if (show_menu) |
| { |
| init_page (); |
| setcursor (0); |
| |
| if (current_term->flags & TERM_DUMB) |
| print_entries_raw (num_entries, first_entry, menu_entries); |
| else |
| print_border (MENU_BOX_Y - 1, MENU_BOX_H); |
| |
| grub_printf ("\nUse the %c and %c keys to highlight an entry.", |
| DISP_UP, DISP_DOWN); |
| |
| if (! auth && password) |
| { |
| #ifdef SUPPORT_GFX |
| if (*graphics_file) |
| { |
| printf ("\ |
| WARNING: graphical menu doesn\'t work\ |
| in conjunction with the password feature\n" ); |
| } |
| #endif |
| printf (" Press ENTER or \'b\' to boot.\n" |
| "Press \'p\' to gain privileged control."); |
| } |
| else |
| { |
| if (config_entries) |
| printf (" Press ENTER or \'b\' to boot.\n" |
| "Press \'e\' to edit the commands before booting, or \'c\' for a command-line."); |
| else |
| printf (" At a selected line, press \'e\' to\n" |
| "edit, \'d\' to delete, or \'O\'/\'o\' to open a new line before/after. When done,\n" |
| "press \'b\' to boot, \'c\' for a command-line, or ESC to go back to the main menu."); |
| } |
| |
| if (current_term->flags & TERM_DUMB) |
| grub_printf ("\n\nThe selected entry is %d ", entryno); |
| else |
| print_entries (first_entry, entryno, menu_entries); |
| } |
| |
| /* XX using RT clock now, need to initialize value */ |
| while ((time1 = getrtsecs()) == 0xFF); |
| |
| old_c = 0; |
| old_c_count = 0; |
| old_c_count_end = 0; |
| temp_entryno = 0; |
| /* Initialize to NULL just in case... */ |
| cur_entry = NULL; |
| while (1) |
| { |
| /* Initialize to NULL just in case... */ |
| //cur_entry = NULL; |
| //cur_entry = menu_entries; /* for modified menu */ |
| |
| if (grub_timeout >= 0 && (time1 = getrtsecs()) != time2 && time1 != 0xFF) |
| { |
| if (grub_timeout <= 0) |
| { |
| grub_timeout = -1; |
| break; |
| } |
| |
| /* else not booting yet! */ |
| time2 = time1; |
| |
| if (current_term->flags & TERM_DUMB) |
| grub_printf ("\r Entry %d will be booted automatically in %d seconds. ", |
| entryno, grub_timeout); |
| else |
| { |
| gotoxy (MENU_BOX_X - 2, MENU_BOX_H + 7); |
| grub_printf ("The highlighted entry will be booted automatically in %d seconds. ", |
| grub_timeout); |
| gotoxy (MENU_BOX_E, MENU_BOX_Y + entryno); |
| } |
| |
| grub_timeout--; |
| } |
| |
| /* Check for a keypress, however if TIMEOUT has been expired |
| (GRUB_TIMEOUT == -1) relax in GETKEY even if no key has been |
| pressed. |
| This avoids polling (relevant in the grub-shell and later on |
| in grub if interrupt driven I/O is done). */ |
| if (checkkey () >= 0 || grub_timeout < 0) |
| { |
| /* Key was pressed, show which entry is selected before GETKEY, |
| since we're comming in here also on GRUB_TIMEOUT == -1 and |
| hang in GETKEY */ |
| if (current_term->flags & TERM_DUMB) |
| grub_printf ("\r Highlighted entry is %d: ", entryno); |
| |
| c = /*ASCII_CHAR*/ (getkey ()); |
| |
| if (! old_c_count_end) |
| { |
| if (old_c == 0) |
| old_c = c; |
| if (c == old_c && old_c_count < 0x7FFFFFFF) |
| old_c_count++; |
| if (c != old_c) |
| { |
| old_c_count_end = 1; |
| } |
| } |
| |
| if (grub_timeout >= 0) |
| { |
| if (current_term->flags & TERM_DUMB) |
| grub_putchar ('\r'); |
| else |
| gotoxy (MENU_BOX_X - 2, MENU_BOX_H + 7); |
| printf (" "); |
| grub_timeout = -1; |
| fallback_entryno = -1; |
| if (! (current_term->flags & TERM_DUMB)) |
| gotoxy (MENU_BOX_E, MENU_BOX_Y + entryno); |
| } |
| |
| if (num_entries == 0) |
| { |
| first_entry = entryno = 0; |
| goto done_key_handling; |
| } |
| |
| /* We told them above (at least in SUPPORT_SERIAL) to use |
| '^' or 'v' so accept these keys. */ |
| if (c == KEY_UP/*16*/ || c == KEY_LEFT || ((char)c) == '^') |
| { |
| temp_entryno = 0; |
| if (current_term->flags & TERM_DUMB) |
| { |
| if (entryno > 0) |
| entryno--; |
| } |
| else |
| { |
| if (c == KEY_UP) |
| { |
| temp_entryno = first_entry + entryno; |
| for (;;) |
| { |
| temp_entryno = (temp_entryno + num_entries - 1) % num_entries; |
| if (temp_entryno == first_entry + entryno) |
| goto done_key_handling; |
| cur_entry = get_entry (menu_entries, temp_entryno); |
| if (*cur_entry != 0x08) |
| goto check_update; |
| } |
| } |
| |
| if (entryno > 0) |
| { |
| cur_entry = get_entry (menu_entries, first_entry + entryno); |
| /* un-highlight the current entry */ |
| print_entry (MENU_BOX_Y + entryno, 0, cur_entry); |
| entryno--; |
| cur_entry = get_entry (menu_entries, first_entry + entryno); |
| /* highlight the previous entry */ |
| print_entry (MENU_BOX_Y + entryno, 1, cur_entry); |
| } |
| else if (first_entry > 0) |
| { |
| first_entry--; |
| print_entries (first_entry, entryno, menu_entries); |
| } |
| else /* loop forward to END */ |
| { |
| temp_entryno = num_entries - 1; |
| goto check_update; |
| } |
| } |
| } |
| else if ((c == KEY_DOWN/*14*/ || c == KEY_RIGHT || ((char)c) == 'v' || (!old_c_count_end && c == old_c && old_c_count >= 8)) |
| /* && first_entry + entryno + 1 < num_entries */) |
| { |
| temp_entryno = 0; |
| if (current_term->flags & TERM_DUMB) |
| { |
| if (first_entry + entryno + 1 < num_entries) |
| entryno++; |
| } |
| else |
| { |
| if (c == KEY_DOWN) |
| { |
| temp_entryno = first_entry + entryno; |
| for (;;) |
| { |
| temp_entryno = (temp_entryno + 1) % num_entries; |
| if (temp_entryno == first_entry + entryno) |
| goto done_key_handling; |
| cur_entry = get_entry (menu_entries, temp_entryno); |
| if (*cur_entry != 0x08) |
| goto check_update; |
| } |
| } |
| |
| if (first_entry + entryno + 1 >= num_entries) |
| { |
| temp_entryno = 0; /* loop backward to HOME */ |
| goto check_update; |
| } |
| if (entryno < MENU_BOX_H - 1) |
| { |
| cur_entry = get_entry (menu_entries, first_entry + entryno); |
| /* un-highlight the current entry */ |
| print_entry (MENU_BOX_Y + entryno, 0, cur_entry); |
| entryno++; |
| cur_entry = get_entry (menu_entries, first_entry + entryno); |
| /* highlight the next entry */ |
| print_entry (MENU_BOX_Y + entryno, 1, cur_entry); |
| } |
| else if (num_entries > MENU_BOX_H + first_entry) |
| { |
| first_entry++; |
| print_entries (first_entry, entryno, menu_entries); |
| } |
| } |
| } |
| else if (c == KEY_PPAGE/*7*/) |
| { |
| /* Page Up */ |
| temp_entryno = 0; |
| first_entry -= MENU_BOX_H; |
| if (first_entry < 0) |
| { |
| entryno += first_entry; |
| first_entry = 0; |
| if (entryno < 0) |
| entryno = 0; |
| } |
| print_entries (first_entry, entryno, menu_entries); |
| } |
| else if (c == KEY_NPAGE/*3*/) |
| { |
| /* Page Down */ |
| temp_entryno = 0; |
| first_entry += MENU_BOX_H; |
| if (first_entry + entryno + 1 >= num_entries) |
| { |
| first_entry = num_entries - MENU_BOX_H; |
| if (first_entry < 0) |
| first_entry = 0; |
| entryno = num_entries - first_entry - 1; |
| } |
| print_entries (first_entry, entryno, menu_entries); |
| } |
| else if ( ((char)c) >= '0' && ((char)c) <= '9') |
| { |
| temp_entryno *= 10; |
| temp_entryno += ((char)c) - '0'; |
| // if (temp_entryno >= num_entries) |
| // temp_entryno = num_entries - 1; |
| if (temp_entryno >= num_entries) /* too big an entryno */ |
| { |
| if ((char)c - '0' >= num_entries) |
| temp_entryno = 0; |
| else |
| temp_entryno = (char)c - '0'; |
| } |
| if (temp_entryno != 0 || (char)c == '0') |
| { |
| check_update: |
| if (temp_entryno != first_entry + entryno) |
| { |
| /* check if entry temp_entryno is out of the screen */ |
| if (temp_entryno < first_entry || temp_entryno >= first_entry + MENU_BOX_H) |
| { |
| first_entry = (temp_entryno / MENU_BOX_H) * MENU_BOX_H; |
| entryno = temp_entryno % MENU_BOX_H; |
| print_entries (first_entry, entryno, menu_entries); |
| } else { |
| /* entry temp_entryno is on the screen, its relative entry number is |
| * (temp_entryno - first_entry) */ |
| cur_entry = get_entry (menu_entries, first_entry + entryno); |
| /* un-highlight the current entry */ |
| print_entry (MENU_BOX_Y + entryno, 0, cur_entry); |
| entryno = temp_entryno - first_entry; |
| cur_entry = get_entry (menu_entries, temp_entryno); |
| /* highlight entry temp_entryno */ |
| print_entry (MENU_BOX_Y + entryno, 1, cur_entry); |
| } |
| } |
| } |
| if (temp_entryno * 10 >= num_entries) |
| temp_entryno = 0; |
| } |
| else if (c == KEY_HOME/*1*/) |
| { |
| temp_entryno = 0; |
| goto check_update; |
| } |
| else if (c == KEY_END/*5*/) |
| { |
| temp_entryno = num_entries - 1; |
| goto check_update; |
| } |
| else |
| temp_entryno = 0; |
| |
| done_key_handling: |
| |
| gotoxy (MENU_BOX_E - 8, MENU_BOX_Y - 2); |
| grub_printf ("%d ", first_entry + entryno); |
| gotoxy (MENU_BOX_E, MENU_BOX_Y + entryno); |
| |
| if (!old_c_count_end && c == old_c && (old_c_count >= 30 || (old_c_count >= 8 && c != KEY_DOWN /*&& c != KEY_RIGHT*/ && c != KEY_UP /*&& c != KEY_LEFT*/))) |
| grub_timeout = 5; |
| |
| cur_entry = NULL; |
| |
| if (config_entries) |
| { |
| if ((((char)c) == '\n') || (((char)c) == '\r') || (((char)c) == 'b') || (((char)c) == '#') || (((char)c) == '*')) |
| break; |
| } |
| else |
| { |
| if ((((char)c) == 'd') || (((char)c) == 'o') || (((char)c) == 'O')) |
| { |
| if (! (current_term->flags & TERM_DUMB)) |
| print_entry (MENU_BOX_Y + entryno, 0, |
| get_entry (menu_entries, first_entry + entryno)); |
| |
| /* insert after is almost exactly like insert before */ |
| if (((char)c) == 'o') |
| { |
| /* But `o' differs from `O', since it may causes |
| the menu screen to scroll up. */ |
| if (num_entries > 0) |
| { |
| if (entryno < MENU_BOX_H - 1 || (current_term->flags & TERM_DUMB)) |
| entryno++; |
| else |
| first_entry++; |
| } |
| |
| c = 'O'; |
| } |
| |
| cur_entry = get_entry (menu_entries, first_entry + entryno); |
| |
| if (((char)c) == 'O') |
| { |
| grub_memmove (cur_entry + 2, cur_entry, ((int) heap) - ((int) cur_entry)); |
| |
| cur_entry[0] = ' '; |
| cur_entry[1] = 0; |
| |
| heap += 2; |
| |
| /*if (first_entry + entryno == num_entries - 1) |
| { |
| cur_entry[2] = 0; |
| heap++; |
| }*/ |
| |
| num_entries++; |
| } |
| else if (num_entries > 0) |
| { |
| char *ptr = get_entry (menu_entries, first_entry + entryno + 1); |
| |
| grub_memmove (cur_entry, ptr, ((int) heap) - ((int) ptr)); |
| heap -= (((int) ptr) - ((int) cur_entry)); |
| |
| num_entries--; |
| |
| if (/*entryno >= num_entries && */entryno > 0) |
| entryno--; |
| else if (first_entry/* && num_entries < MENU_BOX_H + first_entry*/) |
| first_entry--; |
| } |
| |
| if (current_term->flags & TERM_DUMB) |
| { |
| grub_printf ("\n\n"); |
| print_entries_raw (num_entries, first_entry, menu_entries); |
| grub_printf ("\n"); |
| } |
| else if (num_entries > 0) |
| print_entries (first_entry, entryno, menu_entries); |
| else |
| print_entry (MENU_BOX_Y, 0, cur_entry); |
| } |
| |
| cur_entry = menu_entries; |
| if (((char)c) == 27) |
| return; |
| if (((char)c) == 'b') |
| break; |
| } |
| |
| if (! auth && password) |
| { |
| if (((char)c) == 'p') |
| { |
| /* Do password check here! */ |
| char entered[32]; |
| char *pptr = password; |
| |
| if (current_term->flags & TERM_DUMB) |
| grub_printf ("\r "); |
| else |
| gotoxy (MENU_BOX_X - 2, MENU_BOX_H + 6); |
| |
| /* Wipe out the previously entered password */ |
| grub_memset (entered, 0, sizeof (entered)); |
| prompt = "Password: "; |
| maxlen = sizeof (entered) - 1; |
| echo_char = '*'; |
| readline = 0; |
| get_cmdline (entered); |
| |
| while (! isspace (*pptr) && *pptr) |
| pptr++; |
| |
| /* Make sure that PASSWORD is NUL-terminated. */ |
| *pptr++ = 0; |
| |
| if (! check_password (entered, password, password_type)) |
| { |
| char *new_file = config_file; |
| while (isspace (*pptr)) |
| pptr++; |
| |
| /* If *PPTR is NUL, then allow the user to use |
| privileged instructions, otherwise, load |
| another configuration file. */ |
| if (*pptr != 0) |
| { |
| while ((*(new_file++) = *(pptr++)) != 0) |
| ; |
| |
| /* Make sure that the user will not have |
| authority in the next configuration. */ |
| auth = 0; |
| return; |
| } |
| else |
| { |
| /* Now the user is superhuman. */ |
| auth = 1; |
| goto restart; |
| } |
| } |
| else |
| { |
| grub_printf ("Failed! Press any key to continue..."); |
| getkey (); |
| goto restart; |
| } |
| } |
| } |
| else |
| { |
| if (((char)c) == 'e') |
| { |
| int new_num_entries = 0, i = 0; |
| char *new_heap; |
| |
| if (num_entries == 0) |
| { |
| first_entry = entryno = 0; |
| cur_entry = get_entry (menu_entries, 0); |
| grub_memmove (cur_entry + 2, cur_entry, ((int) heap) - ((int) cur_entry)); |
| |
| cur_entry[0] = ' '; |
| cur_entry[1] = 0; |
| |
| heap += 2; |
| |
| /*if (first_entry + entryno == num_entries - 1) |
| { |
| cur_entry[2] = 0; |
| heap++; |
| }*/ |
| |
| num_entries++; |
| } |
| |
| // while (first_entry + entryno >= num_entries) |
| // { |
| // if (entryno > 0) |
| // entryno--; |
| // else |
| // first_entry--; |
| // } |
| |
| if (config_entries) |
| { |
| new_heap = heap; |
| //cur_entry = get_entry (config_entries, first_entry + entryno, 1); |
| cur_entry = titles[first_entry + entryno]; |
| while (*cur_entry++); /* the first entry */ |
| } |
| else |
| { |
| /* safe area! */ |
| new_heap = heap + NEW_HEAPSIZE + 1; |
| cur_entry = get_entry (menu_entries, first_entry + entryno); |
| } |
| |
| do |
| { |
| while ((*(new_heap++) = cur_entry[i++]) != 0); |
| new_num_entries++; |
| } |
| while (config_entries && cur_entry[i]); |
| |
| /* this only needs to be done if config_entries is non-NULL, |
| but it doesn't hurt to do it always */ |
| *(new_heap++) = 0; |
| |
| if (config_entries && new_num_entries) |
| run_menu (heap, NULL, new_num_entries, new_heap, 0); /* recursive!! */ |
| else |
| { |
| cls (); |
| print_cmdline_message (0); |
| |
| new_heap = heap + NEW_HEAPSIZE + 1; |
| |
| saved_drive = boot_drive; |
| saved_partition = install_partition; |
| current_drive = GRUB_INVALID_DRIVE; |
| |
| prompt = PACKAGE " edit> "; |
| maxlen = NEW_HEAPSIZE + 1; |
| echo_char = 0; |
| readline = 1; |
| if (! get_cmdline (new_heap)) |
| { |
| int j = 0; |
| |
| /* get length of new command */ |
| while (new_heap[j++]) |
| ; |
| |
| if (j < 2) |
| { |
| j = 2; |
| new_heap[0] = ' '; |
| new_heap[1] = 0; |
| } |
| |
| /* align rest of commands properly */ |
| grub_memmove (cur_entry + j, cur_entry + i, (int) heap - ((int) cur_entry + i)); |
| |
| /* copy command to correct area */ |
| grub_memmove (cur_entry, new_heap, j); |
| |
| heap += (j - i); |
| if (first_entry + entryno == num_entries - 1) |
| { |
| cur_entry[j] = 0; |
| heap++; |
| } |
| } |
| } |
| |
| goto restart; |
| } |
| if (((char)c) == 'c') |
| { |
| enter_cmdline (heap, 0); |
| goto restart; |
| } |
| #ifdef GRUB_UTIL |
| if (((char)c) == 'q') |
| { |
| /* The same as ``quit''. */ |
| stop (); |
| } |
| #endif |
| } |
| } |
| } |
| |
| /* Attempt to boot an entry. */ |
| |
| boot_entry: |
| |
| cls (); |
| setcursor (1); |
| // /* if our terminal needed initialization, we should shut it down |
| // * before booting the kernel, but we want to save what it was so |
| // * we can come back if needed */ |
| // prev_term = current_term; |
| //// if (current_term->shutdown) |
| //// { |
| //// (*current_term->shutdown)(); |
| //// current_term = term_table; /* assumption: console is first */ |
| //// } |
| |
| fallbacked_entries = 0; |
| while (1) |
| { |
| if (debug > 0) |
| { |
| if (config_entries) |
| { |
| char *p; |
| |
| p = get_entry (menu_entries, first_entry + entryno); |
| printf (" Booting \'%s\'\n\n", (((*p) & 0xF0) ? p : ++p)); |
| } |
| else |
| printf (" Booting command-list\n\n"); |
| } |
| |
| /* Set CURRENT_ENTRYNO for the command "savedefault". */ |
| current_entryno = first_entry + entryno; |
| |
| if (! cur_entry) |
| { |
| //cur_entry = get_entry (config_entries, first_entry + entryno, 1); |
| cur_entry = titles[current_entryno]; |
| while (*cur_entry++); |
| } |
| |
| if (! run_script (cur_entry, heap)) |
| break; |
| if (fallback_entryno < 0) |
| break; |
| cur_entry = NULL; |
| first_entry = 0; |
| entryno = fallback_entries[fallback_entryno]; |
| fallback_entryno++; |
| if (fallback_entryno >= MAX_FALLBACK_ENTRIES || fallback_entries[fallback_entryno] < 0) |
| fallback_entryno = -1; |
| fallbacked_entries++; |
| if (fallbacked_entries > num_entries) |
| { |
| printf ("\nEndless fallback loop detected(entry=%d)! Press any key to exit...", current_entryno); |
| (void) getkey (); |
| break; |
| } |
| } |
| |
| // /* if we get back here, we should go back to what our term was before */ |
| // current_term = prev_term; |
| // if (current_term->startup) |
| // /* if our terminal fails to initialize, fall back to console since |
| // * it should always work */ |
| // if ((*current_term->startup)() == 0) |
| // current_term = term_table; /* we know that console is first */ |
| show_menu = 1; |
| goto restart; |
| } |
| |
| // #define GFX_DEBUG |
| |
| #ifdef SUPPORT_GFX |
| |
| /* kernel + (grub-)module options */ |
| #define GFX_CMD_BUF_SIZE 512 |
| |
| /* command line separator char */ |
| #define GFX_CMD_SEP 1 |
| |
| /* |
| * Search cpio archive for gfx file. |
| */ |
| static unsigned find_file (unsigned char *buf, unsigned len, |
| unsigned *gfx_file_start, unsigned *file_len, |
| unsigned *code_start) |
| { |
| unsigned i, fname_len; |
| |
| *gfx_file_start = 0; |
| |
| for(i = 0; i < len;) |
| { |
| if((len - i) >= 0x1a && (buf[i] + (buf[i + 1] << 8)) == 0x71c7) |
| { |
| fname_len = *(unsigned short *) (buf + i + 20); |
| *file_len = *(unsigned short *) (buf + i + 24) + (*(unsigned short *) (buf + i + 22) << 16); |
| i += 26 + fname_len; |
| i = ((i + 1) & ~1); |
| if (*(unsigned *) (buf + i) == 0x0b2d97f00) /* magic id */ |
| { |
| int v = buf[i + 4]; |
| |
| *code_start = *(unsigned *) (buf + i + 8); |
| *gfx_file_start = i; |
| if ((v>=5) && (v <= 7)) /* version 5 - 7 */ |
| return 1; |
| else if (v == 8) /* version 8 */ |
| return 2; |
| } |
| i += *file_len; |
| i = ((i + 1) & ~1); |
| } |
| else |
| break; |
| } |
| |
| return 0; |
| } |
| |
| #define MIN_GFX_FREE 0x1000 |
| |
| #define SC_BOOTLOADER 0 |
| #define SC_FAILSAFE 3 |
| #define SC_SYSCONFIG_SIZE 4 |
| #define SC_BOOTLOADER_SEG 8 |
| #define SC_XMEM_0 24 |
| #define SC_XMEM_1 26 |
| #define SC_XMEM_2 28 |
| #define SC_XMEM_3 30 |
| #define SC_FILE 32 |
| #define SC_ARCHIVE_START 36 |
| #define SC_ARCHIVE_END 40 |
| #define SC_MEM0_START 44 |
| #define SC_MEM0_END 48 |
| |
| unsigned long gfx_drive, gfx_partition; |
| |
| /* |
| * Does normally not return. |
| */ |
| static void |
| run_graphics_menu (char *menu_entries, char *config_entries, int num_entries, |
| char *heap, int entryno) |
| { |
| unsigned char *buf, *buf_ext; |
| unsigned buf_size, buf_ext_size, code_start, file_start; |
| char *cfg; |
| int i, j, max_len, gfx_file_size, verbose; |
| int selected_entry; |
| gfx_data_v1_t *gfx1; |
| gfx_data_v2_t *gfx2; |
| char *cmd_buf; |
| unsigned mem0_start, mem0_end, file_len; |
| int version; |
| unsigned long tmp_drive, tmp_partition; |
| |
| /* |
| * check gfx_data_t struct offsets for consistency; gcc will optimize away |
| * the whole block |
| */ |
| |
| /* dummy function to make ld fail */ |
| { |
| extern void wrong_struct_size(void); |
| #define gfx_ofs_check(a) if(gfx_ofs_v1_##a != (char *) &gfx1->a - (char *) &gfx1->ok) wrong_struct_size(); |
| gfx_ofs_check(ok); |
| gfx_ofs_check(mem_start); |
| gfx_ofs_check(mem_cur); |
| gfx_ofs_check(mem_max); |
| gfx_ofs_check(code_seg); |
| gfx_ofs_check(jmp_table); |
| gfx_ofs_check(sys_cfg); |
| gfx_ofs_check(cmdline); |
| gfx_ofs_check(cmdline_len); |
| gfx_ofs_check(menu_list); |
| gfx_ofs_check(menu_default_entry); |
| gfx_ofs_check(menu_entries); |
| gfx_ofs_check(menu_entry_len); |
| gfx_ofs_check(args_list); |
| gfx_ofs_check(args_entry_len); |
| gfx_ofs_check(timeout); |
| gfx_ofs_check(mem_file); |
| gfx_ofs_check(mem_align); |
| #undef gfx_ofs_check |
| |
| #define gfx_ofs_check(a) if(gfx_ofs_v2_##a != (char *) &gfx2->a - (char *) &gfx2->ok) wrong_struct_size(); |
| gfx_ofs_check(ok); |
| gfx_ofs_check(code_seg); |
| gfx_ofs_check(jmp_table); |
| gfx_ofs_check(sys_cfg); |
| gfx_ofs_check(cmdline); |
| gfx_ofs_check(cmdline_len); |
| gfx_ofs_check(menu_list); |
| gfx_ofs_check(menu_default_entry); |
| gfx_ofs_check(menu_entries); |
| gfx_ofs_check(menu_entry_len); |
| gfx_ofs_check(args_list); |
| gfx_ofs_check(args_entry_len); |
| gfx_ofs_check(timeout); |
| #undef gfx_ofs_check |
| } |
| |
| if(!num_entries) return; |
| |
| kernel_type = KERNEL_TYPE_NONE; |
| |
| gfx1 = (gfx_data_v1_t *) heap; |
| heap = (char *) (((unsigned) heap + sizeof (*gfx1) + 0xF) & ~0xF); |
| gfx2 = (gfx_data_v2_t *) heap; |
| heap += sizeof *gfx2; |
| memset((char *) gfx1, 0, (char *) heap - (char *) gfx1); |
| |
| //verbose = (*(unsigned char *) 0x417) & 3 ? 1 : 0; /* SHIFT pressed */ |
| verbose = (debug > 1); |
| |
| /* setup command line edit buffer */ |
| |
| gfx1->cmdline_len = gfx2->cmdline_len = 256; |
| gfx1->cmdline = gfx2->cmdline = heap; |
| heap += gfx1->cmdline_len; |
| memset(gfx1->cmdline, 0, gfx1->cmdline_len); |
| |
| cmd_buf = heap; |
| heap += GFX_CMD_BUF_SIZE; |
| |
| /* setup menu entries */ |
| |
| for (i = max_len = 0; i < num_entries; i++) |
| { |
| j = strlen(get_entry(menu_entries, i)); |
| if (j > max_len) |
| max_len = j; |
| } |
| |
| if (!max_len) return; |
| |
| gfx1->menu_entry_len = gfx2->menu_entry_len = max_len; |
| gfx1->menu_entries = gfx2->menu_entries = num_entries; |
| |
| gfx1->menu_list = gfx2->menu_list = heap; |
| heap += gfx1->menu_entry_len * gfx1->menu_entries; |
| |
| memset(gfx1->menu_list, 0, gfx1->menu_entry_len * gfx1->menu_entries); |
| |
| for(i = 0; i < (int) gfx1->menu_entries; i++) |
| { |
| /* Skip the leading '\t'. */ |
| strcpy (gfx1->menu_list + i * gfx1->menu_entry_len, get_entry(menu_entries, i) + 1); |
| } |
| |
| gfx1->menu_default_entry = gfx2->menu_default_entry = gfx1->menu_list + entryno * gfx1->menu_entry_len; |
| |
| gfx1->args_entry_len = gfx2->args_entry_len = 1; |
| gfx1->args_list = gfx2->args_list = heap; |
| heap += gfx1->args_entry_len * gfx1->menu_entries; |
| memset(gfx1->args_list, 0, gfx1->args_entry_len * gfx1->menu_entries); |
| |
| /* use 1MB starting at 4MB as file buffer */ |
| buf_ext = (unsigned char *) (4 << 20); |
| buf_ext_size = 1 << 20; |
| |
| /* must be 16-byte aligned */ |
| buf = (unsigned char *) (((unsigned) heap + 0xf) & ~0xf); |
| |
| buf_size = ((*((unsigned short *)0x413)) << 10) - (unsigned) buf; |
| buf_size &= ~0xf; |
| |
| mem0_start = (unsigned) buf; |
| mem0_end = mem0_start + buf_size; |
| |
| #ifdef GFX_DEBUG |
| if (verbose) |
| { |
| printf("low memory 0x%x - 0x%x (%d bytes)\n", mem0_start, mem0_end, buf_size); |
| } |
| #endif |
| |
| /* read the file */ |
| tmp_drive = saved_drive; |
| tmp_partition = saved_partition; |
| saved_drive = gfx_drive; |
| saved_partition = gfx_partition; |
| if (!grub_open(graphics_file)) |
| { |
| if (verbose) |
| printf("%s: file not found\n", graphics_file); |
| |
| saved_drive = tmp_drive; |
| saved_partition = tmp_partition; |
| return; |
| } |
| |
| gfx_file_size = grub_read(buf_ext, buf_ext_size, 0xedde0d90); |
| |
| grub_close(); |
| |
| saved_drive = tmp_drive; |
| saved_partition = tmp_partition; |
| |
| if (gfx_file_size <= 0) |
| { |
| if (verbose) |
| printf("%s: read error\n", graphics_file); |
| |
| return; |
| } |
| |
| #ifdef GFX_DEBUG |
| if (verbose) |
| { |
| printf("%s: %d bytes (%d bytes left)\n", graphics_file, gfx_file_size, buf_ext_size - gfx_file_size); |
| } |
| #endif |
| |
| /* locate file inside cpio archive */ |
| if (!(version = find_file(buf_ext, gfx_file_size, &file_start, &file_len, &code_start))) |
| { |
| if (verbose) |
| printf("%s: invalid file format\n", graphics_file); |
| |
| return; |
| } |
| |
| #ifdef GFX_DEBUG |
| if (verbose) |
| { |
| printf("init: start 0x%x, len %d; code offset 0x%x\n", file_start, file_len, code_start); |
| } |
| #endif |
| |
| if (version == 1) |
| { |
| unsigned u; |
| |
| if (gfx_file_size + MIN_GFX_FREE + 0xf >= (int) buf_size) |
| { |
| if (verbose) |
| printf("not enough free memory: %d extra bytes need\n", gfx_file_size + MIN_GFX_FREE - buf_size); |
| |
| return; |
| } |
| |
| memcpy ((void *) buf, (void *) buf_ext, gfx_file_size); |
| |
| gfx1->sys_cfg[0] = 2; /* bootloader: grub */ |
| gfx1->timeout = grub_timeout >= 0 ? grub_timeout : 0; |
| |
| gfx1->mem_start = (unsigned) buf; |
| gfx1->mem_max = gfx1->mem_start + buf_size; |
| |
| gfx1->mem_cur = gfx1->mem_start + |
| ((gfx_file_size + 0x0f + 3) & ~3); /* align it */ |
| |
| /* align it */ |
| u = (-(code_start + gfx1->mem_start + file_start)) & 0x0f; |
| gfx1->mem_align = gfx1->mem_start + u; |
| gfx1->mem_file = gfx1->mem_align + file_start; |
| if (u) |
| { |
| memcpy((void *) gfx1->mem_align, (void *) gfx1->mem_start, gfx_file_size); |
| } |
| |
| code_start += gfx1->mem_file; |
| gfx1->code_seg = code_start >> 4; |
| |
| for (i = 0; (unsigned) i < sizeof gfx1->jmp_table / sizeof *gfx1->jmp_table; i++) |
| { |
| gfx1->jmp_table[i] = (gfx1->code_seg << 16) + ((unsigned short *) code_start)[i]; |
| } |
| |
| if (gfx_init_v1 (gfx1)) |
| { |
| if (verbose) |
| printf("graphics initialization failed\n"); |
| |
| return; |
| } |
| |
| gfx_setup_menu_v1 (gfx1); |
| i = gfx_input_v1 (gfx1, &selected_entry); |
| gfx_done_v1 (gfx1); |
| } |
| else |
| { |
| if (file_len - code_start + MIN_GFX_FREE > buf_size) |
| { |
| if (verbose) |
| printf("not enough free memory: %d extra bytes need\n", file_len - code_start + MIN_GFX_FREE - buf_size); |
| |
| return; |
| } |
| |
| gfx2->sys_cfg[SC_BOOTLOADER] = 2; /* bootloader: grub */ |
| gfx2->sys_cfg[SC_SYSCONFIG_SIZE] = 52; /* config data size */ |
| *(unsigned short *) (gfx2->sys_cfg + SC_BOOTLOADER_SEG) = (unsigned) gfx2 >> 4; /* segment */ |
| gfx2->sys_cfg[SC_XMEM_0] = 0x21; /* 1MB @ 2MB */ |
| gfx2->sys_cfg[SC_XMEM_1] = 0x41; /* 1MB @ 4MB */ |
| gfx2->sys_cfg[SC_FAILSAFE] = verbose; |
| gfx2->timeout = grub_timeout >= 0 ? grub_timeout : 0; |
| |
| memcpy((void *) buf, (void *) (buf_ext + file_start + code_start), file_len - code_start); |
| |
| mem0_start += file_len - code_start; |
| mem0_start = (mem0_start + 3) & ~3; /* align */ |
| |
| /* init interface to graphics functions */ |
| |
| *(unsigned *) (gfx2->sys_cfg + SC_FILE) = (unsigned) buf_ext + file_start; |
| *(unsigned *) (gfx2->sys_cfg + SC_ARCHIVE_START) = (unsigned) buf_ext; |
| *(unsigned *) (gfx2->sys_cfg + SC_ARCHIVE_END) = (unsigned) buf_ext + gfx_file_size; |
| *(unsigned *) (gfx2->sys_cfg + SC_MEM0_START) = mem0_start; |
| *(unsigned *) (gfx2->sys_cfg + SC_MEM0_END) = mem0_end; |
| |
| gfx2->code_seg = (unsigned) buf >> 4; |
| |
| #ifdef GFX_DEBUG |
| |
| if (verbose) |
| { |
| printf("init 0x%x, archive 0x%x - 0x%x, low mem 0x%x - 0x%x\ncode seg 0x%x\n", |
| (unsigned) buf_ext + file_start, |
| (unsigned) buf_ext, (unsigned) buf_ext + gfx_file_size, |
| mem0_start, mem0_end, gfx2->code_seg |
| ); |
| } |
| #endif |
| |
| for (i = 0; (unsigned) i < sizeof gfx2->jmp_table / sizeof *gfx2->jmp_table; i++) |
| { |
| gfx2->jmp_table[i] = (gfx2->code_seg << 16) + ((unsigned short *) buf)[i]; |
| } |
| |
| #ifdef GFX_DEBUG |
| if (verbose) |
| { |
| for(i = 0; i < 12; i++) |
| { |
| printf("%d: 0x%x\n", i, gfx2->jmp_table[i]); |
| } |
| |
| for(i = 0; i < gfx2->menu_entries; i++) |
| { |
| printf("\"%s\" -- \"%s\"\n", |
| gfx2->menu_list + i * gfx2->menu_entry_len, |
| gfx2->args_list + i * gfx2->args_entry_len |
| ); |
| } |
| |
| printf("default: \"%s\"\n", gfx2->menu_default_entry); |
| } |
| #endif |
| |
| /* switch to graphics mode */ |
| if (gfx_init_v2 (gfx2)) |
| { |
| if (verbose) |
| printf("graphics initialization failed\n"); |
| |
| return; |
| } |
| |
| gfx_setup_menu_v2 (gfx2); |
| i = gfx_input_v2 (gfx2, &selected_entry); |
| gfx_done_v2 (gfx2); |
| } |
| |
| memset ((char *) HISTORY_BUF, 0, HISTORY_BUFLEN); |
| /* ESC -> show text menu */ |
| |
| if (i == 1) |
| { |
| grub_timeout = -1; |
| return; |
| } |
| |
| //heap = saved_heap; /* free most of the graphics data */ |
| |
| // printf("cmdline: >%s<, entry = %d\n", gfx_data->cmdline, selected_entry); |
| |
| if (selected_entry < 0 || selected_entry > num_entries) |
| return; |
| |
| /* for 'savedefault' */ |
| current_entryno = selected_entry; |
| |
| cfg = get_entry(menu_entries, selected_entry); |
| while (*(cfg++)) |
| ; |
| |
| run_script(cfg, heap); |
| } |
| |
| #endif /* SUPPORT_GFX */ |
| |
| static int |
| get_line_from_config (char *cmdline, int max_len, int preset) |
| { |
| unsigned long pos = 0, info = 0;//literal = 0, comment = 0; |
| char c; /* since we're loading it a byte at a time! */ |
| |
| while (1) |
| { |
| #if 0 |
| /* Skip UTF-8 Byte Order Mark: EF BB BF */ |
| { |
| unsigned long menu_offset; |
| |
| menu_offset = read_from_file ? filepos : preset_menu_offset; |
| if (menu_offset == 0) |
| { |
| /* read 3 bytes, check UTF-8 Byte Order Mark: EF BB BF */ |
| if (read_from_file) |
| { |
| if (3 != grub_read (&menu_offset, 3)) |
| { |
| filepos = 0; |
| break; |
| } |
| if (menu_offset != 0xBFBBEF) |
| { |
| filepos = 0; |
| } |
| } |
| else |
| { |
| if (3 != read_from_preset_menu (&menu_offset, 3)) |
| { |
| preset_menu_offset = 0; |
| break; |
| } |
| if (menu_offset != 0xBFBBEF) |
| { |
| preset_menu_offset = 0; |
| } |
| } |
| } |
| } |
| #endif |
| |
| if (preset) |
| { |
| if (! read_from_preset_menu (&c, 1)) |
| break; |
| } |
| else |
| { |
| if (! grub_read (&c, 1, 0xedde0d90)) |
| break; |
| } |
| |
| ///* Skip UTF-8 Byte Order Mark: EF BB BF */ |
| //if ((c & 0x80) && pos < 3) |
| // continue; |
| |
| /* Replace CR with LF. */ |
| if (c == '\r') |
| c = '\n'; |
| |
| /* Replace tabs with spaces. */ |
| if (c == '\t')//( || c == '\f' || c == '\v') |
| c = ' '; |
| |
| /* all other non-printable chars are illegal. */ |
| if (c != '\n' && (unsigned char)c < ' ') |
| break; |
| |
| /* The previous is a backslash, then... */ |
| if (info & 1) /* bit 0 for literal */ |
| { |
| /* If it is a newline, replace it with a space and continue. */ |
| if (c == '\n') |
| { |
| c = ' '; |
| |
| /* Go back to overwrite a backslash. */ |
| if (pos > 0) |
| pos--; |
| } |
| |
| info &= 0xFFFFFFFE; //literal = 0; |
| } |
| |
| ///* Replace semi-colon with LF. */ |
| //if (c == ';') |
| // c = '\n'; |
| |
| /* translate characters first! */ |
| if (c == '\\') |
| info |= 1; //literal = 1; |
| |
| if (info & 2) /* bit 1 for comment */ |
| { |
| if (c == '\n') |
| info &= 0xFFFFFFFD; //comment = 0; |
| /* Skip all comment chars upto end of line. */ |
| } |
| else if (! pos) |
| { |
| /* At the very beginning of the line... */ |
| if (c == '#') |
| { |
| info |= 2; //comment = 1; |
| /* Skip the comment char. */ |
| } |
| else |
| { |
| /* Skip non-printable chars, including the UTF-8 Byte Order Mark: EF BB BF */ |
| if ((unsigned char)c > ' ' && (unsigned char)c <= 0x7F) //((c != ' ') && (c != '\t') && (c != '\n') && (c != '\r')) |
| cmdline[pos++] = c; |
| } |
| } |
| else |
| { |
| if (c == '\n') |
| break; |
| |
| if (!(info & 4) && ((c & 0x80) || pos > 31)) /* bit 2 for argument */ |
| break; |
| |
| if (pos < max_len) |
| { |
| if (!(info & 4) && c == '=') |
| c = ' '; |
| if (c == ' ') |
| info |= 4; //argument = 1; |
| cmdline[pos++] = c; |
| } |
| } |
| } |
| |
| cmdline[pos] = 0; |
| |
| return pos; |
| } |
| |
| //void |
| //reset (void); |
| int config_len; |
| static int num_entries; |
| static char *config_entries, *cur_entry; |
| |
| static void |
| reset (void) |
| { |
| count_lines = -1; |
| config_len = 0; |
| num_entries = 0; |
| cur_entry = config_entries; |
| |
| /* Initialize the data for the configuration file. */ |
| default_entry = 0; |
| password = 0; |
| fallback_entryno = -1; |
| fallback_entries[0] = -1; |
| grub_timeout = -1; |
| } |
| |
| static unsigned long attr = 0; |
| |
| /* This is the starting function in C. */ |
| void |
| cmain (void) |
| { |
| #ifdef GRUB_UTIL |
| /* Initialize the environment for restarting Stage 2. */ |
| grub_setjmp (restart_env); |
| #endif /* GRUB_UTIL */ |
| |
| #ifndef GRUB_UTIL |
| debug = debug_boot + 1; |
| #endif /* ! GRUB_UTIL */ |
| |
| titles = (char * *)init_free_mem_start; |
| config_entries = ((char *) init_free_mem_start) + 256 * sizeof (char *); |
| |
| /* Never return. */ |
| restart: |
| reset (); |
| |
| /* Here load the configuration file. */ |
| |
| if (! use_config_file) |
| goto done_config_file; |
| |
| /* Get a saved default entry if possible. */ |
| saved_entryno = 0; |
| if (*config_file) |
| { |
| char *default_file = (char *) DEFAULT_FILE_BUF; |
| |
| *default_file = 0; /* initialise default_file */ |
| grub_strncat (default_file, config_file, DEFAULT_FILE_BUFLEN); |
| { |
| int i; |
| for (i = grub_strlen (default_file); i >= 0; i--) |
| if (default_file[i] == '/') |
| break; |
| default_file[++i] = 0; |
| grub_strncat (default_file + i, "default", DEFAULT_FILE_BUFLEN - i); |
| } |
| if (debug > 1) |
| grub_printf("Open %s ... ", default_file); |
| DEBUG_SLEEP |
| |
| if (grub_open (default_file)) |
| { |
| char buf[10]; /* This is good enough. */ |
| char *p = buf; |
| int len; |
| |
| if (debug > 1) |
| grub_printf("Read file: ", default_file); |
| len = grub_read (buf, sizeof (buf), 0xedde0d90); |
| if (debug > 1) |
| grub_printf("len=%d\n", len); |
| if (len > 0) |
| { |
| buf[sizeof (buf) - 1] = 0; |
| safe_parse_maxint (&p, &saved_entryno); |
| } |
| |
| grub_close (); |
| } |
| else if (debug > 1) |
| grub_printf("failure.\n", default_file); |
| DEBUG_SLEEP |
| } |
| errnum = ERR_NONE; |
| |
| #ifndef GRUB_UTIL |
| pxe_restart_config = 0; |
| #endif /* ! GRUB_UTIL */ |
| |
| #ifndef GRUB_UTIL |
| restart_config: |
| #endif /* ! GRUB_UTIL */ |
| |
| { |
| /* STATE 0: Menu init, i.e., before any title command. |
| STATE 1: In a title command. |
| STATE 2: In a entry after a title command. */ |
| int state = 0, prev_config_len = 0; |
| char *cmdline; |
| int is_preset; |
| |
| { |
| int is_opened; |
| |
| is_preset = is_opened = 0; |
| /* Try command-line menu first if it is specified. */ |
| if (preset_menu == (char *)0x0800 /*&& ! *config_file*/) |
| { |
| is_opened = is_preset = open_preset_menu (); |
| } |
| if (! is_opened) |
| { |
| /* Try config_file */ |
| if (*config_file) |
| is_opened = grub_open (config_file); |
| } |
| errnum = ERR_NONE; |
| if (! is_opened) |
| { |
| #ifndef GRUB_UTIL |
| if (pxe_restart_config) |
| goto original_config; |
| #endif /* ! GRUB_UTIL */ |
| |
| /* Try the preset menu. This will succeed at most once, |
| * because the preset menu will be disabled(see below). */ |
| is_opened = is_preset = open_preset_menu (); |
| } |
| |
| if (! is_opened) |
| goto done_config_file; |
| } |
| |
| /* This is necessary, because the menu must be overrided. */ |
| reset (); |
| cmdline = (char *) CMDLINE_BUF; |
| |
| while (get_line_from_config (cmdline, NEW_HEAPSIZE, is_preset)) |
| { |
| struct builtin *builtin; |
| |
| /* Get the pointer to the builtin structure. */ |
| builtin = find_command (cmdline); |
| errnum = 0; |
| if (! builtin) |
| /* Unknown command. Just skip now. */ |
| continue; |
| |
| if (builtin->flags & BUILTIN_TITLE) /* title command */ |
| { |
| /* Finish the menu init commands or previous menu items. */ |
| |
| if (state == 2) |
| { |
| /* The next title is found. */ |
| if (num_entries >= 256) |
| break; |
| num_entries++; /* an entry is completed. */ |
| config_entries[config_len++] = 0; /* finish the entry. */ |
| prev_config_len = config_len; |
| } |
| else if (state) /* state == 1 */ |
| { |
| /* previous is an invalid title, overwrite it. */ |
| config_len = prev_config_len; |
| } |
| else /* state == 0 */ |
| { |
| /* The first title. So finish the menu init commands. */ |
| config_entries[config_len++] = 0; |
| } |
| |
| /* Reset the state. */ |
| state = 1; |
| |
| /* Copy title into config area. */ |
| { |
| char *ptr = cmdline; |
| while (*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '=') |
| ptr++; |
| if (*ptr) |
| ptr++; |
| attr = config_len; |
| if (num_entries < 256) |
| titles[num_entries] = config_entries + config_len; |
| config_entries[config_len++] = 0x08; /* attribute byte */ |
| while ((config_entries[config_len++] = *(ptr++)) != 0); |
| } |
| } |
| else if (! state) /* menu init command */ |
| { |
| if (builtin->flags & BUILTIN_MENU) |
| { |
| char *ptr = cmdline; |
| /* Copy menu-specific commands to config area. */ |
| while ((config_entries[config_len++] = *ptr++) != 0); |
| prev_config_len = config_len; |
| } |
| else |
| /* Ignored. */ |
| continue; |
| } |
| else /* menu item command */ |
| { |
| state = 2; |
| |
| /* Copy config file data to config area. */ |
| { |
| char *ptr = cmdline; |
| while ((config_entries[config_len++] = *ptr++) != 0); |
| prev_config_len = config_len; |
| } |
| config_entries[attr] |= !!(builtin->flags & BUILTIN_BOOTING); |
| } |
| } /* while (get_line_from_config()) */ |
| |
| /* file must be closed here, because the menu-specific commands |
| * below may also use the GRUB_OPEN command. */ |
| if (is_preset) |
| preset_menu = 0; /* Disable the preset menu. */ |
| else |
| grub_close (); |
| |
| if (state == 2) |
| { |
| if (num_entries < 256) |
| num_entries++; /* the last entry is completed. */ |
| } |
| else// if (state) |
| { |
| config_len = prev_config_len; |
| } |
| |
| /* Finish the last entry or the menu init commands. */ |
| config_entries[config_len++] = 0; |
| if (num_entries < 256) |
| titles[num_entries] = 0; |
| |
| /* old MENU_BUF is not used any more. So MENU_BUF is a temp area, |
| * and can be moved to elsewhere. */ |
| |
| /* config_entries contains these: |
| * 1. The array of menu init commands. |
| * 2. The array of menu item commands with leading titles. |
| */ |
| |
| /* Run menu-specific commands before any other menu entry commands. */ |
| |
| { |
| char *old_entry; |
| char *heap = config_entries + config_len; |
| |
| #ifndef GRUB_UTIL |
| int old_debug = 1; |
| #endif /* ! GRUB_UTIL */ |
| |
| /* Initialize the data. */ |
| //saved_drive = boot_drive; |
| //saved_partition = install_partition; |
| current_drive = GRUB_INVALID_DRIVE; |
| count_lines = -1; |
| |
| kernel_type = KERNEL_TYPE_NONE; |
| errnum = 0; |
| |
| while (1) |
| { |
| struct builtin *builtin; |
| char *arg; |
| grub_error_t errnum_old; |
| |
| #ifndef GRUB_UTIL |
| pxe_restart_config = 0; |
| #endif /* ! GRUB_UTIL */ |
| |
| #if 0 |
| #ifndef GRUB_UTIL |
| /* pxe_detect should be done before any other command. */ |
| if (! *config_entries) |
| { |
| if (pxe_entry && ! pxe_inited) |
| { |
| if (pxe_detect (0, 0)) |
| goto restart_config; |
| } |
| } |
| #endif /* ! GRUB_UTIL */ |
| #endif |
| |
| errnum_old = errnum; |
| errnum = 0; |
| |
| /* Copy the first string in CUR_ENTRY to HEAP. */ |
| old_entry = cur_entry; |
| while (*cur_entry++); |
| |
| grub_memmove (heap, old_entry, (int) cur_entry - (int) old_entry); |
| if (! *heap) |
| { |
| /* If there is no more command in SCRIPT... */ |
| |
| /* If no kernel is loaded, just exit successfully. */ |
| if (kernel_type == KERNEL_TYPE_NONE) |
| break; |
| |
| /* Otherwise, the command boot is run implicitly. */ |
| grub_memmove (heap, "boot", 5); |
| } |
| |
| /* Find a builtin. */ |
| builtin = find_command (heap); |
| if (! builtin) |
| { |
| grub_printf ("%s\n", old_entry); |
| continue; |
| } |
| |
| #if 0 |
| #ifndef GRUB_UTIL |
| /* pxe_detect should be done before any other command. */ |
| if ((builtin->func) != pxe_func && old_entry == config_entries) |
| { |
| if (pxe_entry && ! pxe_inited) |
| { |
| if (pxe_detect (0, 0)) |
| goto restart_config; |
| } |
| } |
| #endif /* ! GRUB_UTIL */ |
| #endif |
| |
| /* If BUILTIN cannot be run in the menu, skip it. */ |
| if (! (builtin->flags & BUILTIN_MENU)) |
| { |
| continue; |
| } |
| |
| if ((builtin->func) == errnum_func && (builtin->func) == checkrange_func) |
| errnum = errnum_old; |
| |
| /* find && and || */ |
| |
| for (arg = skip_to (0, heap); *arg != 0; arg = skip_to (0, arg)) |
| { |
| struct builtin *builtin1; |
| int ret; |
| char *arg1; |
| arg1 = arg; |
| if (*arg == '&' && arg[1] == '&' && (arg[2] == ' ' || arg[2] == '\t')) |
| { |
| /* handle the AND operator */ |
| arg = skip_to (0, arg); |
| builtin1 = find_command (arg); |
| if (! builtin1 || ! (builtin1->flags & BUILTIN_MENU)) |
| { |
| errnum = ERR_UNRECOGNIZED; |
| goto next; |
| } |
| |
| *arg1 = 0; |
| ret = (builtin->func) (skip_to (1, heap), BUILTIN_MENU); |
| *arg1 = '&'; |
| if (ret) |
| { |
| arg = skip_to (1, arg); |
| if ((builtin1->func) != errnum_func && (builtin1->func) != checkrange_func) |
| errnum = 0; |
| (builtin1->func) (arg, BUILTIN_MENU); |
| } else |
| errnum = 0; |
| goto next; |
| } else if (*arg == '|' && arg[1] == '|' && (arg[2] == ' ' || arg[2] == '\t')) |
| { |
| /* handle the OR operator */ |
| arg = skip_to (0, arg); |
| builtin1 = find_command (arg); |
| if (! builtin1 || ! (builtin1->flags & BUILTIN_MENU)) |
| { |
| errnum = ERR_UNRECOGNIZED; |
| goto next; |
| } |
| |
| *arg1 = 0; |
| ret = (builtin->func) (skip_to (1, heap), BUILTIN_MENU); |
| *arg1 = '|'; |
| if (! ret) |
| { |
| arg = skip_to (1, arg); |
| if ((builtin1->func) != errnum_func && (builtin1->func) != checkrange_func) |
| errnum = 0; |
| (builtin1->func) (arg, BUILTIN_MENU); |
| } else |
| errnum = 0; |
| goto next; |
| } |
| } |
| |
| /* Run BUILTIN->FUNC. */ |
| arg = (builtin->func) == commandline_func ? heap : skip_to (1, heap); |
| (builtin->func) (arg, BUILTIN_MENU); |
| |
| /* if the INSERT key was pressed at startup, debug is not allowed to be turned off. */ |
| #ifndef GRUB_UTIL |
| if (debug_boot) |
| if ((unsigned int)debug < 2) /* debug == 0 or 1 */ |
| { |
| old_debug = debug; /* save the new debug in old_debug */ |
| debug = 2; |
| } |
| #endif /* ! GRUB_UTIL */ |
| |
| next: |
| DEBUG_SLEEP |
| |
| #ifndef GRUB_UTIL |
| if (pxe_restart_config) |
| goto restart_config; |
| #endif /* ! GRUB_UTIL */ |
| |
| #ifndef GRUB_UTIL |
| original_config: |
| #endif /* ! GRUB_UTIL */ |
| |
| if (! *old_entry) |
| break; |
| } /* while (1) */ |
| |
| kernel_type = KERNEL_TYPE_NONE; |
| |
| #ifndef GRUB_UTIL |
| if (debug_boot) |
| { |
| debug = old_debug; |
| grub_printf ("\n\nEnd of menu init commands. Press any key to enter command-line or run menu...", old_entry); |
| } |
| #endif /* ! GRUB_UTIL */ |
| DEBUG_SLEEP |
| } |
| |
| /* End of menu-specific commands. */ |
| |
| errnum = 0; |
| |
| /* Make sure that all fallback entries are valid. */ |
| if (fallback_entryno >= 0) |
| { |
| int i; |
| |
| for (i = 0; i < MAX_FALLBACK_ENTRIES; i++) |
| { |
| if (fallback_entries[i] < 0) |
| break; |
| if (fallback_entries[i] >= num_entries) |
| { |
| grub_memmove (fallback_entries + i, fallback_entries + i + 1, ((MAX_FALLBACK_ENTRIES - i - 1) * sizeof (int))); |
| i--; |
| } |
| } |
| |
| if (fallback_entries[0] < 0) |
| fallback_entryno = -1; |
| } |
| |
| /* Check if the default entry is present. Otherwise reset it to |
| fallback if fallback is valid, or to DEFAULT_ENTRY if not. */ |
| if (default_entry >= num_entries) |
| { |
| if (fallback_entryno >= 0) |
| { |
| default_entry = fallback_entries[0]; |
| fallback_entryno++; |
| if (fallback_entryno >= MAX_FALLBACK_ENTRIES || fallback_entries[fallback_entryno] < 0) |
| fallback_entryno = -1; |
| } |
| else |
| default_entry = 0; |
| } |
| } |
| |
| done_config_file: |
| |
| #ifndef GRUB_UTIL |
| pxe_restart_config = 1; /* pxe_detect will use configfile to run menu */ |
| #endif /* ! GRUB_UTIL */ |
| |
| /* go ahead and make sure the terminal is setup */ |
| if (current_term->startup) |
| (*current_term->startup)(); |
| |
| if (! num_entries) |
| { |
| /* no config file, goto command-line, starting heap from where the |
| config entries would have been stored if there were any. */ |
| enter_cmdline (config_entries, 1); |
| } |
| else |
| { |
| #ifdef SUPPORT_GFX |
| if (*graphics_file && !password && show_menu && grub_timeout) |
| { |
| run_graphics_menu((char *)titles, cur_entry, num_entries, config_entries + config_len, default_entry); |
| } |
| #endif |
| /* Run menu interface. */ |
| /* cur_entry point to the first menu item command. */ |
| run_menu ((char *)titles, cur_entry, num_entries, config_entries + config_len, default_entry); |
| } |
| goto restart; |
| } |