| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 2005,2006,2007,2008 Free Software Foundation, Inc. |
| * |
| * GRUB 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 3 of the License, or |
| * (at your option) any later version. |
| * |
| * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <grub/normal.h> |
| #include <grub/term.h> |
| #include <grub/misc.h> |
| #include <grub/mm.h> |
| #include <grub/loader.h> |
| #include <grub/command.h> |
| #include <grub/parser.h> |
| #include <grub/auth.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; |
| /* The flag of a completion window. */ |
| int completion_shown; |
| }; |
| |
| /* Used for storing completion items temporarily. */ |
| static struct line completion_buffer; |
| |
| /* 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 delimited 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; |
| |
| /* Initialize the screen. */ |
| screen = grub_zalloc (sizeof (*screen)); |
| if (! screen) |
| return 0; |
| |
| screen->num_lines = 1; |
| 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; |
| |
| insert_string (screen, (char *) entry->sourcecode, 0); |
| |
| /* 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; |
| } |
| |
| /* A completion hook to print items. */ |
| static void |
| store_completion (const char *item, grub_completion_type_t type, int count) |
| { |
| char *p; |
| |
| if (count == 0) |
| { |
| /* If this is the first time, print a label. */ |
| const char *what; |
| |
| switch (type) |
| { |
| case GRUB_COMPLETION_TYPE_COMMAND: |
| what = "commands"; |
| break; |
| case GRUB_COMPLETION_TYPE_DEVICE: |
| what = "devices"; |
| break; |
| case GRUB_COMPLETION_TYPE_FILE: |
| what = "files"; |
| break; |
| case GRUB_COMPLETION_TYPE_PARTITION: |
| what = "partitions"; |
| break; |
| case GRUB_COMPLETION_TYPE_ARGUMENT: |
| what = "arguments"; |
| break; |
| default: |
| what = "things"; |
| break; |
| } |
| |
| grub_gotoxy (0, GRUB_TERM_HEIGHT - 3); |
| grub_printf (" Possible %s are:\n ", what); |
| } |
| |
| /* Make sure that the completion buffer has enough room. */ |
| if (completion_buffer.max_len < (completion_buffer.len |
| + (int) grub_strlen (item) + 1 + 1)) |
| { |
| grub_size_t new_len; |
| |
| new_len = completion_buffer.len + grub_strlen (item) + 80; |
| p = grub_realloc (completion_buffer.buf, new_len); |
| if (! p) |
| { |
| /* Possibly not fatal. */ |
| grub_errno = GRUB_ERR_NONE; |
| return; |
| } |
| p[completion_buffer.len] = 0; |
| completion_buffer.buf = p; |
| completion_buffer.max_len = new_len; |
| } |
| |
| p = completion_buffer.buf + completion_buffer.len; |
| if (completion_buffer.len != 0) |
| { |
| *p++ = ' '; |
| completion_buffer.len++; |
| } |
| grub_strcpy (p, item); |
| completion_buffer.len += grub_strlen (item); |
| } |
| |
| static int |
| complete (struct screen *screen, int continuous, int update) |
| { |
| grub_uint16_t pos; |
| char saved_char; |
| struct line *linep; |
| int restore; |
| char *insert; |
| static int count = -1; |
| |
| if (continuous) |
| count++; |
| else |
| count = 0; |
| |
| pos = grub_getxy (); |
| grub_gotoxy (0, GRUB_TERM_HEIGHT - 3); |
| |
| completion_buffer.buf = 0; |
| completion_buffer.len = 0; |
| completion_buffer.max_len = 0; |
| |
| linep = screen->lines + screen->line; |
| saved_char = linep->buf[screen->column]; |
| linep->buf[screen->column] = '\0'; |
| |
| insert = grub_normal_do_completion (linep->buf, &restore, store_completion); |
| |
| linep->buf[screen->column] = saved_char; |
| |
| if (restore) |
| { |
| char *p = completion_buffer.buf; |
| |
| screen->completion_shown = 1; |
| |
| if (p) |
| { |
| int num_sections = ((completion_buffer.len + GRUB_TERM_WIDTH - 8 - 1) |
| / (GRUB_TERM_WIDTH - 8)); |
| char *endp; |
| |
| p += (count % num_sections) * (GRUB_TERM_WIDTH - 8); |
| endp = p + (GRUB_TERM_WIDTH - 8); |
| |
| if (p != completion_buffer.buf) |
| grub_putcode (GRUB_TERM_DISP_LEFT); |
| else |
| grub_putchar (' '); |
| |
| while (*p && p < endp) |
| grub_putchar (*p++); |
| |
| if (*p) |
| grub_putcode (GRUB_TERM_DISP_RIGHT); |
| } |
| } |
| |
| grub_gotoxy (pos >> 8, pos & 0xFF); |
| |
| if (insert) |
| { |
| insert_string (screen, insert, update); |
| count = -1; |
| grub_free (insert); |
| } |
| else if (update) |
| grub_refresh (); |
| |
| grub_free (completion_buffer.buf); |
| return 1; |
| } |
| |
| /* Clear displayed completions. */ |
| static void |
| clear_completions (void) |
| { |
| grub_uint16_t pos; |
| int i, j; |
| |
| pos = grub_getxy (); |
| grub_gotoxy (0, GRUB_TERM_HEIGHT - 3); |
| |
| for (i = 0; i < 2; i++) |
| { |
| for (j = 0; j < GRUB_TERM_WIDTH - 1; j++) |
| grub_putchar (' '); |
| grub_putchar ('\n'); |
| } |
| |
| grub_gotoxy (pos >> 8, pos & 0xFF); |
| grub_refresh (); |
| } |
| |
| /* Execute the command list in the screen SCREEN. */ |
| static int |
| run (struct screen *screen) |
| { |
| int currline = 0; |
| char *nextline; |
| |
| auto grub_err_t editor_getline (char **line, int cont); |
| grub_err_t editor_getline (char **line, int cont __attribute__ ((unused))) |
| { |
| struct line *linep = screen->lines + currline; |
| char *p; |
| |
| if (currline > screen->num_lines) |
| { |
| *line = 0; |
| return 0; |
| } |
| |
| /* 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++) |
| ; |
| *line = grub_strdup (p); |
| currline++; |
| return 0; |
| } |
| |
| grub_cls (); |
| grub_printf (" Booting a command list\n\n"); |
| |
| |
| /* Execute the script, line for line. */ |
| while (currline < screen->num_lines) |
| { |
| editor_getline (&nextline, 0); |
| if (grub_parser_get_current ()->parse_line (nextline, editor_getline)) |
| break; |
| } |
| |
| if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ()) |
| /* Implicit execution of boot, only if something is loaded. */ |
| grub_command_execute ("boot", 0, 0); |
| |
| if (grub_errno != GRUB_ERR_NONE) |
| { |
| grub_print_error (); |
| grub_errno = GRUB_ERR_NONE; |
| grub_wait_after_message (); |
| } |
| |
| 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; |
| grub_err_t err = GRUB_ERR_NONE; |
| |
| err = grub_auth_check_authentication (NULL); |
| |
| if (err) |
| { |
| grub_print_error (); |
| grub_errno = GRUB_ERR_NONE; |
| return; |
| } |
| |
| 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 ()); |
| |
| if (screen->completion_shown) |
| { |
| clear_completions (); |
| screen->completion_shown = 0; |
| } |
| |
| 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 */ |
| if (! complete (screen, prev_c == c, 1)) |
| goto fail; |
| 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 (); |
| } |