mirror of
				https://git.proxmox.com/git/grub2
				synced 2025-11-04 09:14:22 +00:00 
			
		
		
		
	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>
Patch-Name: safe-alloc-4.patch
		
	
			
		
			
				
	
	
		
			692 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			692 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 *  GRUB  --  GRand Unified Bootloader
 | 
						|
 *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,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/misc.h>
 | 
						|
#include <grub/term.h>
 | 
						|
#include <grub/err.h>
 | 
						|
#include <grub/types.h>
 | 
						|
#include <grub/mm.h>
 | 
						|
#include <grub/partition.h>
 | 
						|
#include <grub/disk.h>
 | 
						|
#include <grub/file.h>
 | 
						|
#include <grub/env.h>
 | 
						|
#include <grub/i18n.h>
 | 
						|
#include <grub/charset.h>
 | 
						|
#include <grub/safemath.h>
 | 
						|
 | 
						|
static grub_uint32_t *kill_buf;
 | 
						|
 | 
						|
static int hist_size;
 | 
						|
static grub_uint32_t **hist_lines = 0;
 | 
						|
static int hist_pos = 0;
 | 
						|
static int hist_end = 0;
 | 
						|
static int hist_used = 0;
 | 
						|
 | 
						|
grub_err_t
 | 
						|
grub_set_history (int newsize)
 | 
						|
{
 | 
						|
  grub_uint32_t **old_hist_lines = hist_lines;
 | 
						|
  hist_lines = grub_calloc (newsize, sizeof (grub_uint32_t *));
 | 
						|
 | 
						|
  /* Copy the old lines into the new buffer.  */
 | 
						|
  if (old_hist_lines)
 | 
						|
    {
 | 
						|
      /* Remove the lines that don't fit in the new buffer.  */
 | 
						|
      if (newsize < hist_used)
 | 
						|
	{
 | 
						|
	  grub_size_t i;
 | 
						|
	  grub_size_t delsize = hist_used - newsize;
 | 
						|
	  hist_used = newsize;
 | 
						|
 | 
						|
	  for (i = 1; i < delsize + 1; i++)
 | 
						|
	    {
 | 
						|
	      grub_ssize_t pos = hist_end - i;
 | 
						|
	      if (pos < 0)
 | 
						|
		pos += hist_size;
 | 
						|
	      grub_free (old_hist_lines[pos]);
 | 
						|
	    }
 | 
						|
 | 
						|
	  hist_end -= delsize;
 | 
						|
	  if (hist_end < 0)
 | 
						|
	    hist_end += hist_size;
 | 
						|
	}
 | 
						|
 | 
						|
      if (hist_pos < hist_end)
 | 
						|
	grub_memmove (hist_lines, old_hist_lines + hist_pos,
 | 
						|
		      (hist_end - hist_pos) * sizeof (grub_uint32_t *));
 | 
						|
      else if (hist_used)
 | 
						|
	{
 | 
						|
	  /* Copy the older part.  */
 | 
						|
	  grub_memmove (hist_lines, old_hist_lines + hist_pos,
 | 
						|
 			(hist_size - hist_pos) * sizeof (grub_uint32_t *));
 | 
						|
 | 
						|
	  /* Copy the newer part. */
 | 
						|
	  grub_memmove (hist_lines + hist_size - hist_pos, old_hist_lines,
 | 
						|
			hist_end * sizeof (grub_uint32_t *));
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  grub_free (old_hist_lines);
 | 
						|
 | 
						|
  hist_size = newsize;
 | 
						|
  hist_pos = 0;
 | 
						|
  hist_end = hist_used;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Get the entry POS from the history where `0' is the newest
 | 
						|
   entry.  */
 | 
						|
static grub_uint32_t *
 | 
						|
grub_history_get (unsigned pos)
 | 
						|
{
 | 
						|
  pos = (hist_pos + pos) % hist_size;
 | 
						|
  return hist_lines[pos];
 | 
						|
}
 | 
						|
 | 
						|
static grub_size_t
 | 
						|
strlen_ucs4 (const grub_uint32_t *s)
 | 
						|
{
 | 
						|
  const grub_uint32_t *p = s;
 | 
						|
 | 
						|
  while (*p)
 | 
						|
    p++;
 | 
						|
 | 
						|
  return p - s;
 | 
						|
}
 | 
						|
 | 
						|
/* Replace the history entry on position POS with the string S.  */
 | 
						|
static void
 | 
						|
grub_history_set (int pos, grub_uint32_t *s, grub_size_t len)
 | 
						|
{
 | 
						|
  grub_free (hist_lines[pos]);
 | 
						|
  hist_lines[pos] = grub_calloc (len + 1, sizeof (grub_uint32_t));
 | 
						|
  if (!hist_lines[pos])
 | 
						|
    {
 | 
						|
      grub_print_error ();
 | 
						|
      grub_errno = GRUB_ERR_NONE;
 | 
						|
      return ;
 | 
						|
    }
 | 
						|
  grub_memcpy (hist_lines[pos], s, len * sizeof (grub_uint32_t));
 | 
						|
  hist_lines[pos][len] = 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Insert a new history line S on the top of the history.  */
 | 
						|
static void
 | 
						|
grub_history_add (grub_uint32_t *s, grub_size_t len)
 | 
						|
{
 | 
						|
  /* Remove the oldest entry in the history to make room for a new
 | 
						|
     entry.  */
 | 
						|
  if (hist_used + 1 > hist_size)
 | 
						|
    {
 | 
						|
      hist_end--;
 | 
						|
      if (hist_end < 0)
 | 
						|
	hist_end = hist_size + hist_end;
 | 
						|
 | 
						|
      grub_free (hist_lines[hist_end]);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    hist_used++;
 | 
						|
 | 
						|
  /* Move to the next position.  */
 | 
						|
  hist_pos--;
 | 
						|
  if (hist_pos < 0)
 | 
						|
    hist_pos = hist_size + hist_pos;
 | 
						|
 | 
						|
  /* Insert into history.  */
 | 
						|
  hist_lines[hist_pos] = NULL;
 | 
						|
  grub_history_set (hist_pos, s, len);
 | 
						|
}
 | 
						|
 | 
						|
/* Replace the history entry on position POS with the string S.  */
 | 
						|
static void
 | 
						|
grub_history_replace (unsigned pos, grub_uint32_t *s, grub_size_t len)
 | 
						|
{
 | 
						|
  grub_history_set ((hist_pos + pos) % hist_size, s, len);
 | 
						|
}
 | 
						|
 | 
						|
/* A completion hook to print items.  */
 | 
						|
static void
 | 
						|
print_completion (const char *item, grub_completion_type_t type, int count)
 | 
						|
{
 | 
						|
  if (count == 0)
 | 
						|
    {
 | 
						|
      /* If this is the first time, print a label.  */
 | 
						|
      
 | 
						|
      grub_puts ("");
 | 
						|
      switch (type)
 | 
						|
	{
 | 
						|
	case GRUB_COMPLETION_TYPE_COMMAND:
 | 
						|
	  grub_puts_ (N_("Possible commands are:"));
 | 
						|
	  break;
 | 
						|
	case GRUB_COMPLETION_TYPE_DEVICE:
 | 
						|
	  grub_puts_ (N_("Possible devices are:"));
 | 
						|
	  break;
 | 
						|
	case GRUB_COMPLETION_TYPE_FILE:
 | 
						|
	  grub_puts_ (N_("Possible files are:"));
 | 
						|
	  break;
 | 
						|
	case GRUB_COMPLETION_TYPE_PARTITION:
 | 
						|
	  grub_puts_ (N_("Possible partitions are:"));
 | 
						|
	  break;
 | 
						|
	case GRUB_COMPLETION_TYPE_ARGUMENT:
 | 
						|
	  grub_puts_ (N_("Possible arguments are:"));
 | 
						|
	  break;
 | 
						|
	default:
 | 
						|
	  /* TRANSLATORS: this message is used if none of above matches.
 | 
						|
	     This shouldn't happen but please use the general term for
 | 
						|
	     "thing" or "object".  */
 | 
						|
	  grub_puts_ (N_("Possible things are:"));
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      grub_puts ("");
 | 
						|
    }
 | 
						|
 | 
						|
  if (type == GRUB_COMPLETION_TYPE_PARTITION)
 | 
						|
    {
 | 
						|
      grub_normal_print_device_info (item);
 | 
						|
      grub_errno = GRUB_ERR_NONE;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    grub_printf (" %s", item);
 | 
						|
}
 | 
						|
 | 
						|
struct cmdline_term
 | 
						|
{
 | 
						|
  struct grub_term_coordinate pos;
 | 
						|
  unsigned ystart, width, height;
 | 
						|
  unsigned prompt_len;
 | 
						|
  struct grub_term_output *term;
 | 
						|
};
 | 
						|
 | 
						|
static inline void
 | 
						|
cl_set_pos (struct cmdline_term *cl_term, grub_size_t lpos)
 | 
						|
{
 | 
						|
  cl_term->pos.x = (cl_term->prompt_len + lpos) % cl_term->width;
 | 
						|
  cl_term->pos.y = cl_term->ystart
 | 
						|
    + (cl_term->prompt_len + lpos) / cl_term->width;
 | 
						|
  grub_term_gotoxy (cl_term->term, cl_term->pos);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
cl_set_pos_all (struct cmdline_term *cl_terms, unsigned nterms,
 | 
						|
		grub_size_t lpos)
 | 
						|
{
 | 
						|
  unsigned i;
 | 
						|
  for (i = 0; i < nterms; i++)
 | 
						|
    cl_set_pos (&cl_terms[i], lpos);
 | 
						|
}
 | 
						|
 | 
						|
static inline void __attribute__ ((always_inline))
 | 
						|
cl_print (struct cmdline_term *cl_term, grub_uint32_t c,
 | 
						|
	  grub_uint32_t *start, grub_uint32_t *end)
 | 
						|
{
 | 
						|
  grub_uint32_t *p;
 | 
						|
 | 
						|
  for (p = start; p < end; p++)
 | 
						|
    {
 | 
						|
      if (c)
 | 
						|
	grub_putcode (c, cl_term->term);
 | 
						|
      else
 | 
						|
	grub_putcode (*p, cl_term->term);
 | 
						|
      cl_term->pos.x++;
 | 
						|
      if (cl_term->pos.x >= cl_term->width - 1)
 | 
						|
	{
 | 
						|
	  cl_term->pos.x = 0;
 | 
						|
	  if (cl_term->pos.y >= (unsigned) (cl_term->height - 1))
 | 
						|
	    cl_term->ystart--;
 | 
						|
	  else
 | 
						|
	    cl_term->pos.y++;
 | 
						|
	  grub_putcode ('\n', cl_term->term);
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
cl_print_all (struct cmdline_term *cl_terms, unsigned nterms,
 | 
						|
	      grub_uint32_t c, grub_uint32_t *start, grub_uint32_t *end)
 | 
						|
{
 | 
						|
  unsigned i;
 | 
						|
  for (i = 0; i < nterms; i++)
 | 
						|
    cl_print (&cl_terms[i], c, start, end);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
init_clterm (struct cmdline_term *cl_term_cur)
 | 
						|
{
 | 
						|
  cl_term_cur->pos.x = cl_term_cur->prompt_len;
 | 
						|
  cl_term_cur->pos.y = grub_term_getxy (cl_term_cur->term).y;
 | 
						|
  cl_term_cur->ystart = cl_term_cur->pos.y;
 | 
						|
  cl_term_cur->width = grub_term_width (cl_term_cur->term);
 | 
						|
  cl_term_cur->height = grub_term_height (cl_term_cur->term);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
cl_delete (struct cmdline_term *cl_terms, unsigned nterms,
 | 
						|
	   grub_uint32_t *buf,
 | 
						|
	   grub_size_t lpos, grub_size_t *llen, unsigned len)
 | 
						|
{
 | 
						|
  if (lpos + len <= (*llen))
 | 
						|
    {
 | 
						|
      cl_set_pos_all (cl_terms, nterms, (*llen) - len);
 | 
						|
      cl_print_all (cl_terms, nterms, ' ', buf + (*llen) - len, buf + (*llen));
 | 
						|
 | 
						|
      cl_set_pos_all (cl_terms, nterms, lpos);
 | 
						|
 | 
						|
      grub_memmove (buf + lpos, buf + lpos + len,
 | 
						|
		    sizeof (grub_uint32_t) * ((*llen) - lpos + 1));
 | 
						|
      (*llen) -= len;
 | 
						|
      cl_print_all (cl_terms, nterms, 0, buf + lpos, buf + (*llen));
 | 
						|
      cl_set_pos_all (cl_terms, nterms, lpos);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
cl_insert (struct cmdline_term *cl_terms, unsigned nterms,
 | 
						|
	   grub_size_t *lpos, grub_size_t *llen,
 | 
						|
	   grub_size_t *max_len, grub_uint32_t **buf,
 | 
						|
	   const grub_uint32_t *str)
 | 
						|
{
 | 
						|
  grub_size_t len = strlen_ucs4 (str);
 | 
						|
 | 
						|
  if (len + (*llen) >= (*max_len))
 | 
						|
    {
 | 
						|
      grub_uint32_t *nbuf;
 | 
						|
      grub_size_t sz;
 | 
						|
 | 
						|
      if (grub_mul (*max_len, 2, max_len) ||
 | 
						|
	  grub_mul (*max_len, sizeof (grub_uint32_t), &sz))
 | 
						|
	{
 | 
						|
	  grub_errno = GRUB_ERR_OUT_OF_RANGE;
 | 
						|
	  goto fail;
 | 
						|
	}
 | 
						|
 | 
						|
      nbuf = grub_realloc ((*buf), sz);
 | 
						|
      if (nbuf)
 | 
						|
	(*buf) = nbuf;
 | 
						|
      else
 | 
						|
	{
 | 
						|
 fail:
 | 
						|
	  grub_print_error ();
 | 
						|
	  grub_errno = GRUB_ERR_NONE;
 | 
						|
	  (*max_len) /= 2;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (len + (*llen) < (*max_len))
 | 
						|
    {
 | 
						|
      grub_memmove ((*buf) + (*lpos) + len, (*buf) + (*lpos),
 | 
						|
		    ((*llen) - (*lpos) + 1) * sizeof (grub_uint32_t));
 | 
						|
      grub_memmove ((*buf) + (*lpos), str, len * sizeof (grub_uint32_t));
 | 
						|
 | 
						|
      (*llen) += len;
 | 
						|
      cl_set_pos_all (cl_terms, nterms, (*lpos));
 | 
						|
      cl_print_all (cl_terms, nterms, 0, *buf + (*lpos), *buf + (*llen));
 | 
						|
      (*lpos) += len;
 | 
						|
      cl_set_pos_all (cl_terms, nterms, (*lpos));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Get a command-line. If ESC is pushed, return zero,
 | 
						|
   otherwise return command line.  */
 | 
						|
/* FIXME: The dumb interface is not supported yet.  */
 | 
						|
char *
 | 
						|
grub_cmdline_get (const char *prompt_translated)
 | 
						|
{
 | 
						|
  grub_size_t lpos, llen;
 | 
						|
  grub_uint32_t *buf;
 | 
						|
  grub_size_t max_len = 256;
 | 
						|
  int key;
 | 
						|
  int histpos = 0;
 | 
						|
  struct cmdline_term *cl_terms;
 | 
						|
  char *ret;
 | 
						|
  unsigned nterms;
 | 
						|
 | 
						|
  buf = grub_calloc (max_len, sizeof (grub_uint32_t));
 | 
						|
  if (!buf)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  lpos = llen = 0;
 | 
						|
  buf[0] = '\0';
 | 
						|
 | 
						|
  {
 | 
						|
    grub_term_output_t term;
 | 
						|
 | 
						|
    FOR_ACTIVE_TERM_OUTPUTS(term)
 | 
						|
      if ((grub_term_getxy (term).x) != 0)
 | 
						|
	grub_putcode ('\n', term);
 | 
						|
  }
 | 
						|
  grub_xputs (prompt_translated);
 | 
						|
  grub_xputs (" ");
 | 
						|
  grub_normal_reset_more ();
 | 
						|
 | 
						|
  {
 | 
						|
    struct cmdline_term *cl_term_cur;
 | 
						|
    struct grub_term_output *cur;
 | 
						|
    grub_uint32_t *unicode_msg;
 | 
						|
    grub_size_t msg_len = grub_strlen (prompt_translated) + 3;
 | 
						|
 | 
						|
    nterms = 0;
 | 
						|
    FOR_ACTIVE_TERM_OUTPUTS(cur)
 | 
						|
      nterms++;
 | 
						|
 | 
						|
    cl_terms = grub_calloc (nterms, sizeof (cl_terms[0]));
 | 
						|
    if (!cl_terms)
 | 
						|
      {
 | 
						|
	grub_free (buf);
 | 
						|
	return 0;
 | 
						|
      }
 | 
						|
    cl_term_cur = cl_terms;
 | 
						|
 | 
						|
    unicode_msg = grub_calloc (msg_len, sizeof (grub_uint32_t));
 | 
						|
    if (!unicode_msg)
 | 
						|
      {
 | 
						|
	grub_free (buf);
 | 
						|
	grub_free (cl_terms);
 | 
						|
	return 0;
 | 
						|
      }
 | 
						|
    msg_len = grub_utf8_to_ucs4 (unicode_msg, msg_len - 1,
 | 
						|
				 (grub_uint8_t *) prompt_translated, -1, 0);
 | 
						|
    unicode_msg[msg_len++] = ' ';
 | 
						|
 | 
						|
    FOR_ACTIVE_TERM_OUTPUTS(cur)
 | 
						|
    {
 | 
						|
      cl_term_cur->term = cur;
 | 
						|
      cl_term_cur->prompt_len = grub_getstringwidth (unicode_msg,
 | 
						|
						     unicode_msg + msg_len,
 | 
						|
						     cur);
 | 
						|
      init_clterm (cl_term_cur);
 | 
						|
      cl_term_cur++;
 | 
						|
    }
 | 
						|
    grub_free (unicode_msg);
 | 
						|
  }
 | 
						|
 | 
						|
  if (hist_used == 0)
 | 
						|
    grub_history_add (buf, llen);
 | 
						|
 | 
						|
  grub_refresh ();
 | 
						|
 | 
						|
  while ((key = grub_getkey ()) != '\n' && key != '\r')
 | 
						|
    {
 | 
						|
      switch (key)
 | 
						|
	{
 | 
						|
	case GRUB_TERM_CTRL | 'a':
 | 
						|
	case GRUB_TERM_KEY_HOME:
 | 
						|
	  lpos = 0;
 | 
						|
	  cl_set_pos_all (cl_terms, nterms, lpos);
 | 
						|
	  break;
 | 
						|
 | 
						|
	case GRUB_TERM_CTRL | 'b':
 | 
						|
	case GRUB_TERM_KEY_LEFT:
 | 
						|
	  if (lpos > 0)
 | 
						|
	    {
 | 
						|
	      lpos--;
 | 
						|
	      cl_set_pos_all (cl_terms, nterms, lpos);
 | 
						|
	    }
 | 
						|
	  break;
 | 
						|
 | 
						|
	case GRUB_TERM_CTRL | 'e':
 | 
						|
	case GRUB_TERM_KEY_END:
 | 
						|
	  lpos = llen;
 | 
						|
	  cl_set_pos_all (cl_terms, nterms, lpos);
 | 
						|
	  break;
 | 
						|
 | 
						|
	case GRUB_TERM_CTRL | 'f':
 | 
						|
	case GRUB_TERM_KEY_RIGHT:
 | 
						|
	  if (lpos < llen)
 | 
						|
	    {
 | 
						|
	      lpos++;
 | 
						|
	      cl_set_pos_all (cl_terms, nterms, lpos);
 | 
						|
	    }
 | 
						|
	  break;
 | 
						|
 | 
						|
	case GRUB_TERM_CTRL | 'i':
 | 
						|
	case '\t':
 | 
						|
	  {
 | 
						|
	    int restore;
 | 
						|
	    char *insertu8;
 | 
						|
	    char *bufu8;
 | 
						|
	    grub_uint32_t c;
 | 
						|
 | 
						|
	    c = buf[lpos];
 | 
						|
	    buf[lpos] = '\0';
 | 
						|
 | 
						|
	    bufu8 = grub_ucs4_to_utf8_alloc (buf, lpos);
 | 
						|
	    buf[lpos] = c;
 | 
						|
	    if (!bufu8)
 | 
						|
	      {
 | 
						|
		grub_print_error ();
 | 
						|
		grub_errno = GRUB_ERR_NONE;
 | 
						|
		break;
 | 
						|
	      }
 | 
						|
 | 
						|
	    insertu8 = grub_normal_do_completion (bufu8, &restore,
 | 
						|
						  print_completion);
 | 
						|
	    grub_free (bufu8);
 | 
						|
 | 
						|
	    grub_normal_reset_more ();
 | 
						|
 | 
						|
	    if (restore)
 | 
						|
	      {
 | 
						|
		unsigned i;
 | 
						|
 | 
						|
		/* Restore the prompt.  */
 | 
						|
		grub_xputs ("\n");
 | 
						|
		grub_xputs (prompt_translated);
 | 
						|
		grub_xputs (" ");
 | 
						|
 | 
						|
		for (i = 0; i < nterms; i++)
 | 
						|
		  init_clterm (&cl_terms[i]);
 | 
						|
 | 
						|
		cl_print_all (cl_terms, nterms, 0, buf, buf + llen);
 | 
						|
	      }
 | 
						|
 | 
						|
	    if (insertu8)
 | 
						|
	      {
 | 
						|
		grub_size_t insertlen;
 | 
						|
		grub_ssize_t t;
 | 
						|
		grub_uint32_t *insert;
 | 
						|
 | 
						|
		insertlen = grub_strlen (insertu8);
 | 
						|
		insert = grub_calloc (insertlen + 1, sizeof (grub_uint32_t));
 | 
						|
		if (!insert)
 | 
						|
		  {
 | 
						|
		    grub_free (insertu8);
 | 
						|
		    grub_print_error ();
 | 
						|
		    grub_errno = GRUB_ERR_NONE;
 | 
						|
		    break;
 | 
						|
		  }
 | 
						|
		t = grub_utf8_to_ucs4 (insert, insertlen,
 | 
						|
				       (grub_uint8_t *) insertu8,
 | 
						|
				       insertlen, 0);
 | 
						|
		if (t > 0)
 | 
						|
		  {
 | 
						|
		    if (insert[t-1] == ' ' && buf[lpos] == ' ')
 | 
						|
		      {
 | 
						|
			insert[t-1] = 0;
 | 
						|
			if (t != 1)
 | 
						|
			  cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, insert);
 | 
						|
			lpos++;
 | 
						|
		      }
 | 
						|
		    else
 | 
						|
		      {
 | 
						|
			insert[t] = 0;
 | 
						|
			cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, insert);
 | 
						|
		      }
 | 
						|
		  }
 | 
						|
 | 
						|
		grub_free (insertu8);
 | 
						|
		grub_free (insert);
 | 
						|
	      }
 | 
						|
	    cl_set_pos_all (cl_terms, nterms, lpos);
 | 
						|
	  }
 | 
						|
	  break;
 | 
						|
 | 
						|
	case GRUB_TERM_CTRL | 'k':
 | 
						|
	  if (lpos < llen)
 | 
						|
	    {
 | 
						|
	      grub_free (kill_buf);
 | 
						|
 | 
						|
	      kill_buf = grub_malloc ((llen - lpos + 1)
 | 
						|
				      * sizeof (grub_uint32_t));
 | 
						|
	      if (grub_errno)
 | 
						|
		{
 | 
						|
		  grub_print_error ();
 | 
						|
		  grub_errno = GRUB_ERR_NONE;
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  grub_memcpy (kill_buf, buf + lpos,
 | 
						|
			       (llen - lpos + 1) * sizeof (grub_uint32_t));
 | 
						|
		  kill_buf[llen - lpos] = 0;
 | 
						|
		}
 | 
						|
 | 
						|
	      cl_delete (cl_terms, nterms,
 | 
						|
			 buf, lpos, &llen, llen - lpos);
 | 
						|
	    }
 | 
						|
	  break;
 | 
						|
 | 
						|
	case GRUB_TERM_CTRL | 'n':
 | 
						|
	case GRUB_TERM_KEY_DOWN:
 | 
						|
	  {
 | 
						|
	    grub_uint32_t *hist;
 | 
						|
 | 
						|
	    lpos = 0;
 | 
						|
 | 
						|
	    if (histpos > 0)
 | 
						|
	      {
 | 
						|
		grub_history_replace (histpos, buf, llen);
 | 
						|
		histpos--;
 | 
						|
	      }
 | 
						|
 | 
						|
	    cl_delete (cl_terms, nterms,
 | 
						|
		       buf, lpos, &llen, llen);
 | 
						|
	    hist = grub_history_get (histpos);
 | 
						|
	    cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, hist);
 | 
						|
 | 
						|
	    break;
 | 
						|
	  }
 | 
						|
 | 
						|
	case GRUB_TERM_KEY_UP:
 | 
						|
	case GRUB_TERM_CTRL | 'p':
 | 
						|
	  {
 | 
						|
	    grub_uint32_t *hist;
 | 
						|
 | 
						|
	    lpos = 0;
 | 
						|
 | 
						|
	    if (histpos < hist_used - 1)
 | 
						|
	      {
 | 
						|
		grub_history_replace (histpos, buf, llen);
 | 
						|
		histpos++;
 | 
						|
	      }
 | 
						|
 | 
						|
	    cl_delete (cl_terms, nterms,
 | 
						|
		       buf, lpos, &llen, llen);
 | 
						|
	    hist = grub_history_get (histpos);
 | 
						|
 | 
						|
	    cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, hist);
 | 
						|
	  }
 | 
						|
	  break;
 | 
						|
 | 
						|
	case GRUB_TERM_CTRL | 'u':
 | 
						|
	  if (lpos > 0)
 | 
						|
	    {
 | 
						|
	      grub_size_t n = lpos;
 | 
						|
 | 
						|
	      grub_free (kill_buf);
 | 
						|
 | 
						|
	      kill_buf = grub_calloc (n + 1, sizeof (grub_uint32_t));
 | 
						|
	      if (grub_errno)
 | 
						|
		{
 | 
						|
		  grub_print_error ();
 | 
						|
		  grub_errno = GRUB_ERR_NONE;
 | 
						|
		}
 | 
						|
	      if (kill_buf)
 | 
						|
		{
 | 
						|
		  grub_memcpy (kill_buf, buf, n * sizeof(grub_uint32_t));
 | 
						|
		  kill_buf[n] = 0;
 | 
						|
		}
 | 
						|
 | 
						|
	      lpos = 0;
 | 
						|
	      cl_set_pos_all (cl_terms, nterms, lpos);
 | 
						|
	      cl_delete (cl_terms, nterms,
 | 
						|
			 buf, lpos, &llen, n);
 | 
						|
	    }
 | 
						|
	  break;
 | 
						|
 | 
						|
	case GRUB_TERM_CTRL | 'y':
 | 
						|
	  if (kill_buf)
 | 
						|
	    cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, kill_buf);
 | 
						|
	  break;
 | 
						|
 | 
						|
	case GRUB_TERM_ESC:
 | 
						|
	  grub_free (cl_terms);
 | 
						|
	  grub_free (buf);
 | 
						|
	  return 0;
 | 
						|
 | 
						|
	case GRUB_TERM_BACKSPACE:
 | 
						|
	  if (lpos > 0)
 | 
						|
	    {
 | 
						|
	      lpos--;
 | 
						|
	      cl_set_pos_all (cl_terms, nterms, lpos);
 | 
						|
	    }
 | 
						|
          else
 | 
						|
            break;
 | 
						|
	  /* fall through */
 | 
						|
 | 
						|
	case GRUB_TERM_CTRL | 'd':
 | 
						|
	case GRUB_TERM_KEY_DC:
 | 
						|
	  if (lpos < llen)
 | 
						|
	    cl_delete (cl_terms, nterms,
 | 
						|
		       buf, lpos, &llen, 1);
 | 
						|
	  break;
 | 
						|
 | 
						|
	default:
 | 
						|
	  if (grub_isprint (key))
 | 
						|
	    {
 | 
						|
	      grub_uint32_t str[2];
 | 
						|
 | 
						|
	      str[0] = key;
 | 
						|
	      str[1] = '\0';
 | 
						|
	      cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, str);
 | 
						|
	    }
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
 | 
						|
      grub_refresh ();
 | 
						|
    }
 | 
						|
 | 
						|
  grub_xputs ("\n");
 | 
						|
  grub_refresh ();
 | 
						|
 | 
						|
  histpos = 0;
 | 
						|
  if (strlen_ucs4 (buf) > 0)
 | 
						|
    {
 | 
						|
      grub_uint32_t empty[] = { 0 };
 | 
						|
      grub_history_replace (histpos, buf, llen);
 | 
						|
      grub_history_add (empty, 0);
 | 
						|
    }
 | 
						|
 | 
						|
  ret = grub_ucs4_to_utf8_alloc (buf, llen + 1);
 | 
						|
  grub_free (buf);
 | 
						|
  grub_free (cl_terms);
 | 
						|
  return ret;
 | 
						|
}
 |