mirror of
				https://git.proxmox.com/git/grub2
				synced 2025-10-31 13:55:51 +00:00 
			
		
		
		
	 3f05d693d1
			
		
	
	
		3f05d693d1
		
	
	
	
	
		
			
			This attempts to fix the places where we do the following where
arithmetic_expr may include unvalidated data:
  X = grub_malloc(arithmetic_expr);
It accomplishes this by doing the arithmetic ahead of time using grub_add(),
grub_sub(), grub_mul() and testing for overflow before proceeding.
Among other issues, this fixes:
  - allocation of integer overflow in grub_video_bitmap_create()
    reported by Chris Coulson,
  - allocation of integer overflow in grub_png_decode_image_header()
    reported by Chris Coulson,
  - allocation of integer overflow in grub_squash_read_symlink()
    reported by Chris Coulson,
  - allocation of integer overflow in grub_ext2_read_symlink()
    reported by Chris Coulson,
  - allocation of integer overflow in read_section_as_string()
    reported by Chris Coulson.
Fixes: CVE-2020-14309, CVE-2020-14310, CVE-2020-14311
Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
		
	
			
		
			
				
	
	
		
			1461 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1461 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *  GRUB  --  GRand Unified Bootloader
 | |
|  *  Copyright (C) 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/mm.h>
 | |
| #include <grub/loader.h>
 | |
| #include <grub/command.h>
 | |
| #include <grub/parser.h>
 | |
| #include <grub/script_sh.h>
 | |
| #include <grub/auth.h>
 | |
| #include <grub/i18n.h>
 | |
| #include <grub/charset.h>
 | |
| #include <grub/safemath.h>
 | |
| 
 | |
| enum update_mode
 | |
|   {
 | |
|     NO_LINE,
 | |
|     SINGLE_LINE,
 | |
|     ALL_LINES
 | |
|   };
 | |
| 
 | |
| struct line
 | |
| {
 | |
|   /* The line buffer.  */
 | |
|   grub_uint32_t *buf;
 | |
|   /* The length of the line.  */
 | |
|   int len;
 | |
|   /* The maximum length of the line.  */
 | |
|   int max_len;
 | |
|   struct grub_term_pos **pos;
 | |
| };
 | |
| 
 | |
| struct per_term_screen
 | |
| {
 | |
|   struct grub_term_output *term;
 | |
|   int y_line_start;
 | |
|   struct grub_term_screen_geometry geo;
 | |
|   /* Scratch variables used when updating. Having them here avoids
 | |
|      loads of small mallocs.  */
 | |
|   int orig_num;
 | |
|   int down;
 | |
|   enum update_mode mode;
 | |
| };
 | |
| 
 | |
| 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 kill buffer.  */
 | |
|   char *killed_text;
 | |
|   /* The flag of a completion window.  */
 | |
|   int completion_shown;
 | |
| 
 | |
|   int submenu;
 | |
| 
 | |
|   struct per_term_screen *terms;
 | |
|   unsigned nterms;
 | |
| };
 | |
| 
 | |
| /* Used for storing completion items temporarily.  */
 | |
| static struct {
 | |
|   char *buf;
 | |
|   grub_size_t len;
 | |
|   grub_size_t max_len;
 | |
| } completion_buffer;
 | |
| static int completion_type;
 | |
| 
 | |
| /* Initialize a line.  */
 | |
| static int
 | |
| init_line (struct screen *screen, struct line *linep)
 | |
| {
 | |
|   linep->len = 0;
 | |
|   linep->max_len = 80;
 | |
|   linep->buf = grub_calloc (linep->max_len + 1, sizeof (linep->buf[0]));
 | |
|   linep->pos = grub_calloc (screen->nterms, sizeof (linep->pos[0]));
 | |
|   if (! linep->buf || !linep->pos)
 | |
|     {
 | |
|       grub_free (linep->buf);
 | |
|       grub_free (linep->pos);
 | |
|       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)
 | |
|     {
 | |
|       grub_size_t sz0, sz1;
 | |
| 
 | |
|       if (grub_add (linep->len, extra, &sz0) ||
 | |
| 	  grub_mul (sz0, 2, &sz0) ||
 | |
| 	  grub_add (sz0, 1, &sz1) ||
 | |
| 	  grub_mul (sz1, sizeof (linep->buf[0]), &sz1))
 | |
| 	return 0;
 | |
| 
 | |
|       linep->buf = grub_realloc (linep->buf, sz1);
 | |
|       if (! linep->buf)
 | |
| 	return 0;
 | |
|       linep->max_len = sz0;
 | |
|     }
 | |
| 
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| /* Return the number of lines occupied by this line on the screen.  */
 | |
| static int
 | |
| get_logical_num_lines (struct line *linep, struct per_term_screen *term_screen)
 | |
| {
 | |
|   grub_size_t width = grub_getstringwidth (linep->buf, linep->buf + linep->len,
 | |
| 					   term_screen->term);
 | |
| 
 | |
|   /* Empty line still consumes space on screen */
 | |
|   return width ? (width + (unsigned) term_screen->geo.entry_width - 1) /
 | |
| 		 (unsigned) term_screen->geo.entry_width
 | |
| 	       : 1;
 | |
| }
 | |
| 
 | |
| static void
 | |
| advance_to (struct screen *screen, int c)
 | |
| {
 | |
|   if (c > screen->lines[screen->line].len)
 | |
|     c = screen->lines[screen->line].len;
 | |
| 
 | |
|   screen->column = grub_unicode_get_comb_end (screen->lines[screen->line].buf
 | |
| 					      + screen->lines[screen->line].len,
 | |
| 					      screen->lines[screen->line].buf
 | |
| 					      + c)
 | |
|     - screen->lines[screen->line].buf;
 | |
| }
 | |
| 
 | |
| /* Print an empty line.  */
 | |
| static void
 | |
| print_empty_line (int y, struct per_term_screen *term_screen)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   grub_term_gotoxy (term_screen->term,
 | |
| 		    (struct grub_term_coordinate) { term_screen->geo.first_entry_x,
 | |
| 			y + term_screen->geo.first_entry_y });
 | |
| 
 | |
|   for (i = 0; i < term_screen->geo.entry_width + 1; i++)
 | |
|     grub_putcode (' ', term_screen->term);
 | |
| }
 | |
| 
 | |
| static void
 | |
| print_updown (int upflag, int downflag, struct per_term_screen *term_screen)
 | |
| {
 | |
|   grub_term_gotoxy (term_screen->term,
 | |
| 		    (struct grub_term_coordinate) { term_screen->geo.first_entry_x
 | |
| 			+ term_screen->geo.entry_width + 1
 | |
| 			+ term_screen->geo.border,
 | |
| 			term_screen->geo.first_entry_y });
 | |
| 
 | |
|   if (upflag && downflag)
 | |
|     grub_putcode (GRUB_UNICODE_UPDOWNARROW, term_screen->term);
 | |
|   else if (upflag)
 | |
|     grub_putcode (GRUB_UNICODE_UPARROW, term_screen->term);
 | |
|   else if (downflag)
 | |
|     grub_putcode (GRUB_UNICODE_DOWNARROW, term_screen->term);
 | |
|   else
 | |
|     grub_putcode (' ', term_screen->term);
 | |
| }
 | |
| 
 | |
| /* Print an up arrow.  */
 | |
| static void
 | |
| print_up (int flag, struct per_term_screen *term_screen)
 | |
| {
 | |
|   grub_term_gotoxy (term_screen->term,
 | |
| 		    (struct grub_term_coordinate) { term_screen->geo.first_entry_x
 | |
| 			+ term_screen->geo.entry_width + 1
 | |
| 			+ term_screen->geo.border,
 | |
| 			term_screen->geo.first_entry_y });
 | |
| 
 | |
|   if (flag)
 | |
|     grub_putcode (GRUB_UNICODE_UPARROW, term_screen->term);
 | |
|   else
 | |
|     grub_putcode (' ', term_screen->term);
 | |
| }
 | |
| 
 | |
| /* Print a down arrow.  */
 | |
| static void
 | |
| print_down (int flag, struct per_term_screen *term_screen)
 | |
| {
 | |
|   grub_term_gotoxy (term_screen->term,
 | |
| 		    (struct grub_term_coordinate) { term_screen->geo.first_entry_x
 | |
| 			+ term_screen->geo.entry_width + 1
 | |
| 			+ term_screen->geo.border,
 | |
| 			term_screen->geo.first_entry_y
 | |
| 			+ term_screen->geo.num_entries - 1 });
 | |
| 
 | |
|   if (flag)
 | |
|     grub_putcode (GRUB_UNICODE_DOWNARROW, term_screen->term);
 | |
|   else
 | |
|     grub_putcode (' ', term_screen->term);
 | |
| }
 | |
| 
 | |
| /* Draw the lines of the screen SCREEN.  */
 | |
| static void
 | |
| update_screen (struct screen *screen, struct per_term_screen *term_screen,
 | |
| 	       int region_start, int region_column __attribute__ ((unused)),
 | |
| 	       int up, int down, enum update_mode mode)
 | |
| {
 | |
|   int up_flag = 0;
 | |
|   int down_flag = 0;
 | |
|   int y;
 | |
|   int i;
 | |
|   struct line *linep;
 | |
| 
 | |
|   y = term_screen->y_line_start;
 | |
|   linep = screen->lines;
 | |
| 
 | |
|   for (i = 0; i < screen->line; i++, linep++)
 | |
|     y += get_logical_num_lines (linep, term_screen);
 | |
|   linep = screen->lines + screen->line;
 | |
|   grub_size_t t = grub_getstringwidth (linep->buf, linep->buf + screen->column,
 | |
| 				       term_screen->term);
 | |
|   y += t / (unsigned) term_screen->geo.entry_width;
 | |
|   if (t % (unsigned) term_screen->geo.entry_width == 0
 | |
|       && t != 0 &&  screen->column == linep->len)
 | |
|     y--;
 | |
|   /* Check if scrolling is necessary.  */
 | |
|   if (y < 0 || y >= term_screen->geo.num_entries)
 | |
|     {
 | |
|       int delta;
 | |
|       if (y < 0)
 | |
| 	delta = -y;
 | |
|       else
 | |
| 	delta = term_screen->geo.num_entries - 1 - y;
 | |
|       term_screen->y_line_start += delta;
 | |
| 
 | |
|       region_start = 0;
 | |
|       up = 1;
 | |
|       down = 1;
 | |
|       mode = ALL_LINES;
 | |
|     }
 | |
| 
 | |
|   grub_term_setcursor (term_screen->term, 0);
 | |
| 
 | |
|   if (mode != NO_LINE)
 | |
|     {
 | |
|       /* Draw lines. This code is tricky, because this must calculate logical
 | |
| 	 positions.  */
 | |
|       y = term_screen->y_line_start;
 | |
|       i = 0;
 | |
|       linep = screen->lines;
 | |
|       while (1)
 | |
|  	{
 | |
| 	  int add;
 | |
| 	  add = get_logical_num_lines (linep, term_screen);
 | |
| 	  if (y + add > 0)
 | |
| 	    break;
 | |
| 	  i++;
 | |
| 	  linep++;
 | |
| 	  y += add;
 | |
|  	}
 | |
| 
 | |
|       if (y < 0 || i > 0)
 | |
| 	up_flag = 1;
 | |
| 
 | |
|       do
 | |
| 	{
 | |
| 	  struct grub_term_pos **pos;
 | |
| 
 | |
| 	  if (linep >= screen->lines + screen->num_lines)
 | |
| 	    break;
 | |
| 
 | |
| 	  pos = linep->pos + (term_screen - screen->terms);
 | |
| 
 | |
| 	  if (!*pos)
 | |
| 	    *pos = grub_calloc (linep->len + 1, sizeof (**pos));
 | |
| 
 | |
| 	  if (i == region_start || linep == screen->lines + screen->line
 | |
| 	      || (i > region_start && mode == ALL_LINES))
 | |
| 	    {
 | |
| 	      grub_term_gotoxy (term_screen->term,
 | |
| 				(struct grub_term_coordinate) { term_screen->geo.first_entry_x,
 | |
| 				    (y < 0 ? 0 : y)
 | |
| 				    + term_screen->geo.first_entry_y });
 | |
| 
 | |
| 	      grub_print_ucs4_menu (linep->buf,
 | |
| 				    linep->buf + linep->len,
 | |
| 				    term_screen->geo.first_entry_x,
 | |
| 				    term_screen->geo.right_margin,
 | |
| 				    term_screen->term,
 | |
| 				    (y < 0) ? -y : 0,
 | |
| 				    term_screen->geo.num_entries
 | |
| 				    - ((y > 0) ? y : 0), '\\',
 | |
| 				    *pos);
 | |
| 	    }
 | |
| 	  y += get_logical_num_lines (linep, term_screen);
 | |
| 	  if (y >= term_screen->geo.num_entries)
 | |
| 	    {
 | |
| 	      if (i + 1 < screen->num_lines)
 | |
| 		down_flag = 1;
 | |
| 	    }
 | |
| 
 | |
| 	  linep++;
 | |
| 	  i++;
 | |
| 
 | |
| 	  if (mode == ALL_LINES && i == screen->num_lines)
 | |
| 	    for (; y < term_screen->geo.num_entries; y++)
 | |
| 	      print_empty_line (y, term_screen);
 | |
| 	}
 | |
|       while (y < term_screen->geo.num_entries);
 | |
| 
 | |
|       /* Draw up and down arrows.  */
 | |
|       
 | |
|       if (term_screen->geo.num_entries == 1)
 | |
| 	{
 | |
| 	  if (up || down)
 | |
| 	    print_updown (up_flag, down_flag, term_screen);
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  if (up)
 | |
| 	    print_up (up_flag, term_screen);
 | |
| 	  if (down)
 | |
| 	    print_down (down_flag, term_screen);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* Place the cursor.  */
 | |
|   if (screen->lines[screen->line].pos[term_screen - screen->terms])
 | |
|     {
 | |
|       const struct grub_term_pos *cpos;
 | |
|       for (cpos = &(screen->lines[screen->line].pos[term_screen - screen->terms])[screen->column];
 | |
| 	   cpos >= &(screen->lines[screen->line].pos[term_screen - screen->terms])[0];
 | |
| 	   cpos--)
 | |
| 	if (cpos->valid)
 | |
| 	  break;
 | |
|       y = term_screen->y_line_start;
 | |
|       for (i = 0; i < screen->line; i++)
 | |
| 	y += get_logical_num_lines (screen->lines + i, term_screen);
 | |
|       if (cpos >= &(screen->lines[screen->line].pos[term_screen - screen->terms])[0])
 | |
| 	grub_term_gotoxy (term_screen->term, 
 | |
| 			  (struct grub_term_coordinate) { cpos->x + term_screen->geo.first_entry_x,
 | |
| 			      cpos->y + y
 | |
| 			      + term_screen->geo.first_entry_y });
 | |
|       else
 | |
| 	grub_term_gotoxy (term_screen->term, 
 | |
| 			  (struct grub_term_coordinate) { term_screen->geo.first_entry_x,
 | |
| 			      y + term_screen->geo.first_entry_y });
 | |
| 
 | |
|     }
 | |
| 
 | |
|   grub_term_setcursor (term_screen->term, 1);
 | |
| 
 | |
|   grub_term_refresh (term_screen->term);
 | |
| }
 | |
| 
 | |
| static void
 | |
| update_screen_all (struct screen *screen,
 | |
| 		   int region_start, int region_column,
 | |
| 		   int up, int down, enum update_mode mode)
 | |
| {
 | |
|   unsigned i;
 | |
|   for (i = 0; i < screen->nterms; i++)
 | |
|     update_screen (screen, &screen->terms[i], region_start, region_column,
 | |
| 		   up, down, mode);
 | |
| }
 | |
| 
 | |
| static int
 | |
| insert_string (struct screen *screen, const char *s, int update)
 | |
| {
 | |
|   int region_start = screen->num_lines;
 | |
|   int region_column = 0;
 | |
|   unsigned i;
 | |
| 
 | |
|   for (i = 0; i < screen->nterms; i++)
 | |
|     {
 | |
|       screen->terms[i].down = 0;
 | |
|       screen->terms[i].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 (screen->lines[0]));
 | |
| 	  if (! screen->lines)
 | |
| 	    return 0;
 | |
| 
 | |
| 	  /* Shift down if not appending after the last line. */
 | |
| 	  if (screen->line < screen->num_lines - 2)
 | |
| 	    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, 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 * sizeof (next_linep->buf[0]));
 | |
| 	  current_linep->len = screen->column;
 | |
| 	  for (i = 0; i < screen->nterms; i++)
 | |
| 	    {
 | |
| 	      grub_free (current_linep->pos[i]);
 | |
| 	      current_linep->pos[i] = 0;
 | |
| 	    }
 | |
| 	  next_linep->len = size;
 | |
| 
 | |
| 	  /* Update a dirty region.  */
 | |
| 	  if (region_start > screen->line)
 | |
| 	    {
 | |
| 	      region_start = screen->line;
 | |
| 	      region_column = screen->column;
 | |
| 	    }
 | |
| 
 | |
| 	  for (i = 0; i < screen->nterms; i++)
 | |
| 	    {
 | |
| 	      screen->terms[i].mode = ALL_LINES;
 | |
| 	      screen->terms[i].down = 1; /* XXX not optimal.  */
 | |
| 	    }
 | |
| 
 | |
| 	  /* Move the cursor.  */
 | |
| 	  screen->column = screen->real_column = 0;
 | |
| 	  screen->line++;
 | |
| 	  s++;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  /* All but LF.  */
 | |
| 	  const char *p;
 | |
| 	  struct line *current_linep;
 | |
| 	  int size;
 | |
| 	  grub_uint32_t *unicode_msg;
 | |
| 
 | |
| 	  /* 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;
 | |
| 	  unicode_msg = grub_calloc (p - s, sizeof (grub_uint32_t));
 | |
| 
 | |
| 	  if (!unicode_msg)
 | |
| 	    return 0;
 | |
| 
 | |
| 	  size = grub_utf8_to_ucs4 (unicode_msg, (p - s),
 | |
| 				    (grub_uint8_t *) s, (p - s), 0);
 | |
| 
 | |
| 	  if (! ensure_space (current_linep, size))
 | |
| 	    {
 | |
| 	      grub_free (unicode_msg);
 | |
| 	      return 0;
 | |
| 	    }
 | |
| 
 | |
| 	  grub_memmove (current_linep->buf + screen->column + size,
 | |
| 			current_linep->buf + screen->column,
 | |
| 			(current_linep->len - screen->column)
 | |
| 			* sizeof (current_linep->buf[0]));
 | |
| 	  grub_memmove (current_linep->buf + screen->column,
 | |
| 			unicode_msg,
 | |
| 			size * sizeof (current_linep->buf[0]));
 | |
| 	  grub_free (unicode_msg);
 | |
| 
 | |
| 	  for (i = 0; i < screen->nterms; i++)
 | |
| 	    {
 | |
| 	      grub_free (current_linep->pos[i]);
 | |
| 	      current_linep->pos[i] = 0;
 | |
| 	    }
 | |
| 
 | |
| 	  for (i = 0; i < screen->nterms; i++)
 | |
| 	    screen->terms[i].orig_num = get_logical_num_lines (current_linep,
 | |
| 						 &screen->terms[i]);
 | |
| 	  current_linep->len += size;
 | |
| 
 | |
| 	  /* Update the dirty region.  */
 | |
| 	  if (region_start > screen->line)
 | |
| 	    {
 | |
| 	      region_start = screen->line;
 | |
| 	      region_column = screen->column;
 | |
| 	    }
 | |
| 
 | |
| 	  for (i = 0; i < screen->nterms; i++)
 | |
| 	    {
 | |
| 	      int new_num = get_logical_num_lines (current_linep,
 | |
| 						   &screen->terms[i]);
 | |
| 	      if (screen->terms[i].orig_num != new_num)
 | |
| 		{
 | |
| 		  screen->terms[i].mode = ALL_LINES;
 | |
| 		  screen->terms[i].down = 1; /* XXX not optimal.  */
 | |
| 		}
 | |
| 	      else if (screen->terms[i].mode != ALL_LINES)
 | |
| 		screen->terms[i].mode = SINGLE_LINE;
 | |
| 	    }
 | |
| 
 | |
| 	  /* Move the cursor.  */
 | |
| 	  advance_to (screen, screen->column + size);
 | |
| 
 | |
| 	  screen->real_column = screen->column;
 | |
| 	  s = p;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (update)
 | |
|     for (i = 0; i < screen->nterms; i++)
 | |
|       update_screen (screen, &screen->terms[i],
 | |
| 		     region_start, region_column, 0, screen->terms[i].down,
 | |
| 		     screen->terms[i].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)
 | |
| 	  {
 | |
| 	    unsigned j;
 | |
| 	    if (linep->pos)
 | |
| 	      for (j = 0; j < screen->nterms; j++)
 | |
| 		grub_free (linep->pos[j]);
 | |
| 
 | |
| 	    grub_free (linep->buf);
 | |
| 	    grub_free (linep->pos);
 | |
| 	  }
 | |
|       }
 | |
| 
 | |
|   grub_free (screen->killed_text);
 | |
|   grub_free (screen->lines);
 | |
|   grub_free (screen->terms);
 | |
|   grub_free (screen);
 | |
| }
 | |
| 
 | |
| /* Make a new screen.  */
 | |
| static struct screen *
 | |
| make_screen (grub_menu_entry_t entry)
 | |
| {
 | |
|   struct screen *screen;
 | |
|   unsigned i;
 | |
| 
 | |
|   /* Initialize the screen.  */
 | |
|   screen = grub_zalloc (sizeof (*screen));
 | |
|   if (! screen)
 | |
|     return 0;
 | |
| 
 | |
|   screen->submenu = entry->submenu;
 | |
| 
 | |
|   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, 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;
 | |
|   for (i = 0; i < screen->nterms; i++)
 | |
|     {
 | |
|       screen->terms[i].y_line_start = 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 = grub_unicode_get_comb_end (screen->lines[screen->line].buf
 | |
| 						  + screen->lines[screen->line].len,
 | |
| 						  screen->lines[screen->line].buf
 | |
| 						  + screen->column + 1)
 | |
| 	- screen->lines[screen->line].buf;
 | |
|     }
 | |
|   else if (screen->num_lines > screen->line + 1)
 | |
|     {
 | |
|       screen->column = 0;
 | |
|       screen->line++;
 | |
|     }
 | |
| 
 | |
|   screen->real_column = screen->column;
 | |
| 
 | |
|   if (update)
 | |
|     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| backward_char (struct screen *screen, int update)
 | |
| {
 | |
|   if (screen->column > 0)
 | |
|     {
 | |
|       struct grub_unicode_glyph glyph;
 | |
|       struct line *linep;
 | |
| 
 | |
|       linep = screen->lines + screen->line;
 | |
| 
 | |
|       screen->column--;
 | |
|       screen->column = grub_unicode_get_comb_start (linep->buf, 
 | |
| 						    linep->buf + screen->column)
 | |
| 	- linep->buf;
 | |
| 
 | |
|       grub_unicode_aglomerate_comb (screen->lines[screen->line].buf + screen->column,
 | |
| 				    screen->lines[screen->line].len - screen->column,
 | |
| 				    &glyph);
 | |
|       screen->column = grub_unicode_get_comb_start (linep->buf, 
 | |
| 						    linep->buf + screen->column)
 | |
| 	- linep->buf;
 | |
| 
 | |
|       grub_unicode_destroy_glyph (&glyph);
 | |
|     }
 | |
|   else if (screen->line > 0)
 | |
|     {
 | |
|       screen->line--;
 | |
|       screen->column = screen->lines[screen->line].len;
 | |
|     }
 | |
| 
 | |
|   screen->real_column = screen->column;
 | |
| 
 | |
|   if (update)
 | |
|     update_screen_all (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 col;
 | |
| 
 | |
|       screen->line--;
 | |
| 
 | |
|       linep = screen->lines + screen->line;
 | |
|       if (linep->len < screen->real_column)
 | |
| 	col = linep->len;
 | |
|       else
 | |
| 	col = screen->real_column;
 | |
| 
 | |
|       screen->column = 0;
 | |
|       advance_to (screen, col);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       screen->column = 0;
 | |
|     }
 | |
| 
 | |
|   if (update)
 | |
|     update_screen_all (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 c;
 | |
| 
 | |
|       /* How many physical lines from the current position
 | |
| 	 to the last physical line?  */
 | |
|       linep = screen->lines + screen->line;
 | |
| 
 | |
|       screen->line++;
 | |
|       if ((linep + 1)->len < screen->real_column)
 | |
| 	c = (linep + 1)->len;
 | |
|       else
 | |
| 	c = screen->real_column;
 | |
|       screen->column = 0;
 | |
| 
 | |
|       advance_to (screen, c);
 | |
|     }
 | |
|   else
 | |
|     advance_to (screen, screen->lines[screen->line].len);
 | |
| 
 | |
|   if (update)
 | |
|     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
 | |
| 
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| beginning_of_line (struct screen *screen, int update)
 | |
| {
 | |
|   screen->column = screen->real_column = 0;
 | |
| 
 | |
|   if (update)
 | |
|     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
 | |
| 
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| end_of_line (struct screen *screen, int update)
 | |
| {
 | |
|   advance_to (screen, screen->lines[screen->line].len);
 | |
| 
 | |
|   if (update)
 | |
|     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
 | |
| 
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| delete_char (struct screen *screen, int update)
 | |
| {
 | |
|   struct line *linep;
 | |
|   int start = screen->num_lines;
 | |
|   int column = 0;
 | |
| 
 | |
|   linep = screen->lines + screen->line;
 | |
|   if (linep->len > screen->column)
 | |
|     {
 | |
|       unsigned i;
 | |
| 
 | |
|       for (i = 0; i < screen->nterms; i++)
 | |
| 	screen->terms[i].orig_num = get_logical_num_lines (linep, &screen->terms[i]);
 | |
| 
 | |
|       grub_memmove (linep->buf + screen->column,
 | |
| 		    linep->buf + screen->column + 1,
 | |
| 		    (linep->len - screen->column - 1)
 | |
| 		    * sizeof (linep->buf[0]));
 | |
|       linep->len--;
 | |
| 
 | |
|       for (i = 0; i < screen->nterms; i++)
 | |
| 	{
 | |
| 	  grub_free (linep->pos[i]);
 | |
| 	  linep->pos[i] = 0;
 | |
| 	}
 | |
|       start = screen->line;
 | |
|       column = screen->column;
 | |
| 
 | |
|       screen->real_column = screen->column;
 | |
| 
 | |
|       if (update)
 | |
| 	{
 | |
| 	  for (i = 0; i < screen->nterms; i++)
 | |
| 	    {
 | |
| 	      int new_num;
 | |
| 	      new_num = get_logical_num_lines (linep, &screen->terms[i]);
 | |
| 	      if (screen->terms[i].orig_num != new_num)
 | |
| 		update_screen (screen, &screen->terms[i],
 | |
| 			       start, column, 0, 0, ALL_LINES);
 | |
| 	      else
 | |
| 		update_screen (screen, &screen->terms[i],
 | |
| 			       start, column, 0, 0, SINGLE_LINE);
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|   else if (screen->num_lines > screen->line + 1)
 | |
|     {
 | |
|       struct line *next_linep;
 | |
|       unsigned i;
 | |
| 
 | |
|       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 * sizeof (linep->buf[0]));
 | |
|       linep->len += next_linep->len;
 | |
|       for (i = 0; i < screen->nterms; i++)
 | |
| 	{
 | |
| 	  grub_free (linep->pos[i]);
 | |
| 	  linep->pos[i] = 0;
 | |
| 	}
 | |
| 
 | |
|       grub_free (next_linep->buf);
 | |
|       grub_memmove (next_linep,
 | |
| 		    next_linep + 1,
 | |
| 		    (screen->num_lines - screen->line - 2)
 | |
| 		    * sizeof (struct line));
 | |
|       screen->num_lines--;
 | |
| 
 | |
|       start = screen->line;
 | |
|       column = screen->column;
 | |
| 
 | |
|       screen->real_column = screen->column;
 | |
|       if (update)
 | |
| 	update_screen_all (screen, start, column, 0, 1, ALL_LINES);
 | |
|     }
 | |
| 
 | |
|   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)
 | |
|     {
 | |
|       unsigned i;
 | |
| 
 | |
|       p = grub_realloc (p, offset + size + 1);
 | |
|       if (! p)
 | |
| 	return 0;
 | |
| 
 | |
|       grub_memmove (p + offset, linep->buf + screen->column, size);
 | |
|       p[offset + size] = '\0';
 | |
| 
 | |
|       screen->killed_text = p;
 | |
| 
 | |
|       for (i = 0; i < screen->nterms; i++)
 | |
| 	screen->terms[i].orig_num = get_logical_num_lines (linep, &screen->terms[i]);
 | |
|       linep->len = screen->column;
 | |
| 
 | |
|       if (update)
 | |
| 	{
 | |
| 	  for (i = 0; i < screen->nterms; i++)
 | |
| 	    {
 | |
| 	      int new_num;
 | |
| 	      new_num = get_logical_num_lines (linep, &screen->terms[i]);
 | |
| 	      if (screen->terms[i].orig_num != new_num)
 | |
| 		update_screen (screen, &screen->terms[i],
 | |
| 			       screen->line, screen->column, 0, 1, ALL_LINES);
 | |
| 	      else
 | |
| 		update_screen (screen, &screen->terms[i],
 | |
| 			       screen->line, screen->column, 0, 0, SINGLE_LINE);
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|   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)
 | |
| {
 | |
|   if (! insert_string (screen, "\n", 0))
 | |
|     return 0;
 | |
| 
 | |
|   if (! backward_char (screen, 0))
 | |
|     return 0;
 | |
| 
 | |
|   if (update)
 | |
|     update_screen_all (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 __attribute__ ((unused)))
 | |
| {
 | |
|   char *p;
 | |
| 
 | |
|   completion_type = type;
 | |
| 
 | |
|   /* 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)
 | |
| {
 | |
|   struct line *linep;
 | |
|   int restore;
 | |
|   char *insert;
 | |
|   static int count = -1;
 | |
|   unsigned i;
 | |
|   grub_uint32_t *ucs4;
 | |
|   grub_size_t buflen;
 | |
|   grub_ssize_t ucs4len;
 | |
|   char *u8;
 | |
| 
 | |
|   if (continuous)
 | |
|     count++;
 | |
|   else
 | |
|     count = 0;
 | |
| 
 | |
|   completion_buffer.buf = 0;
 | |
|   completion_buffer.len = 0;
 | |
|   completion_buffer.max_len = 0;
 | |
| 
 | |
|   linep = screen->lines + screen->line;
 | |
|   u8 = grub_ucs4_to_utf8_alloc (linep->buf, screen->column);
 | |
|   if (!u8)
 | |
|     return 1;
 | |
| 
 | |
|   insert = grub_normal_do_completion (u8, &restore, store_completion);
 | |
|   
 | |
|   if (completion_buffer.buf)
 | |
|     {
 | |
|       buflen = grub_strlen (completion_buffer.buf);
 | |
|       ucs4 = grub_calloc (buflen + 1, sizeof (grub_uint32_t));
 | |
|       
 | |
|       if (!ucs4)
 | |
| 	{
 | |
| 	  grub_print_error ();
 | |
| 	  grub_errno = GRUB_ERR_NONE;
 | |
| 	  return 1;
 | |
| 	}
 | |
| 
 | |
|       ucs4len = grub_utf8_to_ucs4 (ucs4, buflen,
 | |
| 				   (grub_uint8_t *) completion_buffer.buf,
 | |
| 				   buflen, 0);
 | |
|       ucs4[ucs4len] = 0;
 | |
| 
 | |
|       if (restore)
 | |
| 	for (i = 0; i < screen->nterms; i++)
 | |
| 	  {
 | |
| 	    unsigned width = grub_term_width (screen->terms[i].term);
 | |
| 	    if (width > 2)
 | |
| 	      width -= 2;
 | |
| 	    if (width > 15)
 | |
| 	      width -= 6;
 | |
| 	    unsigned num_sections = ((completion_buffer.len
 | |
| 				      + width - 1)
 | |
| 				     / width);
 | |
| 	    grub_uint32_t *endp;
 | |
| 	    struct grub_term_coordinate pos;
 | |
| 	    grub_uint32_t *p = ucs4;
 | |
| 
 | |
| 	    pos = grub_term_getxy (screen->terms[i].term);
 | |
| 
 | |
| 	    screen->completion_shown = 1;
 | |
| 
 | |
| 	    grub_term_gotoxy (screen->terms[i].term,
 | |
| 			      (struct grub_term_coordinate) { 0,
 | |
| 				  screen->terms[i].geo.timeout_y });
 | |
| 	    if (screen->terms[i].geo.timeout_lines >= 2)
 | |
| 	      {
 | |
| 		grub_puts_terminal ("   ", screen->terms[i].term);
 | |
| 		switch (completion_type)
 | |
| 		  {
 | |
| 		  case GRUB_COMPLETION_TYPE_COMMAND:
 | |
| 		    grub_puts_terminal (_("Possible commands are:"),
 | |
| 					screen->terms[i].term);
 | |
| 		    break;
 | |
| 		  case GRUB_COMPLETION_TYPE_DEVICE:
 | |
| 		    grub_puts_terminal (_("Possible devices are:"),
 | |
| 					screen->terms[i].term);
 | |
| 		    break;
 | |
| 		  case GRUB_COMPLETION_TYPE_FILE:
 | |
| 		    grub_puts_terminal (_("Possible files are:"),
 | |
| 					screen->terms[i].term);
 | |
| 		    break;
 | |
| 		  case GRUB_COMPLETION_TYPE_PARTITION:
 | |
| 		    grub_puts_terminal (_("Possible partitions are:"),
 | |
| 					screen->terms[i].term);
 | |
| 		    break;
 | |
| 		  case GRUB_COMPLETION_TYPE_ARGUMENT:
 | |
| 		    grub_puts_terminal (_("Possible arguments are:"),
 | |
| 					screen->terms[i].term);
 | |
| 		    break;
 | |
| 		  default:
 | |
| 		    grub_puts_terminal (_("Possible things are:"),
 | |
| 					screen->terms[i].term);
 | |
| 		    break;
 | |
| 		  }
 | |
| 
 | |
| 		grub_puts_terminal ("\n    ", screen->terms[i].term);
 | |
| 	      }
 | |
| 
 | |
| 	    p += ((unsigned) count % num_sections) * width;
 | |
| 	    endp = p + width;
 | |
| 
 | |
| 	    if (p != ucs4)
 | |
| 	      grub_putcode (GRUB_UNICODE_LEFTARROW, screen->terms[i].term);
 | |
| 	    else
 | |
| 	      grub_putcode (' ', screen->terms[i].term);
 | |
| 
 | |
| 	    grub_print_ucs4 (p, ucs4 + ucs4len < endp ? ucs4 + ucs4len : endp,
 | |
| 			     0, 0, screen->terms[i].term);
 | |
| 
 | |
| 	    if (ucs4 + ucs4len > endp)
 | |
| 	      grub_putcode (GRUB_UNICODE_RIGHTARROW, screen->terms[i].term);
 | |
| 	    grub_term_gotoxy (screen->terms[i].term, pos);
 | |
| 	  }
 | |
|     }
 | |
| 
 | |
|   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 (struct per_term_screen *term_screen)
 | |
| {
 | |
|   struct grub_term_coordinate pos;
 | |
|   unsigned j;
 | |
|   int i;
 | |
| 
 | |
|   pos = grub_term_getxy (term_screen->term);
 | |
|   grub_term_gotoxy (term_screen->term,
 | |
| 		    (struct grub_term_coordinate) { 0,
 | |
| 			term_screen->geo.timeout_y });
 | |
| 
 | |
|   for (i = 0; i < term_screen->geo.timeout_lines; i++)
 | |
|     {
 | |
|       for (j = 0; j < grub_term_width (term_screen->term) - 1; j++)
 | |
| 	grub_putcode (' ', term_screen->term);
 | |
|       if (i + 1 < term_screen->geo.timeout_lines)
 | |
| 	grub_putcode ('\n', term_screen->term);
 | |
|     }
 | |
| 
 | |
|   grub_term_gotoxy (term_screen->term, pos);
 | |
|   grub_term_refresh (term_screen->term);
 | |
| }
 | |
| 
 | |
| static void
 | |
| clear_completions_all (struct screen *screen)
 | |
| {
 | |
|   unsigned i;
 | |
| 
 | |
|   for (i = 0; i < screen->nterms; i++)
 | |
|     clear_completions (&screen->terms[i]);
 | |
| }
 | |
| 
 | |
| /* Execute the command list in the screen SCREEN.  */
 | |
| static int
 | |
| run (struct screen *screen)
 | |
| {
 | |
|   char *script;
 | |
|   int errs_before;
 | |
|   grub_menu_t menu = NULL;
 | |
|   char *dummy[1] = { NULL };
 | |
| 
 | |
|   grub_cls ();
 | |
|   grub_printf ("  ");
 | |
|   grub_printf_ (N_("Booting a command list"));
 | |
|   grub_printf ("\n\n");
 | |
| 
 | |
|   errs_before = grub_err_printed_errors;
 | |
| 
 | |
|   if (screen->submenu)
 | |
|     {
 | |
|       grub_env_context_open ();
 | |
|       menu = grub_zalloc (sizeof (*menu));
 | |
|       if (! menu)
 | |
| 	return 0;
 | |
|       grub_env_set_menu (menu);
 | |
|     }
 | |
| 
 | |
|   /* Execute the script, line for line.  */
 | |
|   {
 | |
|     int i;
 | |
|     grub_size_t size = 0, tot_size = 0;
 | |
| 
 | |
|     for (i = 0; i < screen->num_lines; i++)
 | |
|       tot_size += grub_get_num_of_utf8_bytes (screen->lines[i].buf,
 | |
| 					      screen->lines[i].len) + 1;
 | |
| 
 | |
|     script = grub_malloc (tot_size + 1);
 | |
|     if (! script)
 | |
|       return 0;
 | |
| 
 | |
|     for (i = 0; i < screen->num_lines; i++)
 | |
|       {
 | |
| 	size += grub_ucs4_to_utf8 (screen->lines[i].buf, screen->lines[i].len,
 | |
| 				   (grub_uint8_t *) script + size,
 | |
| 				   tot_size - size);
 | |
| 	script[size++] = '\n';
 | |
|       }
 | |
|     script[size] = '\0';
 | |
|   }
 | |
|   grub_script_execute_new_scope (script, 0, dummy);
 | |
|   grub_free (script);
 | |
| 
 | |
|   if (errs_before != grub_err_printed_errors)
 | |
|     grub_wait_after_message ();
 | |
| 
 | |
|   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 (screen->submenu)
 | |
|     {
 | |
|       if (menu && menu->size)
 | |
| 	{
 | |
| 	  grub_show_menu (menu, 1, 0);
 | |
| 	  grub_normal_free_menu (menu);
 | |
| 	}
 | |
|       grub_env_context_close ();
 | |
|     }
 | |
| 
 | |
|   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;
 | |
|   unsigned i;
 | |
|   grub_term_output_t term;
 | |
| 
 | |
|   err = grub_auth_check_authentication (NULL);
 | |
| 
 | |
|   if (err)
 | |
|     {
 | |
|       grub_print_error ();
 | |
|       grub_errno = GRUB_ERR_NONE;
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   screen = make_screen (entry);
 | |
|   if (! screen)
 | |
|     return;
 | |
| 
 | |
|   screen->terms = NULL;
 | |
| 
 | |
|  refresh:
 | |
|   grub_free (screen->terms);
 | |
|   screen->nterms = 0;
 | |
|   FOR_ACTIVE_TERM_OUTPUTS(term)
 | |
|     screen->nterms++;
 | |
| 
 | |
|   for (i = 0; i < (unsigned) screen->num_lines; i++)
 | |
|     {
 | |
|       grub_free (screen->lines[i].pos);
 | |
|       screen->lines[i].pos = grub_calloc (screen->nterms, sizeof (screen->lines[i].pos[0]));
 | |
|       if (! screen->lines[i].pos)
 | |
| 	{
 | |
| 	  grub_print_error ();
 | |
| 	  destroy_screen (screen);
 | |
| 	  grub_errno = GRUB_ERR_NONE;
 | |
| 	  return;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   screen->terms = grub_calloc (screen->nterms, sizeof (screen->terms[0]));
 | |
|   if (!screen->terms)
 | |
|     {
 | |
|       grub_print_error ();
 | |
|       destroy_screen (screen);
 | |
|       grub_errno = GRUB_ERR_NONE;
 | |
|       return;
 | |
|     }
 | |
|   i = 0;
 | |
|   FOR_ACTIVE_TERM_OUTPUTS(term)
 | |
|   {
 | |
|     screen->terms[i].term = term;
 | |
|     screen->terms[i].y_line_start = 0;
 | |
|     i++;
 | |
|   }
 | |
|   /* Draw the screen.  */
 | |
|   for (i = 0; i < screen->nterms; i++)
 | |
|     grub_menu_init_page (0, 1, &screen->terms[i].geo,
 | |
| 			 screen->terms[i].term);
 | |
|   update_screen_all (screen, 0, 0, 1, 1, ALL_LINES);
 | |
|   for (i = 0; i < screen->nterms; i++)
 | |
|     grub_term_setcursor (screen->terms[i].term, 1);
 | |
|   prev_c = '\0';
 | |
| 
 | |
|   while (1)
 | |
|     {
 | |
|       int c = grub_getkey ();
 | |
| 
 | |
|       if (screen->completion_shown)
 | |
| 	{
 | |
| 	  clear_completions_all (screen);
 | |
| 	  screen->completion_shown = 0;
 | |
| 	}
 | |
| 
 | |
|       if (grub_normal_exit_level)
 | |
| 	{
 | |
| 	  destroy_screen (screen);
 | |
| 	  return;
 | |
| 	}
 | |
| 
 | |
|       switch (c)
 | |
| 	{
 | |
| 	case GRUB_TERM_KEY_UP:
 | |
| 	case GRUB_TERM_CTRL | 'p':
 | |
| 	  if (! previous_line (screen, 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'n':
 | |
| 	case GRUB_TERM_KEY_DOWN:
 | |
| 	  if (! next_line (screen, 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'f':
 | |
| 	case GRUB_TERM_KEY_RIGHT:
 | |
| 	  if (! forward_char (screen, 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'b':
 | |
| 	case GRUB_TERM_KEY_LEFT:
 | |
| 	  if (! backward_char (screen, 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'a':
 | |
| 	case GRUB_TERM_KEY_HOME:
 | |
| 	  if (! beginning_of_line (screen, 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'e':
 | |
| 	case GRUB_TERM_KEY_END:
 | |
| 	  if (! end_of_line (screen, 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'i':
 | |
| 	case '\t':
 | |
| 	  if (! complete (screen, prev_c == c, 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'd':
 | |
| 	case GRUB_TERM_KEY_DC:
 | |
| 	  if (! delete_char (screen, 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'h':
 | |
| 	case '\b':
 | |
| 	  if (! backward_delete_char (screen, 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'k':
 | |
| 	  if (! kill_line (screen, prev_c == c, 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'u':
 | |
| 	  /* FIXME: What behavior is good for this key?  */
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'y':
 | |
| 	  if (! yank (screen, 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'l':
 | |
| 	  /* FIXME: centering.  */
 | |
| 	  goto refresh;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'o':
 | |
| 	  if (! open_line (screen, 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case '\n':
 | |
| 	case '\r':
 | |
| 	  if (! insert_string (screen, "\n", 1))
 | |
| 	    goto fail;
 | |
| 	  break;
 | |
| 
 | |
| 	case GRUB_TERM_ESC:
 | |
| 	  destroy_screen (screen);
 | |
| 	  return;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'c':
 | |
| 	case GRUB_TERM_KEY_F2:
 | |
| 	  grub_cmdline_run (1, 0);
 | |
| 	  goto refresh;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'x':
 | |
| 	case GRUB_TERM_KEY_F10:
 | |
| 	  run (screen);
 | |
| 	  goto refresh;
 | |
| 
 | |
| 	case GRUB_TERM_CTRL | 'r':
 | |
| 	case GRUB_TERM_CTRL | 's':
 | |
| 	case GRUB_TERM_CTRL | '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_xputs ("\n");
 | |
|   grub_printf_ (N_("Press any key to continue..."));
 | |
|   (void) grub_getkey ();
 | |
| }
 |