#include <shared.h>
#include <term.h>
#include <expand.h>
#define MENU_ROWS 12
grub_jmp_buf restart_env;
struct silentbuf silent;
int reset_term;
#if defined(PRESET_MENU_STRING) || defined(SUPPORT_DISKLESS)
# if defined(PRESET_MENU_STRING)
static const char *preset_menu = PRESET_MENU_STRING;
# elif defined(SUPPORT_DISKLESS)
static const char *preset_menu = "dhcp\n";
# endif
static int preset_menu_offset;
static int
open_preset_menu (void)
{
#ifdef GRUB_UTIL
if (! use_preset_menu)
return 0;
#endif
preset_menu_offset = 0;
return preset_menu != 0;
}
static int
read_from_preset_menu (char *buf, int maxlen)
{
int len = grub_strlen (preset_menu + preset_menu_offset);
if (len > maxlen)
len = maxlen;
grub_memmove (buf, preset_menu + preset_menu_offset, len);
preset_menu_offset += len;
return len;
}
static void
close_preset_menu (void)
{
preset_menu = 0;
}
#else
#define open_preset_menu() 0
#define read_from_preset_menu(buf, maxlen) 0
#define close_preset_menu()
#endif
static char *
get_entry (char *list, int num, int nested)
{
int i;
for (i = 0; i < num; i++)
{
do
{
while (*(list++));
}
while (nested && *(list++));
}
return list;
}
static void
print_entry (int y, int highlight, char *entry)
{
int x;
if (current_term->setcolorstate)
current_term->setcolorstate (COLOR_STATE_NORMAL);
if (highlight && current_term->setcolorstate)
current_term->setcolorstate (COLOR_STATE_HIGHLIGHT);
gotoxy (2, y);
grub_putchar (' ');
for (x = 3; x < 75; x++)
{
if (*entry && x <= 72)
{
if (x == 72)
grub_putchar (DISP_RIGHT);
else
grub_putchar (*entry++);
}
else
grub_putchar (' ');
}
gotoxy (74, y);
if (current_term->setcolorstate)
current_term->setcolorstate (COLOR_STATE_STANDARD);
}
static void
print_entries (int y, int size, int first, int entryno, char *menu_entries)
{
int i;
gotoxy (77, y + 1);
if (first)
grub_putchar (DISP_UP);
else
grub_putchar (' ');
menu_entries = get_entry (menu_entries, first, 0);
for (i = 0; i < size; i++)
{
print_entry (y + i + 1, entryno == i, menu_entries);
while (*menu_entries)
menu_entries++;
if (*(menu_entries - 1))
menu_entries++;
}
gotoxy (77, y + size);
if (*menu_entries)
grub_putchar (DISP_DOWN);
else
grub_putchar (' ');
gotoxy (74, y + entryno + 1);
}
static void
print_entries_raw (int size, int first, char *menu_entries)
{
int i;
#define LINE_LENGTH 67
for (i = 0; i < LINE_LENGTH; i++)
grub_putchar ('-');
grub_putchar ('\n');
for (i = first; i < size; i++)
{
if (i < 10)
grub_putchar (' ');
grub_printf ("%d: %s\n", i, get_entry (menu_entries, i, 0));
}
for (i = 0; i < LINE_LENGTH; i++)
grub_putchar ('-');
grub_putchar ('\n');
#undef LINE_LENGTH
}
static void
print_border (int y, int size)
{
int i;
if (current_term->setcolorstate)
current_term->setcolorstate (COLOR_STATE_NORMAL);
gotoxy (1, y);
grub_putchar (DISP_UL);
for (i = 0; i < 73; i++)
grub_putchar (DISP_HORIZ);
grub_putchar (DISP_UR);
i = 1;
while (1)
{
gotoxy (1, y + i);
if (i > size)
break;
grub_putchar (DISP_VERT);
gotoxy (75, y + i);
grub_putchar (DISP_VERT);
i++;
}
grub_putchar (DISP_LL);
for (i = 0; i < 73; i++)
grub_putchar (DISP_HORIZ);
grub_putchar (DISP_LR);
if (current_term->setcolorstate)
current_term->setcolorstate (COLOR_STATE_STANDARD);
}
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;
const char *console = NULL;
restart:
if (! (current_term->flags & TERM_DUMB))
{
while (entryno > MENU_ROWS - 1)
{
first_entry++;
entryno--;
}
}
if (grub_timeout < 0)
show_menu = 1;
if (! show_menu)
{
while ((time1 = getrtsecs ()) == 0xFF)
;
while (1)
{
if (checkkey () != -1 && ASCII_CHAR (getkey ()) == '\e')
{
grub_timeout = -1;
show_menu = 1;
break;
}
if (grub_timeout >=0
&& (time1 = getrtsecs ()) != time2
&& time1 != 0xFF)
{
if (grub_timeout <= 0)
{
grub_timeout = -1;
goto boot_entry;
}
time2 = time1;
grub_timeout--;
grub_printf ("\rPress `ESC' to enter the menu... %d ",
grub_timeout);
}
}
}
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 (3, MENU_ROWS);
grub_printf ("\n\
Use the %c and %c keys to select which entry is highlighted.\n",
DISP_UP, DISP_DOWN);
if (! auth && password)
{
printf ("\
Press enter to boot the selected OS or \'p\' to enter a\n\
password to unlock the next set of features.");
}
else
{
if (config_entries)
printf ("\
Press enter to boot the selected OS, \'e\' to edit the\n\
commands before booting, or \'c\' for a command-line.");
else
printf ("\
Press \'b\' to boot, \'e\' to edit the selected command in the\n\
boot sequence, \'c\' for a command-line, \'o\' to open a new line\n\
after (\'O\' for before) the selected line, \'d\' to remove the\n\
selected line, or escape to go back to the main menu.");
}
console = get_variable("os_console");
if (console != NULL) {
printf("\n\n Selected OS console device is '%s'."
"\n To change OS console device, enter command-line mode"
"\n and use 'variable os_console <dev>', then Esc to return."
"\n Valid <dev> values are: ttya, ttyb, ttyc, ttyd, vga",
console);
}
if (current_term->flags & TERM_DUMB)
grub_printf ("\n\nThe selected entry is %d ", entryno);
else
print_entries (3, MENU_ROWS, first_entry, entryno, menu_entries);
}
while ((time1 = getrtsecs()) == 0xFF);
while (1)
{
cur_entry = NULL;
if (grub_timeout >= 0 && (time1 = getrtsecs()) != time2 && time1 != 0xFF)
{
if (grub_timeout <= 0)
{
grub_timeout = -1;
break;
}
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 (3, 22);
grub_printf ("The highlighted entry will be booted automatically in %d seconds. ",
grub_timeout);
gotoxy (74, 4 + entryno);
}
grub_timeout--;
}
if (checkkey () >= 0 || grub_timeout < 0)
{
if (current_term->flags & TERM_DUMB)
grub_printf ("\r Highlighted entry is %d: ", entryno);
c = ASCII_CHAR (getkey ());
if (grub_timeout >= 0)
{
if (current_term->flags & TERM_DUMB)
grub_putchar ('\r');
else
gotoxy (3, 22);
printf (" ");
grub_timeout = -1;
fallback_entryno = -1;
if (! (current_term->flags & TERM_DUMB))
gotoxy (74, 4 + entryno);
}
if (c == 16 || c == '^')
{
if (current_term->flags & TERM_DUMB)
{
if (entryno > 0)
entryno--;
}
else
{
if (entryno > 0)
{
print_entry (4 + entryno, 0,
get_entry (menu_entries,
first_entry + entryno,
0));
entryno--;
print_entry (4 + entryno, 1,
get_entry (menu_entries,
first_entry + entryno,
0));
}
else if (first_entry > 0)
{
first_entry--;
print_entries (3, MENU_ROWS, first_entry, entryno,
menu_entries);
}
}
}
else if ((c == 14 || c == 'v')
&& first_entry + entryno + 1 < num_entries)
{
if (current_term->flags & TERM_DUMB)
entryno++;
else
{
if (entryno < MENU_ROWS - 1)
{
print_entry (4 + entryno, 0,
get_entry (menu_entries,
first_entry + entryno,
0));
entryno++;
print_entry (4 + entryno, 1,
get_entry (menu_entries,
first_entry + entryno,
0));
}
else if (num_entries > MENU_ROWS + first_entry)
{
first_entry++;
print_entries (3, MENU_ROWS, first_entry, entryno, menu_entries);
}
}
}
else if (c == 7)
{
first_entry -= MENU_ROWS;
if (first_entry < 0)
{
entryno += first_entry;
first_entry = 0;
if (entryno < 0)
entryno = 0;
}
print_entries (3, MENU_ROWS, first_entry, entryno, menu_entries);
}
else if (c == 3)
{
first_entry += MENU_ROWS;
if (first_entry + entryno + 1 >= num_entries)
{
first_entry = num_entries - MENU_ROWS;
if (first_entry < 0)
first_entry = 0;
entryno = num_entries - first_entry - 1;
}
print_entries (3, MENU_ROWS, first_entry, entryno, menu_entries);
}
if (config_entries)
{
if ((c == '\n') || (c == '\r') || (c == 6))
break;
}
else
{
if ((c == 'd') || (c == 'o') || (c == 'O'))
{
if (! (current_term->flags & TERM_DUMB))
print_entry (4 + entryno, 0,
get_entry (menu_entries,
first_entry + entryno,
0));
if (c == 'o')
{
if (entryno < MENU_ROWS - 1 ||
(current_term->flags & TERM_DUMB))
entryno++;
else
first_entry++;
c = 'O';
}
cur_entry = get_entry (menu_entries,
first_entry + entryno,
0);
if (c == 'O')
{
grub_memmove (cur_entry + 2, cur_entry,
((int) heap) - ((int) cur_entry));
cur_entry[0] = ' ';
cur_entry[1] = 0;
heap += 2;
num_entries++;
}
else if (num_entries > 0)
{
char *ptr = get_entry(menu_entries,
first_entry + entryno + 1,
0);
grub_memmove (cur_entry, ptr,
((int) heap) - ((int) ptr));
heap -= (((int) ptr) - ((int) cur_entry));
num_entries--;
if (entryno >= num_entries)
entryno--;
if (first_entry && num_entries < MENU_ROWS + 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
print_entries (3, MENU_ROWS, first_entry, entryno, menu_entries);
}
cur_entry = menu_entries;
if (c == 27)
return;
if (c == 'b')
break;
}
if (! auth && password)
{
if (c == 'p')
{
char entered[32];
char *pptr = password;
if (current_term->flags & TERM_DUMB)
grub_printf ("\r ");
else
gotoxy (1, 21);
grub_memset (entered, 0, sizeof (entered));
get_cmdline (" Password: ", entered, 31, '*', 0);
while (! isspace (*pptr) && *pptr)
pptr++;
*pptr++ = 0;
if (! check_password (entered, password, password_type))
{
char *new_file = config_file;
while (isspace (*pptr))
pptr++;
if (*pptr != 0)
{
while ((*(new_file++) = *(pptr++)) != 0)
;
auth = 0;
return;
}
else
{
auth = 1;
goto restart;
}
}
else
{
grub_printf ("Failed!\n Press any key to continue...");
getkey ();
goto restart;
}
}
}
else
{
if (c == 'e')
{
int new_num_entries = 0, i = 0;
char *new_heap;
if (config_entries)
{
new_heap = heap;
cur_entry = get_entry (config_entries,
first_entry + entryno,
1);
}
else
{
new_heap = heap + NEW_HEAPSIZE + 1;
cur_entry = get_entry (menu_entries,
first_entry + entryno,
0);
}
do
{
while ((*(new_heap++) = cur_entry[i++]) != 0);
new_num_entries++;
}
while (config_entries && cur_entry[i]);
*(new_heap++) = 0;
if (config_entries)
run_menu (heap, NULL, new_num_entries, new_heap, 0);
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;
if (! get_cmdline (PACKAGE " edit> ", new_heap,
NEW_HEAPSIZE + 1, 0, 1))
{
int j = 0;
while (new_heap[j++])
;
if (j < 2)
{
j = 2;
new_heap[0] = ' ';
new_heap[1] = 0;
}
grub_memmove (cur_entry + j, cur_entry + i,
(int) heap - ((int) cur_entry + i));
grub_memmove (cur_entry, new_heap, j);
heap += (j - i);
}
}
goto restart;
}
if (c == 'c')
{
enter_cmdline (heap, 0);
goto restart;
}
#ifdef GRUB_UTIL
if (c == 'q')
{
stop ();
}
#endif
}
}
}
boot_entry:
if (silent.status != DEFER_VERBOSE)
silent.status = SILENT;
reset_term = 1;
cls ();
setcursor (1);
prev_term = current_term;
if (silent.status != SILENT)
if (current_term->shutdown) {
(*current_term->shutdown)();
current_term = term_table;
}
while (1)
{
if (config_entries)
printf (" Booting \'%s\'\n\n",
get_entry (menu_entries, first_entry + entryno, 0));
else
printf (" Booting command-list\n\n");
if (! cur_entry)
cur_entry = get_entry (config_entries, first_entry + entryno, 1);
current_entryno = first_entry + entryno;
if (run_script (cur_entry, heap))
{
if (fallback_entryno >= 0)
{
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;
}
else
break;
}
else
break;
}
if (silent.status != SILENT) {
current_term = prev_term;
if (current_term->startup)
if ((*current_term->startup)() == 0)
current_term = term_table;
}
show_menu = 1;
goto restart;
}
static int
get_line_from_config (char *cmdline, int maxlen, int read_from_file)
{
int pos = 0, literal = 0, comment = 0;
char c;
while (1)
{
if (read_from_file)
{
if (! grub_read (&c, 1))
break;
}
else
{
if (! read_from_preset_menu (&c, 1))
break;
}
if (c == '\r')
continue;
if (c == '\t')
c = ' ';
if (literal)
{
if (c == '\n')
{
c = ' ';
if (pos > 0)
pos--;
}
literal = 0;
}
if (c == '\\' && ! literal)
literal = 1;
if (comment)
{
if (c == '\n')
comment = 0;
}
else if (! pos)
{
if (c == '#')
comment = 1;
else if ((c != ' ') && (c != '\n'))
cmdline[pos++] = c;
}
else
{
if (c == '\n')
break;
if (pos < maxlen)
cmdline[pos++] = c;
}
}
cmdline[pos] = 0;
return pos;
}
extern int findroot_func (char *arg, int flags);
void
cmain (void)
{
int config_len, menu_len, num_entries;
char *config_entries, *menu_entries;
char *kill_buf = (char *) KILL_BUF;
silent.status = DEFER_SILENT;
silent.looped = 0;
silent.buffer_start = silent.buffer;
auto void reset (void);
void reset (void)
{
count_lines = -1;
config_len = 0;
menu_len = 0;
num_entries = 0;
config_entries = (char *) mbi.drives_addr + mbi.drives_length;
menu_entries = (char *) MENU_BUF;
init_config ();
}
grub_setjmp (restart_env);
*kill_buf = 0;
for (;;)
{
int is_opened, is_preset;
reset ();
#ifdef GRUB_UTIL
if (use_config_file)
#endif
{
char *default_file = (char *) DEFAULT_FILE_BUF;
int i;
saved_entryno = 0;
grub_strcpy (default_file, config_file);
for (i = grub_strlen(default_file); i >= 0; i--)
if (default_file[i] == '/')
{
i++;
break;
}
default_file[i] = 0;
grub_strncat (default_file + i, "default", DEFAULT_FILE_BUFLEN - i);
if (grub_open (default_file))
{
char buf[10];
char *p = buf;
int len;
len = grub_read (buf, sizeof (buf));
if (len > 0)
{
buf[sizeof (buf) - 1] = 0;
safe_parse_maxint (&p, &saved_entryno);
}
grub_close ();
}
errnum = ERR_NONE;
do
{
int state = 0, prev_config_len = 0, prev_menu_len = 0;
char *cmdline;
is_opened = is_preset = open_preset_menu ();
if (! is_opened)
{
is_opened = grub_open (config_file);
}
if (!is_opened && errnum == ERR_FSYS_MOUNT &&
(findroot_func(config_file, 0) == 0)) {
is_opened = grub_open (config_file);
}
if (! is_opened) {
errnum = ERR_NONE;
break;
}
reset ();
cmdline = (char *) CMDLINE_BUF;
while (get_line_from_config (cmdline, NEW_HEAPSIZE,
! is_preset))
{
struct builtin *builtin;
builtin = find_command (cmdline);
errnum = 0;
if (! builtin)
continue;
if (builtin->flags & BUILTIN_TITLE)
{
char *ptr;
if (state > 1)
{
num_entries++;
config_entries[config_len++] = 0;
prev_menu_len = menu_len;
prev_config_len = config_len;
}
else
{
menu_len = prev_menu_len;
config_len = prev_config_len;
}
state = 1;
ptr = skip_to (1, cmdline);
while ((menu_entries[menu_len++] = *(ptr++)) != 0)
;
}
else if (! state)
{
if (builtin->flags & BUILTIN_MENU)
{
char *arg = skip_to (1, cmdline);
(builtin->func) (arg, BUILTIN_MENU);
errnum = 0;
}
else
continue;
}
else
{
char *ptr = cmdline;
state++;
while ((config_entries[config_len++] = *ptr++) != 0)
;
}
}
if (state > 1)
{
num_entries++;
config_entries[config_len++] = 0;
}
else
{
menu_len = prev_menu_len;
config_len = prev_config_len;
}
menu_entries[menu_len++] = 0;
config_entries[config_len++] = 0;
grub_memmove (config_entries + config_len, menu_entries,
menu_len);
menu_entries = config_entries + config_len;
if (fallback_entryno >= 0)
{
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;
}
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;
}
if (is_preset)
close_preset_menu ();
else
grub_close ();
}
while (is_preset);
}
if (current_term->startup)
(*current_term->startup)();
if (! num_entries)
{
enter_cmdline (config_entries, 1);
}
else
{
run_menu (menu_entries, config_entries, num_entries,
menu_entries + menu_len, default_entry);
}
}
}