grub/normal/menu_entry.c
okuji 4d4e372ebd 2005-02-19 Yoshinori K. Okuji <okuji@enbug.org>
This implements an Emacs-like menu entry editor.

  * normal/menu_entry.c: New file.

  * util/console.c (grub_ncurses_putchar): Translate some Unicode
  characters to ASCII.
  (saved_char): New variable.
  (grub_ncurses_checkkey): Rewritten completely.
  (grub_ncurses_getkey): Likewise.
  (grub_ncurses_init): Call raw instead of cbreak.

  * normal/menu.c (print_entry): Do not put a space.
  (init_page): Renamed to ...
  (grub_menu_init_page): ... this. All callers changed.
  (edit_menu_entry): Removed.
  (run_menu): Call grub_menu_entry_run instead of edit_menu_entry.

  * normal/cmdline.c (grub_cmdline_run): Call grub_setcursor.

  * kern/misc.c (grub_vprintf): Call grub_refresh.

  * normal/menu.c (DISP_LEFT): Renamed to ...
  * include/grub/term.h (GRUB_TERM_DISP_LEFT): ... this.
  * normal/menu.c (DISP_UP): Renamed to ...
  * include/grub/term.h (GRUB_TERM_DISP_UP): ... this.
  * normal/menu.c (DISP_RIGHT): Renamed to ...
  * include/grub/term.h (GRUB_TERM_DISP_RIGHT): ... this.
  * normal/menu.c (DISP_DOWN): Renamed to ...
  * include/grub/term.h (GRUB_TERM_DISP_DOWN): ... this.
  * normal/menu.c (DISP_HLINE): Renamed to ...
  * include/grub/term.h (GRUB_TERM_DISP_HLINE): ... this.
  * normal/menu.c (DISP_VLINE): Renamed to ...
  * include/grub/term.h (GRUB_TERM_DISP_VLINE): ... this.
  * normal/menu.c (DISP_UL): Renamed to ...
  * include/grub/term.h (GRUB_TERM_DISP_UL): ... this.
  * normal/menu.c (DISP_UR): Renamed to ...
  * include/grub/term.h (GRUB_TERM_DISP_UR): ... this.
  * normal/menu.c (DISP_LL): Renamed to ...
  * include/grub/term.h (GRUB_TERM_DISP_LL): ... this.
  * normal/menu.c (DISP_LR): Renamed to ...
  * include/grub/term.h (GRUB_TERM_DISP_LR): ... this.
  * normal/menu.c (TERM_WIDTH): Renamed to ...
  * include/grub/term.h (GRUB_TERM_WIDTH): ... this.
  * normal/menu.c (TERM_HEIGHT): Renamed to ...
  * include/grub/term.h (GRUB_TERM_HEIGHT): ... this.
  * normal/menu.c (TERM_INFO_HEIGHT): Renamed to ...
  * include/grub/term.h (GRUB_TERM_INFO_HEIGHT): ... this.
  * normal/menu.c (TERM_MARGIN): Renamed to ...
  * include/grub/term.h (GRUB_TERM_MARGIN): ... this.
  * normal/menu.c (TERM_SCROLL_WIDTH): Renamed to ...
  * include/grub/term.h (GRUB_TERM_SCROLL_WIDTH): ... this.
  * normal/menu.c (TERM_TOP_BORDER_Y): Renamed to ...
  * include/grub/term.h (GRUB_TERM_TOP_BORDER_Y): ... this.
  * normal/menu.c (TERM_LEFT_BORDER_X): Renamed to ...
  * include/grub/term.h (GRUB_TERM_LEFT_BORDER_X): ... this.
  * normal/menu.c (TERM_BORDER_WIDTH): Renamed to ...
  * include/grub/term.h (GRUB_TERM_BORDER_WIDTH): ... this.
  * normal/menu.c (TERM_MESSAGE_HEIGHT): Renamed to ...
  * include/grub/term.h (GRUB_TERM_MESSAGE_HEIGHT): ... this.
  * normal/menu.c (TERM_BORDER_HEIGHT): Renamed to ...
  * include/grub/term.h (GRUB_TERM_BORDER_HEIGHT): ... this.
  * normal/menu.c (TERM_NUM_ENTRIES): Renamed to ...
  * include/grub/term.h (GRUB_TERM_NUM_ENTRIES): ... this.
  * normal/menu.c (TERM_FIRST_ENTRY_Y): Renamed to ...
  * include/grub/term.h (GRUB_TERM_FIRST_ENTRY_Y): ... this.
  * normal/menu.c (TERM_ENTRY_WIDTH): Renamed to ...
  * include/grub/term.h (GRUB_TERM_ENTRY_WIDTH): ... this.
  * normal/menu.c (TERM_CURSOR_X): Renamed to ...
  * include/grub/term.h (GRUB_TERM_CURSOR_X): ... this.
  All callers changed.

  * include/grub/normal.h: New prototype.

  * conf/i386-pc.rmk (grub_emu_SOURCES): Added
  normal/menu_entry.c.
  (normal_mod_SOURCES): Likewise.
  * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise.
  (normal_mod_SOURCES): Likewise.

2005-02-15  Yoshinori K. Okuji  <okuji@enbug.org>

  * include/grub/normal.h (grub_halt_init): New prototype.
  (grub_halt_fini): Likewise.
  (grub_reboot_init): Likewise.
  (grub_reboot_fini): Likewise.

  * util/grub-emu.c: Include signal.h.
  (main_env): New global variable.
  (grub_machine_init): Ignore SIGINT. Otherwise grub-emu cannot
  catch C-c.
  (grub_machine_fini): New function.
  (main): Call grub_halt_init and grub_reboot_init before
  grub_main, and grub_reboot_fini and grub_halt_fini after it.
  Call setjmp with MAIN_ENV to go back afterwards.
  Call grub_machine_fini right before return.

  * include/grub/util/misc.h: Include setjmp.h.
  (main_env): New prototype.

  * include/grub/kernel.h (grub_machine_fini): New prototype.
  * include/grub/i386/pc/biosdisk.h (grub_biosdisk_fini): Likewise.
  * include/grub/i386/pc/console.h (grub_console_fini): Likewise.

  * disk/i386/pc/biosdisk.c (grub_biosdisk_fini): New function.
  * kern/i386/pc/init.c (grub_machine_fini): Likewise.
  * term/i386/pc/console.c (grub_console_fini): Likewise.

  * util/i386/pc/misc.c: New file.

  * conf/i386-pc.rmk (grub_emu_SOURCES): Added
  util/i386/pc/misc.c, commands/i386/pc/halt.c and
  commands/i386/pc/reboot.c.
2005-02-19 20:56:07 +00:00

1016 lines
21 KiB
C

/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 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 <grub/normal.h>
#include <grub/term.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/loader.h>
enum update_mode
{
NO_LINE,
SINGLE_LINE,
ALL_LINES
};
struct line
{
/* The line buffer. */
char *buf;
/* The length of the line. */
int len;
/* The maximum length of the line. */
int max_len;
};
struct screen
{
/* The array of lines. */
struct line *lines;
/* The number of lines. */
int num_lines;
/* The current column. */
int column;
/* The real column. */
int real_column;
/* The current line. */
int line;
/* The X coordinate. */
int x;
/* The Y coordinate. */
int y;
/* The kill buffer. */
char *killed_text;
};
/* Initialize a line. */
static int
init_line (struct line *linep)
{
linep->len = 0;
linep->max_len = 80; /* XXX */
linep->buf = grub_malloc (linep->max_len);
if (! linep->buf)
return 0;
return 1;
}
/* Allocate extra space if necessary. */
static int
ensure_space (struct line *linep, int extra)
{
if (linep->max_len < linep->len + extra)
{
linep->max_len = linep->len + extra + 80; /* XXX */
linep->buf = grub_realloc (linep->buf, linep->max_len + 1);
if (! linep->buf)
return 0;
}
return 1;
}
/* Return the number of lines occupied by this line on the screen. */
static int
get_logical_num_lines (struct line *linep)
{
return (linep->len / GRUB_TERM_ENTRY_WIDTH) + 1;
}
/* Print a line. */
static void
print_line (struct line *linep, int offset, int start, int y)
{
int i;
char *p;
grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + start + 1,
y + GRUB_TERM_FIRST_ENTRY_Y);
for (p = linep->buf + offset + start, i = start;
i < GRUB_TERM_ENTRY_WIDTH && offset + i < linep->len;
p++, i++)
grub_putchar (*p);
for (; i < GRUB_TERM_ENTRY_WIDTH; i++)
grub_putchar (' ');
if (linep->len >= offset + GRUB_TERM_ENTRY_WIDTH)
grub_putchar ('\\');
else
grub_putchar (' ');
}
/* Print an empty line. */
static void
print_empty_line (int y)
{
int i;
grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1,
y + GRUB_TERM_FIRST_ENTRY_Y);
for (i = 0; i < GRUB_TERM_ENTRY_WIDTH + 1; i++)
grub_putchar (' ');
}
/* Print an up arrow. */
static void
print_up (int flag)
{
grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH,
GRUB_TERM_FIRST_ENTRY_Y);
if (flag)
grub_putcode (GRUB_TERM_DISP_UP);
else
grub_putchar (' ');
}
/* Print a down arrow. */
static void
print_down (int flag)
{
grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH,
GRUB_TERM_TOP_BORDER_Y + GRUB_TERM_NUM_ENTRIES);
if (flag)
grub_putcode (GRUB_TERM_DISP_DOWN);
else
grub_putchar (' ');
}
/* Draw the lines of the screen SCREEN. */
static void
update_screen (struct screen *screen, int region_start, int region_column,
int up, int down, enum update_mode mode)
{
int up_flag = 0;
int down_flag = 0;
int y;
int i;
struct line *linep;
/* Check if scrolling is necessary. */
if (screen->y < 0 || screen->y >= GRUB_TERM_NUM_ENTRIES)
{
if (screen->y < 0)
screen->y = 0;
else
screen->y = GRUB_TERM_NUM_ENTRIES - 1;
region_start = 0;
region_column = 0;
up = 1;
down = 1;
mode = ALL_LINES;
}
if (mode != NO_LINE)
{
/* Draw lines. This code is tricky, because this must calculate logical
positions. */
y = screen->y - screen->column / GRUB_TERM_ENTRY_WIDTH;
i = screen->line;
linep = screen->lines + i;
while (y > 0)
{
i--;
linep--;
y -= get_logical_num_lines (linep);
}
if (y < 0 || i > 0)
up_flag = 1;
do
{
int column;
for (column = 0;
column <= linep->len && y < GRUB_TERM_NUM_ENTRIES;
column += GRUB_TERM_ENTRY_WIDTH, y++)
{
if (y < 0)
continue;
if (i == region_start)
{
if (region_column >= column
&& region_column < column + GRUB_TERM_ENTRY_WIDTH)
print_line (linep, column, region_column - column, y);
else if (region_column < column)
print_line (linep, column, 0, y);
}
else if (i > region_start && mode == ALL_LINES)
print_line (linep, column, 0, y);
}
if (y == GRUB_TERM_NUM_ENTRIES)
{
if (column <= linep->len || i + 1 < screen->num_lines)
down_flag = 1;
}
linep++;
i++;
if (mode == ALL_LINES && i == screen->num_lines)
for (; y < GRUB_TERM_NUM_ENTRIES; y++)
print_empty_line (y);
}
while (y < GRUB_TERM_NUM_ENTRIES);
/* Draw up and down arrows. */
if (up)
print_up (up_flag);
if (down)
print_down (down_flag);
}
/* Place the cursor. */
grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1 + screen->x,
GRUB_TERM_FIRST_ENTRY_Y + screen->y);
grub_refresh ();
}
/* Insert the string S into the screen SCREEN. This updates the cursor
position and redraw the screen. Return zero if fails. */
static int
insert_string (struct screen *screen, char *s, int update)
{
int region_start = screen->num_lines;
int region_column = 0;
int down = 0;
enum update_mode mode = NO_LINE;
while (*s)
{
if (*s == '\n')
{
/* LF is special because it creates a new line. */
struct line *current_linep;
struct line *next_linep;
int size;
/* Make a new line. */
screen->num_lines++;
screen->lines = grub_realloc (screen->lines,
screen->num_lines
* sizeof (struct line));
if (! screen->lines)
return 0;
/* Scroll down. */
grub_memmove (screen->lines + screen->line + 2,
screen->lines + screen->line + 1,
((screen->num_lines - screen->line - 2)
* sizeof (struct line)));
if (! init_line (screen->lines + screen->line + 1))
return 0;
/* Fold the line. */
current_linep = screen->lines + screen->line;
next_linep = current_linep + 1;
size = current_linep->len - screen->column;
if (! ensure_space (next_linep, size))
return 0;
grub_memmove (next_linep->buf,
current_linep->buf + screen->column,
size);
current_linep->len = screen->column;
next_linep->len = size;
/* Update a dirty region. */
if (region_start > screen->line)
{
region_start = screen->line;
region_column = screen->column;
}
mode = ALL_LINES;
down = 1; /* XXX not optimal. */
/* Move the cursor. */
screen->column = screen->real_column = 0;
screen->line++;
screen->x = 0;
screen->y++;
s++;
}
else
{
/* All but LF. */
char *p;
struct line *current_linep;
int size;
int orig_num, new_num;
/* Find a string delimitted by LF. */
p = grub_strchr (s, '\n');
if (! p)
p = s + grub_strlen (s);
/* Insert the string. */
current_linep = screen->lines + screen->line;
size = p - s;
if (! ensure_space (current_linep, size))
return 0;
grub_memmove (current_linep->buf + screen->column + size,
current_linep->buf + screen->column,
current_linep->len - screen->column);
grub_memmove (current_linep->buf + screen->column,
s,
size);
orig_num = get_logical_num_lines (current_linep);
current_linep->len += size;
new_num = get_logical_num_lines (current_linep);
/* Update the dirty region. */
if (region_start > screen->line)
{
region_start = screen->line;
region_column = screen->column;
}
if (orig_num != new_num)
{
mode = ALL_LINES;
down = 1; /* XXX not optimal. */
}
else if (mode != ALL_LINES)
mode = SINGLE_LINE;
/* Move the cursor. */
screen->column += size;
screen->real_column = screen->column;
screen->x += size;
screen->y += screen->x / GRUB_TERM_ENTRY_WIDTH;
screen->x %= GRUB_TERM_ENTRY_WIDTH;
s = p;
}
}
if (update)
update_screen (screen, region_start, region_column, 0, down, mode);
return 1;
}
/* Release the resource allocated for SCREEN. */
static void
destroy_screen (struct screen *screen)
{
int i;
if (screen->lines)
for (i = 0; i < screen->num_lines; i++)
{
struct line *linep = screen->lines + i;
if (linep)
grub_free (linep->buf);
}
grub_free (screen->killed_text);
grub_free (screen->lines);
grub_free (screen);
}
/* Make a new screen. */
static struct screen *
make_screen (grub_menu_entry_t entry)
{
struct screen *screen;
grub_command_list_t cl;
/* Initialize the screen. */
screen = grub_malloc (sizeof (*screen));
if (! screen)
return 0;
screen->num_lines = 1;
screen->column = 0;
screen->real_column = 0;
screen->line = 0;
screen->x = 0;
screen->y = 0;
screen->killed_text = 0;
screen->lines = grub_malloc (sizeof (struct line));
if (! screen->lines)
goto fail;
/* Initialize the first line which must be always present. */
if (! init_line (screen->lines))
goto fail;
/* Input the entry. */
for (cl = entry->command_list; cl; cl = cl->next)
{
if (! insert_string (screen, cl->command, 0))
goto fail;
if (! insert_string (screen, "\n", 0))
goto fail;
}
/* Reset the cursor position. */
screen->column = 0;
screen->real_column = 0;
screen->line = 0;
screen->x = 0;
screen->y = 0;
return screen;
fail:
destroy_screen (screen);
return 0;
}
static int
forward_char (struct screen *screen, int update)
{
struct line *linep;
linep = screen->lines + screen->line;
if (screen->column < linep->len)
{
screen->column++;
screen->x++;
if (screen->x == GRUB_TERM_ENTRY_WIDTH)
{
screen->x = 0;
screen->y++;
}
}
else if (screen->num_lines > screen->line + 1)
{
screen->column = 0;
screen->line++;
screen->x = 0;
screen->y++;
}
screen->real_column = screen->column;
if (update)
update_screen (screen, screen->num_lines, 0, 0, 0, NO_LINE);
return 1;
}
static int
backward_char (struct screen *screen, int update)
{
if (screen->column > 0)
{
screen->column--;
screen->x--;
if (screen->x == -1)
{
screen->x = GRUB_TERM_ENTRY_WIDTH - 1;
screen->y--;
}
}
else if (screen->line > 0)
{
struct line *linep;
screen->line--;
linep = screen->lines + screen->line;
screen->column = linep->len;
screen->x = screen->column % GRUB_TERM_ENTRY_WIDTH;
screen->y--;
}
screen->real_column = screen->column;
if (update)
update_screen (screen, screen->num_lines, 0, 0, 0, NO_LINE);
return 1;
}
static int
previous_line (struct screen *screen, int update)
{
if (screen->line > 0)
{
struct line *linep;
int dy;
/* How many physical lines from the current position
to the first physical line? */
dy = screen->column / GRUB_TERM_ENTRY_WIDTH;
screen->line--;
linep = screen->lines + screen->line;
if (linep->len < screen->real_column)
screen->column = linep->len;
else
screen->column = screen->real_column;
/* How many physical lines from the current position
to the last physical line? */
dy += (linep->len / GRUB_TERM_ENTRY_WIDTH
- screen->column / GRUB_TERM_ENTRY_WIDTH);
screen->y -= dy + 1;
screen->x = screen->column % GRUB_TERM_ENTRY_WIDTH;
}
else
{
screen->y -= screen->column / GRUB_TERM_ENTRY_WIDTH;
screen->column = 0;
screen->x = 0;
}
if (update)
update_screen (screen, screen->num_lines, 0, 0, 0, NO_LINE);
return 1;
}
static int
next_line (struct screen *screen, int update)
{
if (screen->line < screen->num_lines - 1)
{
struct line *linep;
int dy;
/* How many physical lines from the current position
to the last physical line? */
linep = screen->lines + screen->line;
dy = (linep->len / GRUB_TERM_ENTRY_WIDTH
- screen->column / GRUB_TERM_ENTRY_WIDTH);
screen->line++;
linep++;
if (linep->len < screen->real_column)
screen->column = linep->len;
else
screen->column = screen->real_column;
/* How many physical lines from the current position
to the first physical line? */
dy += screen->column / GRUB_TERM_ENTRY_WIDTH;
screen->y += dy + 1;
screen->x = screen->column % GRUB_TERM_ENTRY_WIDTH;
}
else
{
struct line *linep;
linep = screen->lines + screen->line;
screen->y += (linep->len / GRUB_TERM_ENTRY_WIDTH
- screen->column / GRUB_TERM_ENTRY_WIDTH);
screen->column = linep->len;
screen->x = screen->column % GRUB_TERM_ENTRY_WIDTH;
}
if (update)
update_screen (screen, screen->num_lines, 0, 0, 0, NO_LINE);
return 1;
}
static int
beginning_of_line (struct screen *screen, int update)
{
screen->y -= screen->column / GRUB_TERM_ENTRY_WIDTH;
screen->column = screen->real_column = 0;
screen->x = 0;
if (update)
update_screen (screen, screen->num_lines, 0, 0, 0, NO_LINE);
return 1;
}
static int
end_of_line (struct screen *screen, int update)
{
struct line *linep;
linep = screen->lines + screen->line;
screen->y += (linep->len / GRUB_TERM_ENTRY_WIDTH
- screen->column / GRUB_TERM_ENTRY_WIDTH);
screen->column = screen->real_column = linep->len;
screen->x = screen->column % GRUB_TERM_ENTRY_WIDTH;
if (update)
update_screen (screen, screen->num_lines, 0, 0, 0, NO_LINE);
return 1;
}
static int
delete_char (struct screen *screen, int update)
{
struct line *linep;
enum update_mode mode = NO_LINE;
int start = screen->num_lines;
int column = 0;
int down = 0;
linep = screen->lines + screen->line;
if (linep->len > screen->column)
{
int orig_num, new_num;
orig_num = get_logical_num_lines (linep);
grub_memmove (linep->buf + screen->column,
linep->buf + screen->column + 1,
linep->len - screen->column - 1);
linep->len--;
new_num = get_logical_num_lines (linep);
if (orig_num != new_num)
mode = ALL_LINES;
else
mode = SINGLE_LINE;
start = screen->line;
column = screen->column;
}
else if (screen->num_lines > screen->line + 1)
{
struct line *next_linep;
next_linep = linep + 1;
if (! ensure_space (linep, next_linep->len))
return 0;
grub_memmove (linep->buf + linep->len, next_linep->buf, next_linep->len);
linep->len += next_linep->len;
grub_free (next_linep->buf);
grub_memmove (next_linep,
next_linep + 1,
(screen->num_lines - screen->line - 2)
* sizeof (struct line));
screen->num_lines--;
mode = ALL_LINES;
start = screen->line;
column = screen->column;
down = 1;
}
screen->real_column = screen->column;
if (update)
update_screen (screen, start, column, 0, down, mode);
return 1;
}
static int
backward_delete_char (struct screen *screen, int update)
{
int saved_column;
int saved_line;
saved_column = screen->column;
saved_line = screen->line;
if (! backward_char (screen, 0))
return 0;
if (saved_column != screen->column || saved_line != screen->line)
if (! delete_char (screen, update))
return 0;
return 1;
}
static int
kill_line (struct screen *screen, int continuous, int update)
{
struct line *linep;
char *p;
int size;
int offset;
p = screen->killed_text;
if (! continuous && p)
p[0] = '\0';
linep = screen->lines + screen->line;
size = linep->len - screen->column;
if (p)
offset = grub_strlen (p);
else
offset = 0;
if (size > 0)
{
enum update_mode mode = SINGLE_LINE;
int down = 0;
int orig_num, new_num;
p = grub_realloc (p, offset + size + 1);
if (! p)
return 0;
grub_memmove (p + offset, linep->buf + screen->column, size);
p[offset + size - 1] = '\0';
screen->killed_text = p;
orig_num = get_logical_num_lines (linep);
linep->len = screen->column;
new_num = get_logical_num_lines (linep);
if (orig_num != new_num)
{
mode = ALL_LINES;
down = 1;
}
if (update)
update_screen (screen, screen->line, screen->column, 0, down, mode);
}
else if (screen->line + 1 < screen->num_lines)
{
p = grub_realloc (p, offset + 1 + 1);
if (! p)
return 0;
p[offset] = '\n';
p[offset + 1] = '\0';
screen->killed_text = p;
return delete_char (screen, update);
}
return 1;
}
static int
yank (struct screen *screen, int update)
{
if (screen->killed_text)
return insert_string (screen, screen->killed_text, update);
return 1;
}
static int
open_line (struct screen *screen, int update)
{
int saved_y = screen->y;
if (! insert_string (screen, "\n", 0))
return 0;
if (! backward_char (screen, 0))
return 0;
screen->y = saved_y;
if (update)
update_screen (screen, screen->line, screen->column, 0, 1, ALL_LINES);
return 1;
}
/* Execute the command list in the screen SCREEN. */
static int
run (struct screen *screen)
{
int i;
grub_cls ();
grub_printf (" Booting a command list\n\n");
for (i = 0; i < screen->num_lines; i++)
{
struct line *linep = screen->lines + i;
char *p;
grub_command_t c;
/* Trim down space characters. */
for (p = linep->buf + linep->len - 1;
p >= linep->buf && grub_isspace (*p);
p--)
;
*++p = '\0';
linep->len = p - linep->buf;
for (p = linep->buf; grub_isspace (*p); p++)
;
if (*p == '\0')
/* Ignore an empty command line. */
continue;
c = grub_command_find (p);
if (! c)
break;
if (! (c->flags & GRUB_COMMAND_FLAG_CMDLINE))
{
grub_error (GRUB_ERR_INVALID_COMMAND, "invalid command `%s'", p);
break;
}
if (! (c->flags & GRUB_COMMAND_FLAG_NO_ECHO))
grub_printf ("%s\n", p);
if (grub_command_execute (p) != 0)
break;
}
if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
/* Implicit execution of boot, only if something is loaded. */
grub_command_execute ("boot");
if (grub_errno != GRUB_ERR_NONE)
{
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
/* Wait until the user pushes any key so that the user
can see what happened. */
grub_printf ("\nPress any key to continue...");
(void) grub_getkey ();
}
return 1;
}
/* Edit a menu entry with an Emacs-like interface. */
void
grub_menu_entry_run (grub_menu_entry_t entry)
{
struct screen *screen;
int prev_c;
screen = make_screen (entry);
if (! screen)
return;
refresh:
/* Draw the screen. */
grub_menu_init_page (0, 1);
update_screen (screen, 0, 0, 1, 1, ALL_LINES);
grub_setcursor (1);
prev_c = '\0';
while (1)
{
int c = GRUB_TERM_ASCII_CHAR (grub_getkey ());
switch (c)
{
case 16: /* C-p */
if (! previous_line (screen, 1))
goto fail;
break;
case 14: /* C-n */
if (! next_line (screen, 1))
goto fail;
break;
case 6: /* C-f */
if (! forward_char (screen, 1))
goto fail;
break;
case 2: /* C-b */
if (! backward_char (screen, 1))
goto fail;
break;
case 1: /* C-a */
if (! beginning_of_line (screen, 1))
goto fail;
break;
case 5: /* C-e */
if (! end_of_line (screen, 1))
goto fail;
break;
case '\t': /* C-i */
/* FIXME: Completion */
break;
case 4: /* C-d */
if (! delete_char (screen, 1))
goto fail;
break;
case 8: /* C-h */
if (! backward_delete_char (screen, 1))
goto fail;
break;
case 11: /* C-k */
if (! kill_line (screen, prev_c == c, 1))
goto fail;
break;
case 21: /* C-u */
/* FIXME: What behavior is good for this key? */
break;
case 25: /* C-y */
if (! yank (screen, 1))
goto fail;
break;
case 12: /* C-l */
/* FIXME: centering. */
goto refresh;
case 15: /* C-o */
if (! open_line (screen, 1))
goto fail;
break;
case '\n':
case '\r':
if (! insert_string (screen, "\n", 1))
goto fail;
break;
case '\e':
destroy_screen (screen);
return;
case 3: /* C-c */
grub_cmdline_run (1);
goto refresh;
case 24: /* C-x */
if (! run (screen))
goto fail;
goto refresh;
case 18: /* C-r */
case 19: /* C-s */
case 20: /* C-t */
/* FIXME */
break;
default:
if (grub_isprint (c))
{
char buf[2];
buf[0] = c;
buf[1] = '\0';
if (! insert_string (screen, buf, 1))
goto fail;
}
break;
}
prev_c = c;
}
fail:
destroy_screen (screen);
grub_cls ();
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
grub_printf ("\nPress any key to continue...");
(void) grub_getkey ();
}