mirror of
				https://git.proxmox.com/git/grub2
				synced 2025-10-31 20:57:24 +00:00 
			
		
		
		
	 f725fa7cb2
			
		
	
	
		f725fa7cb2
		
	
	
	
	
		
			
			This modifies most of the places we do some form of:
  X = malloc(Y * Z);
to use calloc(Y, Z) instead.
Among other issues, this fixes:
  - allocation of integer overflow in grub_png_decode_image_header()
    reported by Chris Coulson,
  - allocation of integer overflow in luks_recover_key()
    reported by Chris Coulson,
  - allocation of integer overflow in grub_lvm_detect()
    reported by Chris Coulson.
Fixes: CVE-2020-14308
Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
		
	
			
		
			
				
	
	
		
			603 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			603 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* menu_text.c - Basic text menu implementation.  */
 | |
| /*
 | |
|  *  GRUB  --  GRand Unified Bootloader
 | |
|  *  Copyright (C) 2003,2004,2005,2006,2007,2008,2009  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/loader.h>
 | |
| #include <grub/mm.h>
 | |
| #include <grub/time.h>
 | |
| #include <grub/env.h>
 | |
| #include <grub/menu_viewer.h>
 | |
| #include <grub/i18n.h>
 | |
| #include <grub/charset.h>
 | |
| 
 | |
| static grub_uint8_t grub_color_menu_normal;
 | |
| static grub_uint8_t grub_color_menu_highlight;
 | |
| 
 | |
| struct menu_viewer_data
 | |
| {
 | |
|   int first, offset;
 | |
|   struct grub_term_screen_geometry geo;
 | |
|   enum { 
 | |
|     TIMEOUT_UNKNOWN, 
 | |
|     TIMEOUT_NORMAL,
 | |
|     TIMEOUT_TERSE,
 | |
|     TIMEOUT_TERSE_NO_MARGIN
 | |
|   } timeout_msg;
 | |
|   grub_menu_t menu;
 | |
|   struct grub_term_output *term;
 | |
| };
 | |
| 
 | |
| static inline int
 | |
| grub_term_cursor_x (const struct grub_term_screen_geometry *geo)
 | |
| {
 | |
|   return (geo->first_entry_x + geo->entry_width);
 | |
| }
 | |
| 
 | |
| grub_size_t
 | |
| grub_getstringwidth (grub_uint32_t * str, const grub_uint32_t * last_position,
 | |
| 		     struct grub_term_output *term)
 | |
| {
 | |
|   grub_ssize_t width = 0;
 | |
| 
 | |
|   while (str < last_position)
 | |
|     {
 | |
|       struct grub_unicode_glyph glyph;
 | |
|       glyph.ncomb = 0;
 | |
|       str += grub_unicode_aglomerate_comb (str, last_position - str, &glyph);
 | |
|       width += grub_term_getcharwidth (term, &glyph);
 | |
|       grub_unicode_destroy_glyph (&glyph);
 | |
|     }
 | |
|   return width;
 | |
| }
 | |
| 
 | |
| static int
 | |
| grub_print_message_indented_real (const char *msg, int margin_left,
 | |
| 				  int margin_right,
 | |
| 				  struct grub_term_output *term, int dry_run)
 | |
| {
 | |
|   grub_uint32_t *unicode_msg;
 | |
|   grub_uint32_t *last_position;
 | |
|   grub_size_t msg_len = grub_strlen (msg) + 2;
 | |
|   int ret = 0;
 | |
| 
 | |
|   unicode_msg = grub_calloc (msg_len, sizeof (grub_uint32_t));
 | |
|  
 | |
|   if (!unicode_msg)
 | |
|     return 0;
 | |
| 
 | |
|   msg_len = grub_utf8_to_ucs4 (unicode_msg, msg_len,
 | |
| 			       (grub_uint8_t *) msg, -1, 0);
 | |
|   
 | |
|   last_position = unicode_msg + msg_len;
 | |
|   *last_position = 0;
 | |
| 
 | |
|   if (dry_run)
 | |
|     ret = grub_ucs4_count_lines (unicode_msg, last_position, margin_left,
 | |
| 				 margin_right, term);
 | |
|   else
 | |
|     grub_print_ucs4_menu (unicode_msg, last_position, margin_left,
 | |
| 			  margin_right, term, 0, -1, 0, 0);
 | |
| 
 | |
|   grub_free (unicode_msg);
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| void
 | |
| grub_print_message_indented (const char *msg, int margin_left, int margin_right,
 | |
| 			     struct grub_term_output *term)
 | |
| {
 | |
|   grub_print_message_indented_real (msg, margin_left, margin_right, term, 0);
 | |
| }
 | |
| 
 | |
| static void
 | |
| draw_border (struct grub_term_output *term, const struct grub_term_screen_geometry *geo)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
 | |
| 
 | |
|   grub_term_gotoxy (term, (struct grub_term_coordinate) { geo->first_entry_x - 1,
 | |
| 	geo->first_entry_y - 1 });
 | |
|   grub_putcode (GRUB_UNICODE_CORNER_UL, term);
 | |
|   for (i = 0; i < geo->entry_width + 1; i++)
 | |
|     grub_putcode (GRUB_UNICODE_HLINE, term);
 | |
|   grub_putcode (GRUB_UNICODE_CORNER_UR, term);
 | |
| 
 | |
|   for (i = 0; i < geo->num_entries; i++)
 | |
|     {
 | |
|       grub_term_gotoxy (term, (struct grub_term_coordinate) { geo->first_entry_x - 1,
 | |
| 	    geo->first_entry_y + i });
 | |
|       grub_putcode (GRUB_UNICODE_VLINE, term);
 | |
|       grub_term_gotoxy (term,
 | |
| 			(struct grub_term_coordinate) { geo->first_entry_x + geo->entry_width + 1,
 | |
| 			    geo->first_entry_y + i });
 | |
|       grub_putcode (GRUB_UNICODE_VLINE, term);
 | |
|     }
 | |
| 
 | |
|   grub_term_gotoxy (term,
 | |
| 		    (struct grub_term_coordinate) { geo->first_entry_x - 1,
 | |
| 			geo->first_entry_y - 1 + geo->num_entries + 1 });
 | |
|   grub_putcode (GRUB_UNICODE_CORNER_LL, term);
 | |
|   for (i = 0; i < geo->entry_width + 1; i++)
 | |
|     grub_putcode (GRUB_UNICODE_HLINE, term);
 | |
|   grub_putcode (GRUB_UNICODE_CORNER_LR, term);
 | |
| 
 | |
|   grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
 | |
| 
 | |
|   grub_term_gotoxy (term,
 | |
| 		    (struct grub_term_coordinate) { geo->first_entry_x - 1,
 | |
| 			(geo->first_entry_y - 1 + geo->num_entries
 | |
| 			 + GRUB_TERM_MARGIN + 1) });
 | |
| }
 | |
| 
 | |
| static int
 | |
| print_message (int nested, int edit, struct grub_term_output *term, int dry_run)
 | |
| {
 | |
|   int ret = 0;
 | |
|   grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
 | |
| 
 | |
|   if (edit)
 | |
|     {
 | |
|       ret += grub_print_message_indented_real (_("Minimum Emacs-like screen editing is \
 | |
| supported. TAB lists completions. Press Ctrl-x or F10 to boot, Ctrl-c or F2 for a \
 | |
| command-line or ESC to discard edits and return to the GRUB menu."),
 | |
| 					       STANDARD_MARGIN, STANDARD_MARGIN,
 | |
| 					       term, dry_run);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       char *msg_translated;
 | |
| 
 | |
|       msg_translated = grub_xasprintf (_("Use the %C and %C keys to select which "
 | |
| 					 "entry is highlighted."),
 | |
| 				       GRUB_UNICODE_UPARROW,
 | |
| 				       GRUB_UNICODE_DOWNARROW);
 | |
|       if (!msg_translated)
 | |
| 	return 0;
 | |
|       ret += grub_print_message_indented_real (msg_translated, STANDARD_MARGIN,
 | |
| 					       STANDARD_MARGIN, term, dry_run);
 | |
| 
 | |
|       grub_free (msg_translated);
 | |
| 
 | |
|       if (nested)
 | |
| 	{
 | |
| 	  ret += grub_print_message_indented_real
 | |
| 	    (_("Press enter to boot the selected OS, "
 | |
| 	       "`e' to edit the commands before booting "
 | |
| 	       "or `c' for a command-line. ESC to return previous menu."),
 | |
| 	     STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run);
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  ret += grub_print_message_indented_real
 | |
| 	    (_("Press enter to boot the selected OS, "
 | |
| 	       "`e' to edit the commands before booting "
 | |
| 	       "or `c' for a command-line."),
 | |
| 	     STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run);
 | |
| 	}	
 | |
|     }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static void
 | |
| print_entry (int y, int highlight, grub_menu_entry_t entry,
 | |
| 	     const struct menu_viewer_data *data)
 | |
| {
 | |
|   const char *title;
 | |
|   grub_size_t title_len;
 | |
|   grub_ssize_t len;
 | |
|   grub_uint32_t *unicode_title;
 | |
|   grub_ssize_t i;
 | |
|   grub_uint8_t old_color_normal, old_color_highlight;
 | |
| 
 | |
|   title = entry ? entry->title : "";
 | |
|   title_len = grub_strlen (title);
 | |
|   unicode_title = grub_calloc (title_len, sizeof (*unicode_title));
 | |
|   if (! unicode_title)
 | |
|     /* XXX How to show this error?  */
 | |
|     return;
 | |
| 
 | |
|   len = grub_utf8_to_ucs4 (unicode_title, title_len,
 | |
|                            (grub_uint8_t *) title, -1, 0);
 | |
|   if (len < 0)
 | |
|     {
 | |
|       /* It is an invalid sequence.  */
 | |
|       grub_free (unicode_title);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   old_color_normal = grub_term_normal_color;
 | |
|   old_color_highlight = grub_term_highlight_color;
 | |
|   grub_term_normal_color = grub_color_menu_normal;
 | |
|   grub_term_highlight_color = grub_color_menu_highlight;
 | |
|   grub_term_setcolorstate (data->term, highlight
 | |
| 			   ? GRUB_TERM_COLOR_HIGHLIGHT
 | |
| 			   : GRUB_TERM_COLOR_NORMAL);
 | |
| 
 | |
|   grub_term_gotoxy (data->term, (struct grub_term_coordinate) { 
 | |
|       data->geo.first_entry_x, y });
 | |
| 
 | |
|   for (i = 0; i < len; i++)
 | |
|     if (unicode_title[i] == '\n' || unicode_title[i] == '\b'
 | |
| 	|| unicode_title[i] == '\r' || unicode_title[i] == '\e')
 | |
|       unicode_title[i] = ' ';
 | |
| 
 | |
|   if (data->geo.num_entries > 1)
 | |
|     grub_putcode (highlight ? '*' : ' ', data->term);
 | |
| 
 | |
|   grub_print_ucs4_menu (unicode_title,
 | |
| 			unicode_title + len,
 | |
| 			0,
 | |
| 			data->geo.right_margin,
 | |
| 			data->term, 0, 1,
 | |
| 			GRUB_UNICODE_RIGHTARROW, 0);
 | |
| 
 | |
|   grub_term_setcolorstate (data->term, GRUB_TERM_COLOR_NORMAL);
 | |
|   grub_term_gotoxy (data->term,
 | |
| 		    (struct grub_term_coordinate) { 
 | |
| 		      grub_term_cursor_x (&data->geo), y });
 | |
| 
 | |
|   grub_term_normal_color = old_color_normal;
 | |
|   grub_term_highlight_color = old_color_highlight;
 | |
| 
 | |
|   grub_term_setcolorstate (data->term, GRUB_TERM_COLOR_NORMAL);
 | |
|   grub_free (unicode_title);
 | |
| }
 | |
| 
 | |
| static void
 | |
| print_entries (grub_menu_t menu, const struct menu_viewer_data *data)
 | |
| {
 | |
|   grub_menu_entry_t e;
 | |
|   int i;
 | |
| 
 | |
|   grub_term_gotoxy (data->term,
 | |
| 		    (struct grub_term_coordinate) { 
 | |
| 		      data->geo.first_entry_x + data->geo.entry_width
 | |
| 			+ data->geo.border + 1,
 | |
| 			data->geo.first_entry_y });
 | |
| 
 | |
|   if (data->geo.num_entries != 1)
 | |
|     {
 | |
|       if (data->first)
 | |
| 	grub_putcode (GRUB_UNICODE_UPARROW, data->term);
 | |
|       else
 | |
| 	grub_putcode (' ', data->term);
 | |
|     }
 | |
|   e = grub_menu_get_entry (menu, data->first);
 | |
| 
 | |
|   for (i = 0; i < data->geo.num_entries; i++)
 | |
|     {
 | |
|       print_entry (data->geo.first_entry_y + i, data->offset == i,
 | |
| 		   e, data);
 | |
|       if (e)
 | |
| 	e = e->next;
 | |
|     }
 | |
| 
 | |
|   grub_term_gotoxy (data->term,
 | |
| 		    (struct grub_term_coordinate) { data->geo.first_entry_x + data->geo.entry_width
 | |
| 			+ data->geo.border + 1,
 | |
| 			data->geo.first_entry_y + data->geo.num_entries - 1 });
 | |
|   if (data->geo.num_entries == 1)
 | |
|     {
 | |
|       if (data->first && e)
 | |
| 	grub_putcode (GRUB_UNICODE_UPDOWNARROW, data->term);
 | |
|       else if (data->first)
 | |
| 	grub_putcode (GRUB_UNICODE_UPARROW, data->term);
 | |
|       else if (e)
 | |
| 	grub_putcode (GRUB_UNICODE_DOWNARROW, data->term);
 | |
|       else
 | |
| 	grub_putcode (' ', data->term);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       if (e)
 | |
| 	grub_putcode (GRUB_UNICODE_DOWNARROW, data->term);
 | |
|       else
 | |
| 	grub_putcode (' ', data->term);
 | |
|     }
 | |
| 
 | |
|   grub_term_gotoxy (data->term,
 | |
| 		    (struct grub_term_coordinate) { grub_term_cursor_x (&data->geo),
 | |
| 			data->geo.first_entry_y + data->offset });
 | |
| }
 | |
| 
 | |
| /* Initialize the screen.  If NESTED is non-zero, assume that this menu
 | |
|    is run from another menu or a command-line. If EDIT is non-zero, show
 | |
|    a message for the menu entry editor.  */
 | |
| void
 | |
| grub_menu_init_page (int nested, int edit,
 | |
| 		     struct grub_term_screen_geometry *geo,
 | |
| 		     struct grub_term_output *term)
 | |
| {
 | |
|   grub_uint8_t old_color_normal, old_color_highlight;
 | |
|   int msg_num_lines;
 | |
|   int bottom_message = 1;
 | |
|   int empty_lines = 1;
 | |
|   int version_msg = 1;
 | |
| 
 | |
|   geo->border = 1;
 | |
|   geo->first_entry_x = 1 /* margin */ + 1 /* border */;
 | |
|   geo->entry_width = grub_term_width (term) - 5;
 | |
| 
 | |
|   geo->first_entry_y = 2 /* two empty lines*/
 | |
|     + 1 /* GNU GRUB version text  */ + 1 /* top border */;
 | |
| 
 | |
|   geo->timeout_lines = 2;
 | |
| 
 | |
|   /* 3 lines for timeout message and bottom margin.  2 lines for the border.  */
 | |
|   geo->num_entries = grub_term_height (term) - geo->first_entry_y
 | |
|     - 1 /* bottom border */
 | |
|     - 1 /* empty line before info message*/
 | |
|     - geo->timeout_lines /* timeout */
 | |
|     - 1 /* empty final line  */;
 | |
|   msg_num_lines = print_message (nested, edit, term, 1);
 | |
|   if (geo->num_entries - msg_num_lines < 3
 | |
|       || geo->entry_width < 10)
 | |
|     {
 | |
|       geo->num_entries += 4;
 | |
|       geo->first_entry_y -= 2;
 | |
|       empty_lines = 0;
 | |
|       geo->first_entry_x -= 1;
 | |
|       geo->entry_width += 1;
 | |
|     }
 | |
|   if (geo->num_entries - msg_num_lines < 3
 | |
|       || geo->entry_width < 10)
 | |
|     {
 | |
|       geo->num_entries += 2;
 | |
|       geo->first_entry_y -= 1;
 | |
|       geo->first_entry_x -= 1;
 | |
|       geo->entry_width += 2;
 | |
|       geo->border = 0;
 | |
|     }
 | |
| 
 | |
|   if (geo->entry_width <= 0)
 | |
|     geo->entry_width = 1;
 | |
| 
 | |
|   if (geo->num_entries - msg_num_lines < 3
 | |
|       && geo->timeout_lines == 2)
 | |
|     {
 | |
|       geo->timeout_lines = 1;
 | |
|       geo->num_entries++;
 | |
|     }
 | |
| 
 | |
|   if (geo->num_entries - msg_num_lines < 3)
 | |
|     {
 | |
|       geo->num_entries += 1;
 | |
|       geo->first_entry_y -= 1;
 | |
|       version_msg = 0;
 | |
|     }
 | |
| 
 | |
|   if (geo->num_entries - msg_num_lines >= 2)
 | |
|     geo->num_entries -= msg_num_lines;
 | |
|   else
 | |
|     bottom_message = 0;
 | |
| 
 | |
|   /* By default, use the same colors for the menu.  */
 | |
|   old_color_normal = grub_term_normal_color;
 | |
|   old_color_highlight = grub_term_highlight_color;
 | |
|   grub_color_menu_normal = grub_term_normal_color;
 | |
|   grub_color_menu_highlight = grub_term_highlight_color;
 | |
| 
 | |
|   /* Then give user a chance to replace them.  */
 | |
|   grub_parse_color_name_pair (&grub_color_menu_normal,
 | |
| 			      grub_env_get ("menu_color_normal"));
 | |
|   grub_parse_color_name_pair (&grub_color_menu_highlight,
 | |
| 			      grub_env_get ("menu_color_highlight"));
 | |
| 
 | |
|   if (version_msg)
 | |
|     grub_normal_init_page (term, empty_lines);
 | |
|   else
 | |
|     grub_term_cls (term);
 | |
| 
 | |
|   grub_term_normal_color = grub_color_menu_normal;
 | |
|   grub_term_highlight_color = grub_color_menu_highlight;
 | |
|   if (geo->border)
 | |
|     draw_border (term, geo);
 | |
|   grub_term_normal_color = old_color_normal;
 | |
|   grub_term_highlight_color = old_color_highlight;
 | |
|   geo->timeout_y = geo->first_entry_y + geo->num_entries
 | |
|     + geo->border + empty_lines;
 | |
|   if (bottom_message)
 | |
|     {
 | |
|       grub_term_gotoxy (term,
 | |
| 			(struct grub_term_coordinate) { GRUB_TERM_MARGIN,
 | |
| 			    geo->timeout_y });
 | |
| 
 | |
|       print_message (nested, edit, term, 0);
 | |
|       geo->timeout_y += msg_num_lines;
 | |
|     }
 | |
|   geo->right_margin = grub_term_width (term)
 | |
|     - geo->first_entry_x
 | |
|     - geo->entry_width - 1;
 | |
| }
 | |
| 
 | |
| static void
 | |
| menu_text_print_timeout (int timeout, void *dataptr)
 | |
| {
 | |
|   struct menu_viewer_data *data = dataptr;
 | |
|   char *msg_translated = 0;
 | |
| 
 | |
|   grub_term_gotoxy (data->term,
 | |
| 		    (struct grub_term_coordinate) { 0, data->geo.timeout_y });
 | |
| 
 | |
|   if (data->timeout_msg == TIMEOUT_TERSE
 | |
|       || data->timeout_msg == TIMEOUT_TERSE_NO_MARGIN)
 | |
|     msg_translated = grub_xasprintf (_("%ds"), timeout);
 | |
|   else
 | |
|     msg_translated = grub_xasprintf (_("The highlighted entry will be executed automatically in %ds."), timeout);
 | |
|   if (!msg_translated)
 | |
|     {
 | |
|       grub_print_error ();
 | |
|       grub_errno = GRUB_ERR_NONE;
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   if (data->timeout_msg == TIMEOUT_UNKNOWN)
 | |
|     {
 | |
|       data->timeout_msg = grub_print_message_indented_real (msg_translated,
 | |
| 							    3, 1, data->term, 1)
 | |
| 	<= data->geo.timeout_lines ? TIMEOUT_NORMAL : TIMEOUT_TERSE;
 | |
|       if (data->timeout_msg == TIMEOUT_TERSE)
 | |
| 	{
 | |
| 	  grub_free (msg_translated);
 | |
| 	  msg_translated = grub_xasprintf (_("%ds"), timeout);
 | |
| 	  if (grub_term_width (data->term) < 10)
 | |
| 	    data->timeout_msg = TIMEOUT_TERSE_NO_MARGIN;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   grub_print_message_indented (msg_translated,
 | |
| 			       data->timeout_msg == TIMEOUT_TERSE_NO_MARGIN ? 0 : 3,
 | |
| 			       data->timeout_msg == TIMEOUT_TERSE_NO_MARGIN ? 0 : 1,
 | |
| 			       data->term);
 | |
|   grub_free (msg_translated);
 | |
| 
 | |
|   grub_term_gotoxy (data->term,
 | |
| 		    (struct grub_term_coordinate) { 
 | |
| 		      grub_term_cursor_x (&data->geo),
 | |
| 			data->geo.first_entry_y + data->offset });
 | |
|   grub_term_refresh (data->term);
 | |
| }
 | |
| 
 | |
| static void
 | |
| menu_text_set_chosen_entry (int entry, void *dataptr)
 | |
| {
 | |
|   struct menu_viewer_data *data = dataptr;
 | |
|   int oldoffset = data->offset;
 | |
|   int complete_redraw = 0;
 | |
| 
 | |
|   data->offset = entry - data->first;
 | |
|   if (data->offset > data->geo.num_entries - 1)
 | |
|     {
 | |
|       data->first = entry - (data->geo.num_entries - 1);
 | |
|       data->offset = data->geo.num_entries - 1;
 | |
|       complete_redraw = 1;
 | |
|     }
 | |
|   if (data->offset < 0)
 | |
|     {
 | |
|       data->offset = 0;
 | |
|       data->first = entry;
 | |
|       complete_redraw = 1;
 | |
|     }
 | |
|   if (complete_redraw)
 | |
|     print_entries (data->menu, data);
 | |
|   else
 | |
|     {
 | |
|       print_entry (data->geo.first_entry_y + oldoffset, 0,
 | |
| 		   grub_menu_get_entry (data->menu, data->first + oldoffset),
 | |
| 		   data);
 | |
|       print_entry (data->geo.first_entry_y + data->offset, 1,
 | |
| 		   grub_menu_get_entry (data->menu, data->first + data->offset),
 | |
| 		   data);
 | |
|     }
 | |
|   grub_term_refresh (data->term);
 | |
| }
 | |
| 
 | |
| static void
 | |
| menu_text_fini (void *dataptr)
 | |
| {
 | |
|   struct menu_viewer_data *data = dataptr;
 | |
| 
 | |
|   grub_term_setcursor (data->term, 1);
 | |
|   grub_term_cls (data->term);
 | |
|   grub_free (data);
 | |
| }
 | |
| 
 | |
| static void
 | |
| menu_text_clear_timeout (void *dataptr)
 | |
| {
 | |
|   struct menu_viewer_data *data = dataptr;
 | |
|   int i;
 | |
| 
 | |
|   for (i = 0; i < data->geo.timeout_lines;i++)
 | |
|     {
 | |
|       grub_term_gotoxy (data->term, (struct grub_term_coordinate) {
 | |
| 	  0, data->geo.timeout_y + i });
 | |
|       grub_print_spaces (data->term, grub_term_width (data->term) - 1);
 | |
|     }
 | |
|   if (data->geo.num_entries <= 5 && !data->geo.border)
 | |
|     {
 | |
|       grub_term_gotoxy (data->term,
 | |
| 			(struct grub_term_coordinate) { 
 | |
| 			  data->geo.first_entry_x + data->geo.entry_width
 | |
| 			    + data->geo.border + 1,
 | |
| 			    data->geo.first_entry_y + data->geo.num_entries - 1
 | |
| 			    });
 | |
|       grub_putcode (' ', data->term);
 | |
| 
 | |
|       data->geo.timeout_lines = 0;
 | |
|       data->geo.num_entries++;
 | |
|       print_entries (data->menu, data);
 | |
|     }
 | |
|   grub_term_gotoxy (data->term,
 | |
| 		    (struct grub_term_coordinate) {
 | |
| 		      grub_term_cursor_x (&data->geo),
 | |
| 			data->geo.first_entry_y + data->offset });
 | |
|   grub_term_refresh (data->term);
 | |
| }
 | |
| 
 | |
| grub_err_t 
 | |
| grub_menu_try_text (struct grub_term_output *term, 
 | |
| 		    int entry, grub_menu_t menu, int nested)
 | |
| {
 | |
|   struct menu_viewer_data *data;
 | |
|   struct grub_menu_viewer *instance;
 | |
| 
 | |
|   instance = grub_zalloc (sizeof (*instance));
 | |
|   if (!instance)
 | |
|     return grub_errno;
 | |
| 
 | |
|   data = grub_zalloc (sizeof (*data));
 | |
|   if (!data)
 | |
|     {
 | |
|       grub_free (instance);
 | |
|       return grub_errno;
 | |
|     }
 | |
| 
 | |
|   data->term = term;
 | |
|   instance->data = data;
 | |
|   instance->set_chosen_entry = menu_text_set_chosen_entry;
 | |
|   instance->print_timeout = menu_text_print_timeout;
 | |
|   instance->clear_timeout = menu_text_clear_timeout;
 | |
|   instance->fini = menu_text_fini;
 | |
| 
 | |
|   data->menu = menu;
 | |
| 
 | |
|   data->offset = entry;
 | |
|   data->first = 0;
 | |
| 
 | |
|   grub_term_setcursor (data->term, 0);
 | |
|   grub_menu_init_page (nested, 0, &data->geo, data->term);
 | |
| 
 | |
|   if (data->offset > data->geo.num_entries - 1)
 | |
|     {
 | |
|       data->first = data->offset - (data->geo.num_entries - 1);
 | |
|       data->offset = data->geo.num_entries - 1;
 | |
|     }
 | |
| 
 | |
|   print_entries (menu, data);
 | |
|   grub_term_refresh (data->term);
 | |
|   grub_menu_register_viewer (instance);
 | |
| 
 | |
|   return GRUB_ERR_NONE;
 | |
| }
 |