Home | History | Annotate | Download | only in stage2
      1 /* char_io.c - basic console input and output */
      2 /*
      3  *  GRUB  --  GRand Unified Bootloader
      4  *  Copyright (C) 1999,2000,2001,2002,2004  Free Software Foundation, Inc.
      5  *
      6  *  This program is free software; you can redistribute it and/or modify
      7  *  it under the terms of the GNU General Public License as published by
      8  *  the Free Software Foundation; either version 2 of the License, or
      9  *  (at your option) any later version.
     10  *
     11  *  This program is distributed in the hope that it will be useful,
     12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *  GNU General Public License for more details.
     15  *
     16  *  You should have received a copy of the GNU General Public License
     17  *  along with this program; if not, write to the Free Software
     18  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     19  */
     20 
     21 #include <shared.h>
     22 #include <term.h>
     23 
     24 #ifdef SUPPORT_HERCULES
     25 # include <hercules.h>
     26 #endif
     27 
     28 #ifdef SUPPORT_SERIAL
     29 # include <serial.h>
     30 #endif
     31 
     32 #ifndef STAGE1_5
     33 struct term_entry term_table[] =
     34   {
     35     {
     36       "console",
     37       0,
     38       console_putchar,
     39       console_checkkey,
     40       console_getkey,
     41       console_getxy,
     42       console_gotoxy,
     43       console_cls,
     44       console_setcolorstate,
     45       console_setcolor,
     46       console_setcursor
     47     },
     48 #ifdef SUPPORT_SERIAL
     49     {
     50       "serial",
     51       /* A serial device must be initialized.  */
     52       TERM_NEED_INIT,
     53       serial_putchar,
     54       serial_checkkey,
     55       serial_getkey,
     56       serial_getxy,
     57       serial_gotoxy,
     58       serial_cls,
     59       serial_setcolorstate,
     60       0,
     61       0
     62     },
     63 #endif /* SUPPORT_SERIAL */
     64 #ifdef SUPPORT_HERCULES
     65     {
     66       "hercules",
     67       0,
     68       hercules_putchar,
     69       console_checkkey,
     70       console_getkey,
     71       hercules_getxy,
     72       hercules_gotoxy,
     73       hercules_cls,
     74       hercules_setcolorstate,
     75       hercules_setcolor,
     76       hercules_setcursor
     77     },
     78 #endif /* SUPPORT_HERCULES */
     79     /* This must be the last entry.  */
     80     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
     81   };
     82 
     83 /* This must be console.  */
     84 struct term_entry *current_term = term_table;
     85 
     86 int max_lines = 24;
     87 int count_lines = -1;
     88 int use_pager = 1;
     89 #endif
     90 
     91 void
     92 print_error (void)
     93 {
     94   if (errnum > ERR_NONE && errnum < MAX_ERR_NUM)
     95 #ifndef STAGE1_5
     96     /* printf("\7\n %s\n", err_list[errnum]); */
     97     printf ("\nError %u: %s\n", errnum, err_list[errnum]);
     98 #else /* STAGE1_5 */
     99     printf ("Error %u\n", errnum);
    100 #endif /* STAGE1_5 */
    101 }
    102 
    103 char *
    104 convert_to_ascii (char *buf, int c,...)
    105 {
    106   unsigned long num = *((&c) + 1), mult = 10;
    107   char *ptr = buf;
    108 
    109 #ifndef STAGE1_5
    110   if (c == 'x' || c == 'X')
    111     mult = 16;
    112 
    113   if ((num & 0x80000000uL) && c == 'd')
    114     {
    115       num = (~num) + 1;
    116       *(ptr++) = '-';
    117       buf++;
    118     }
    119 #endif
    120 
    121   do
    122     {
    123       int dig = num % mult;
    124       *(ptr++) = ((dig > 9) ? dig + 'a' - 10 : '0' + dig);
    125     }
    126   while (num /= mult);
    127 
    128   /* reorder to correct direction!! */
    129   {
    130     char *ptr1 = ptr - 1;
    131     char *ptr2 = buf;
    132     while (ptr1 > ptr2)
    133       {
    134 	int tmp = *ptr1;
    135 	*ptr1 = *ptr2;
    136 	*ptr2 = tmp;
    137 	ptr1--;
    138 	ptr2++;
    139       }
    140   }
    141 
    142   return ptr;
    143 }
    144 
    145 void
    146 grub_putstr (const char *str)
    147 {
    148   while (*str)
    149     grub_putchar (*str++);
    150 }
    151 
    152 void
    153 grub_printf (const char *format,...)
    154 {
    155   int *dataptr = (int *) &format;
    156   char c, str[16];
    157 
    158   dataptr++;
    159 
    160   while ((c = *(format++)) != 0)
    161     {
    162       if (c != '%')
    163 	grub_putchar (c);
    164       else
    165 	switch (c = *(format++))
    166 	  {
    167 #ifndef STAGE1_5
    168 	  case 'd':
    169 	  case 'x':
    170 	  case 'X':
    171 #endif
    172 	  case 'u':
    173 	    *convert_to_ascii (str, c, *((unsigned long *) dataptr++)) = 0;
    174 	    grub_putstr (str);
    175 	    break;
    176 
    177 #ifndef STAGE1_5
    178 	  case 'c':
    179 	    grub_putchar ((*(dataptr++)) & 0xff);
    180 	    break;
    181 
    182 	  case 's':
    183 	    grub_putstr ((char *) *(dataptr++));
    184 	    break;
    185 #endif
    186 	  }
    187     }
    188 }
    189 
    190 #ifndef STAGE1_5
    191 int
    192 grub_sprintf (char *buffer, const char *format, ...)
    193 {
    194   /* XXX hohmuth
    195      ugly hack -- should unify with printf() */
    196   int *dataptr = (int *) &format;
    197   char c, *ptr, str[16];
    198   char *bp = buffer;
    199 
    200   dataptr++;
    201 
    202   while ((c = *format++) != 0)
    203     {
    204       if (c != '%')
    205 	*bp++ = c; /* putchar(c); */
    206       else
    207 	switch (c = *(format++))
    208 	  {
    209 	  case 'd': case 'u': case 'x':
    210 	    *convert_to_ascii (str, c, *((unsigned long *) dataptr++)) = 0;
    211 
    212 	    ptr = str;
    213 
    214 	    while (*ptr)
    215 	      *bp++ = *(ptr++); /* putchar(*(ptr++)); */
    216 	    break;
    217 
    218 	  case 'c': *bp++ = (*(dataptr++))&0xff;
    219 	    /* putchar((*(dataptr++))&0xff); */
    220 	    break;
    221 
    222 	  case 's':
    223 	    ptr = (char *) (*(dataptr++));
    224 
    225 	    while ((c = *ptr++) != 0)
    226 	      *bp++ = c; /* putchar(c); */
    227 	    break;
    228 	  }
    229     }
    230 
    231   *bp = 0;
    232   return bp - buffer;
    233 }
    234 
    235 
    236 void
    237 init_page (void)
    238 {
    239   cls ();
    240 
    241   grub_printf ("\n    GNU GRUB  version %s  (%dK lower / %dK upper memory)\n\n",
    242 	  version_string, mbi.mem_lower, mbi.mem_upper);
    243 }
    244 
    245 /* The number of the history entries.  */
    246 static int num_history = 0;
    247 
    248 /* Get the NOth history. If NO is less than zero or greater than or
    249    equal to NUM_HISTORY, return NULL. Otherwise return a valid string.  */
    250 static char *
    251 get_history (int no)
    252 {
    253   if (no < 0 || no >= num_history)
    254     return 0;
    255 
    256   return (char *) HISTORY_BUF + MAX_CMDLINE * no;
    257 }
    258 
    259 /* Add CMDLINE to the history buffer.  */
    260 static void
    261 add_history (const char *cmdline, int no)
    262 {
    263   grub_memmove ((char *) HISTORY_BUF + MAX_CMDLINE * (no + 1),
    264 		(char *) HISTORY_BUF + MAX_CMDLINE * no,
    265 		MAX_CMDLINE * (num_history - no));
    266   grub_strcpy ((char *) HISTORY_BUF + MAX_CMDLINE * no, cmdline);
    267   if (num_history < HISTORY_SIZE)
    268     num_history++;
    269 }
    270 
    271 static int
    272 real_get_cmdline (char *prompt, char *cmdline, int maxlen,
    273 		  int echo_char, int readline)
    274 {
    275   /* This is a rather complicated function. So explain the concept.
    276 
    277      A command-line consists of ``section''s. A section is a part of the
    278      line which may be displayed on the screen, but a section is never
    279      displayed with another section simultaneously.
    280 
    281      Each section is basically 77 or less characters, but the exception
    282      is the first section, which is 78 or less characters, because the
    283      starting point is special. See below.
    284 
    285      The first section contains a prompt and a command-line (or the
    286      first part of a command-line when it is too long to be fit in the
    287      screen). So, in the first section, the number of command-line
    288      characters displayed is 78 minus the length of the prompt (or
    289      less). If the command-line has more characters, `>' is put at the
    290      position 78 (zero-origin), to inform the user of the hidden
    291      characters.
    292 
    293      Other sections always have `<' at the first position, since there
    294      is absolutely a section before each section. If there is a section
    295      after another section, this section consists of 77 characters and
    296      `>' at the last position. The last section has 77 or less
    297      characters and doesn't have `>'.
    298 
    299      Each section other than the last shares some characters with the
    300      previous section. This region is called ``margin''. If the cursor
    301      is put at the magin which is shared by the first section and the
    302      second, the first section is displayed. Otherwise, a displayed
    303      section is switched to another section, only if the cursor is put
    304      outside that section.  */
    305 
    306   /* XXX: These should be defined in shared.h, but I leave these here,
    307      until this code is freezed.  */
    308 #define CMDLINE_WIDTH	78
    309 #define CMDLINE_MARGIN	10
    310 
    311   int xpos, lpos, c, section;
    312   /* The length of PROMPT.  */
    313   int plen;
    314   /* The length of the command-line.  */
    315   int llen;
    316   /* The index for the history.  */
    317   int history = -1;
    318   /* The working buffer for the command-line.  */
    319   char *buf = (char *) CMDLINE_BUF;
    320   /* The kill buffer.  */
    321   char *kill_buf = (char *) KILL_BUF;
    322 
    323   /* Nested function definitions for code simplicity.  */
    324 
    325   /* The forward declarations of nested functions are prefixed
    326      with `auto'.  */
    327   auto void cl_refresh (int full, int len);
    328   auto void cl_backward (int count);
    329   auto void cl_forward (int count);
    330   auto void cl_insert (const char *str);
    331   auto void cl_delete (int count);
    332   auto void cl_init (void);
    333 
    334   /* Move the cursor backward.  */
    335   void cl_backward (int count)
    336     {
    337       lpos -= count;
    338 
    339       /* If the cursor is in the first section, display the first section
    340 	 instead of the second.  */
    341       if (section == 1 && plen + lpos < CMDLINE_WIDTH)
    342 	cl_refresh (1, 0);
    343       else if (xpos - count < 1)
    344 	cl_refresh (1, 0);
    345       else
    346 	{
    347 	  xpos -= count;
    348 
    349 	  if (current_term->flags & TERM_DUMB)
    350 	    {
    351 	      int i;
    352 
    353 	      for (i = 0; i < count; i++)
    354 		grub_putchar ('\b');
    355 	    }
    356 	  else
    357 	    gotoxy (xpos, getxy () & 0xFF);
    358 	}
    359     }
    360 
    361   /* Move the cursor forward.  */
    362   void cl_forward (int count)
    363     {
    364       lpos += count;
    365 
    366       /* If the cursor goes outside, scroll the screen to the right.  */
    367       if (xpos + count >= CMDLINE_WIDTH)
    368 	cl_refresh (1, 0);
    369       else
    370 	{
    371 	  xpos += count;
    372 
    373 	  if (current_term->flags & TERM_DUMB)
    374 	    {
    375 	      int i;
    376 
    377 	      for (i = lpos - count; i < lpos; i++)
    378 		{
    379 		  if (! echo_char)
    380 		    grub_putchar (buf[i]);
    381 		  else
    382 		    grub_putchar (echo_char);
    383 		}
    384 	    }
    385 	  else
    386 	    gotoxy (xpos, getxy () & 0xFF);
    387 	}
    388     }
    389 
    390   /* Refresh the screen. If FULL is true, redraw the full line, otherwise,
    391      only LEN characters from LPOS.  */
    392   void cl_refresh (int full, int len)
    393     {
    394       int i;
    395       int start;
    396       int pos = xpos;
    397 
    398       if (full)
    399 	{
    400 	  /* Recompute the section number.  */
    401 	  if (lpos + plen < CMDLINE_WIDTH)
    402 	    section = 0;
    403 	  else
    404 	    section = ((lpos + plen - CMDLINE_WIDTH)
    405 		       / (CMDLINE_WIDTH - 1 - CMDLINE_MARGIN) + 1);
    406 
    407 	  /* From the start to the end.  */
    408 	  len = CMDLINE_WIDTH;
    409 	  pos = 0;
    410 	  grub_putchar ('\r');
    411 
    412 	  /* If SECTION is the first section, print the prompt, otherwise,
    413 	     print `<'.  */
    414 	  if (section == 0)
    415 	    {
    416 	      grub_printf ("%s", prompt);
    417 	      len -= plen;
    418 	      pos += plen;
    419 	    }
    420 	  else
    421 	    {
    422 	      grub_putchar ('<');
    423 	      len--;
    424 	      pos++;
    425 	    }
    426 	}
    427 
    428       /* Compute the index to start writing BUF and the resulting position
    429 	 on the screen.  */
    430       if (section == 0)
    431 	{
    432 	  int offset = 0;
    433 
    434 	  if (! full)
    435 	    offset = xpos - plen;
    436 
    437 	  start = 0;
    438 	  xpos = lpos + plen;
    439 	  start += offset;
    440 	}
    441       else
    442 	{
    443 	  int offset = 0;
    444 
    445 	  if (! full)
    446 	    offset = xpos - 1;
    447 
    448 	  start = ((section - 1) * (CMDLINE_WIDTH - 1 - CMDLINE_MARGIN)
    449 		   + CMDLINE_WIDTH - plen - CMDLINE_MARGIN);
    450 	  xpos = lpos + 1 - start;
    451 	  start += offset;
    452 	}
    453 
    454       /* Print BUF. If ECHO_CHAR is not zero, put it instead.  */
    455       for (i = start; i < start + len && i < llen; i++)
    456 	{
    457 	  if (! echo_char)
    458 	    grub_putchar (buf[i]);
    459 	  else
    460 	    grub_putchar (echo_char);
    461 
    462 	  pos++;
    463 	}
    464 
    465       /* Fill up the rest of the line with spaces.  */
    466       for (; i < start + len; i++)
    467 	{
    468 	  grub_putchar (' ');
    469 	  pos++;
    470 	}
    471 
    472       /* If the cursor is at the last position, put `>' or a space,
    473 	 depending on if there are more characters in BUF.  */
    474       if (pos == CMDLINE_WIDTH)
    475 	{
    476 	  if (start + len < llen)
    477 	    grub_putchar ('>');
    478 	  else
    479 	    grub_putchar (' ');
    480 
    481 	  pos++;
    482 	}
    483 
    484       /* Back to XPOS.  */
    485       if (current_term->flags & TERM_DUMB)
    486 	{
    487 	  for (i = 0; i < pos - xpos; i++)
    488 	    grub_putchar ('\b');
    489 	}
    490       else
    491 	gotoxy (xpos, getxy () & 0xFF);
    492     }
    493 
    494   /* Initialize the command-line.  */
    495   void cl_init (void)
    496     {
    497       /* Distinguish us from other lines and error messages!  */
    498       grub_putchar ('\n');
    499 
    500       /* Print full line and set position here.  */
    501       cl_refresh (1, 0);
    502     }
    503 
    504   /* Insert STR to BUF.  */
    505   void cl_insert (const char *str)
    506     {
    507       int l = grub_strlen (str);
    508 
    509       if (llen + l < maxlen)
    510 	{
    511 	  if (lpos == llen)
    512 	    grub_memmove (buf + lpos, str, l + 1);
    513 	  else
    514 	    {
    515 	      grub_memmove (buf + lpos + l, buf + lpos, llen - lpos + 1);
    516 	      grub_memmove (buf + lpos, str, l);
    517 	    }
    518 
    519 	  llen += l;
    520 	  lpos += l;
    521 	  if (xpos + l >= CMDLINE_WIDTH)
    522 	    cl_refresh (1, 0);
    523 	  else if (xpos + l + llen - lpos > CMDLINE_WIDTH)
    524 	    cl_refresh (0, CMDLINE_WIDTH - xpos);
    525 	  else
    526 	    cl_refresh (0, l + llen - lpos);
    527 	}
    528     }
    529 
    530   /* Delete COUNT characters in BUF.  */
    531   void cl_delete (int count)
    532     {
    533       grub_memmove (buf + lpos, buf + lpos + count, llen - count + 1);
    534       llen -= count;
    535 
    536       if (xpos + llen + count - lpos > CMDLINE_WIDTH)
    537 	cl_refresh (0, CMDLINE_WIDTH - xpos);
    538       else
    539 	cl_refresh (0, llen + count - lpos);
    540     }
    541 
    542   plen = grub_strlen (prompt);
    543   llen = grub_strlen (cmdline);
    544 
    545   if (maxlen > MAX_CMDLINE)
    546     {
    547       maxlen = MAX_CMDLINE;
    548       if (llen >= MAX_CMDLINE)
    549 	{
    550 	  llen = MAX_CMDLINE - 1;
    551 	  cmdline[MAX_CMDLINE] = 0;
    552 	}
    553     }
    554   lpos = llen;
    555   grub_strcpy (buf, cmdline);
    556 
    557   cl_init ();
    558 
    559   while ((c = ASCII_CHAR (getkey ())) != '\n' && c != '\r')
    560     {
    561       /* If READLINE is non-zero, handle readline-like key bindings.  */
    562       if (readline)
    563 	{
    564 	  switch (c)
    565 	    {
    566 	    case 9:		/* TAB lists completions */
    567 	      {
    568 		int i;
    569 		/* POS points to the first space after a command.  */
    570 		int pos = 0;
    571 		int ret;
    572 		char *completion_buffer = (char *) COMPLETION_BUF;
    573 		int equal_pos = -1;
    574 		int is_filename;
    575 
    576 		/* Find the first word.  */
    577 		while (buf[pos] == ' ')
    578 		  pos++;
    579 		while (buf[pos] && buf[pos] != '=' && buf[pos] != ' ')
    580 		  pos++;
    581 
    582 		is_filename = (lpos > pos);
    583 
    584 		/* Find the position of the equal character after a
    585 		   command, and replace it with a space.  */
    586 		for (i = pos; buf[i] && buf[i] != ' '; i++)
    587 		  if (buf[i] == '=')
    588 		    {
    589 		      equal_pos = i;
    590 		      buf[i] = ' ';
    591 		      break;
    592 		    }
    593 
    594 		/* Find the position of the first character in this
    595 		   word.  */
    596 		for (i = lpos; i > 0 && buf[i - 1] != ' '; i--)
    597 		  ;
    598 
    599 		/* Invalidate the cache, because the user may exchange
    600 		   removable disks.  */
    601 		buf_drive = -1;
    602 
    603 		/* Copy this word to COMPLETION_BUFFER and do the
    604 		   completion.  */
    605 		grub_memmove (completion_buffer, buf + i, lpos - i);
    606 		completion_buffer[lpos - i] = 0;
    607 		ret = print_completions (is_filename, 1);
    608 		errnum = ERR_NONE;
    609 
    610 		if (ret >= 0)
    611 		  {
    612 		    /* Found, so insert COMPLETION_BUFFER.  */
    613 		    cl_insert (completion_buffer + lpos - i);
    614 
    615 		    if (ret > 0)
    616 		      {
    617 			/* There are more than one candidates, so print
    618 			   the list.  */
    619 			grub_putchar ('\n');
    620 			print_completions (is_filename, 0);
    621 			errnum = ERR_NONE;
    622 		      }
    623 		  }
    624 
    625 		/* Restore the command-line.  */
    626 		if (equal_pos >= 0)
    627 		  buf[equal_pos] = '=';
    628 
    629 		if (ret)
    630 		  cl_init ();
    631 	      }
    632 	      break;
    633 	    case 1:		/* C-a go to beginning of line */
    634 	      cl_backward (lpos);
    635 	      break;
    636 	    case 5:		/* C-e go to end of line */
    637 	      cl_forward (llen - lpos);
    638 	      break;
    639 	    case 6:		/* C-f forward one character */
    640 	      if (lpos < llen)
    641 		cl_forward (1);
    642 	      break;
    643 	    case 2:		/* C-b backward one character */
    644 	      if (lpos > 0)
    645 		cl_backward (1);
    646 	      break;
    647 	    case 21:		/* C-u kill to beginning of line */
    648 	      if (lpos == 0)
    649 		break;
    650 	      /* Copy the string being deleted to KILL_BUF.  */
    651 	      grub_memmove (kill_buf, buf, lpos);
    652 	      kill_buf[lpos] = 0;
    653 	      {
    654 		/* XXX: Not very clever.  */
    655 
    656 		int count = lpos;
    657 
    658 		cl_backward (lpos);
    659 		cl_delete (count);
    660 	      }
    661 	      break;
    662 	    case 11:		/* C-k kill to end of line */
    663 	      if (lpos == llen)
    664 		break;
    665 	      /* Copy the string being deleted to KILL_BUF.  */
    666 	      grub_memmove (kill_buf, buf + lpos, llen - lpos + 1);
    667 	      cl_delete (llen - lpos);
    668 	      break;
    669 	    case 25:		/* C-y yank the kill buffer */
    670 	      cl_insert (kill_buf);
    671 	      break;
    672 	    case 16:		/* C-p fetch the previous command */
    673 	      {
    674 		char *p;
    675 
    676 		if (history < 0)
    677 		  /* Save the working buffer.  */
    678 		  grub_strcpy (cmdline, buf);
    679 		else if (grub_strcmp (get_history (history), buf) != 0)
    680 		  /* If BUF is modified, add it into the history list.  */
    681 		  add_history (buf, history);
    682 
    683 		history++;
    684 		p = get_history (history);
    685 		if (! p)
    686 		  {
    687 		    history--;
    688 		    break;
    689 		  }
    690 
    691 		grub_strcpy (buf, p);
    692 		llen = grub_strlen (buf);
    693 		lpos = llen;
    694 		cl_refresh (1, 0);
    695 	      }
    696 	      break;
    697 	    case 14:		/* C-n fetch the next command */
    698 	      {
    699 		char *p;
    700 
    701 		if (history < 0)
    702 		  {
    703 		    break;
    704 		  }
    705 		else if (grub_strcmp (get_history (history), buf) != 0)
    706 		  /* If BUF is modified, add it into the history list.  */
    707 		  add_history (buf, history);
    708 
    709 		history--;
    710 		p = get_history (history);
    711 		if (! p)
    712 		  p = cmdline;
    713 
    714 		grub_strcpy (buf, p);
    715 		llen = grub_strlen (buf);
    716 		lpos = llen;
    717 		cl_refresh (1, 0);
    718 	      }
    719 	      break;
    720 	    }
    721 	}
    722 
    723       /* ESC, C-d and C-h are always handled. Actually C-d is not
    724 	 functional if READLINE is zero, as the cursor cannot go
    725 	 backward, but that's ok.  */
    726       switch (c)
    727 	{
    728 	case 27:	/* ESC immediately return 1 */
    729 	  return 1;
    730 	case 4:		/* C-d delete character under cursor */
    731 	  if (lpos == llen)
    732 	    break;
    733 	  cl_delete (1);
    734 	  break;
    735 	case 8:		/* C-h backspace */
    736 # ifdef GRUB_UTIL
    737 	case 127:	/* also backspace */
    738 # endif
    739 	  if (lpos > 0)
    740 	    {
    741 	      cl_backward (1);
    742 	      cl_delete (1);
    743 	    }
    744 	  break;
    745 	default:		/* insert printable character into line */
    746 	  if (c >= ' ' && c <= '~')
    747 	    {
    748 	      char str[2];
    749 
    750 	      str[0] = c;
    751 	      str[1] = 0;
    752 	      cl_insert (str);
    753 	    }
    754 	}
    755     }
    756 
    757   grub_putchar ('\n');
    758 
    759   /* If ECHO_CHAR is NUL, remove the leading spaces.  */
    760   lpos = 0;
    761   if (! echo_char)
    762     while (buf[lpos] == ' ')
    763       lpos++;
    764 
    765   /* Copy the working buffer to CMDLINE.  */
    766   grub_memmove (cmdline, buf + lpos, llen - lpos + 1);
    767 
    768   /* If the readline-like feature is turned on and CMDLINE is not
    769      empty, add it into the history list.  */
    770   if (readline && lpos < llen)
    771     add_history (cmdline, 0);
    772 
    773   return 0;
    774 }
    775 
    776 /* Don't use this with a MAXLEN greater than 1600 or so!  The problem
    777    is that GET_CMDLINE depends on the everything fitting on the screen
    778    at once.  So, the whole screen is about 2000 characters, minus the
    779    PROMPT, and space for error and status lines, etc.  MAXLEN must be
    780    at least 1, and PROMPT and CMDLINE must be valid strings (not NULL
    781    or zero-length).
    782 
    783    If ECHO_CHAR is nonzero, echo it instead of the typed character. */
    784 int
    785 get_cmdline (char *prompt, char *cmdline, int maxlen,
    786 	     int echo_char, int readline)
    787 {
    788   int old_cursor;
    789   int ret;
    790 
    791   old_cursor = setcursor (1);
    792 
    793   /* Because it is hard to deal with different conditions simultaneously,
    794      less functional cases are handled here. Assume that TERM_NO_ECHO
    795      implies TERM_NO_EDIT.  */
    796   if (current_term->flags & (TERM_NO_ECHO | TERM_NO_EDIT))
    797     {
    798       char *p = cmdline;
    799       int c;
    800 
    801       /* Make sure that MAXLEN is not too large.  */
    802       if (maxlen > MAX_CMDLINE)
    803 	maxlen = MAX_CMDLINE;
    804 
    805       /* Print only the prompt. The contents of CMDLINE is simply discarded,
    806 	 even if it is not empty.  */
    807       grub_printf ("%s", prompt);
    808 
    809       /* Gather characters until a newline is gotten.  */
    810       while ((c = ASCII_CHAR (getkey ())) != '\n' && c != '\r')
    811 	{
    812 	  /* Return immediately if ESC is pressed.  */
    813 	  if (c == 27)
    814 	    {
    815 	      setcursor (old_cursor);
    816 	      return 1;
    817 	    }
    818 
    819 	  /* Printable characters are added into CMDLINE.  */
    820 	  if (c >= ' ' && c <= '~')
    821 	    {
    822 	      if (! (current_term->flags & TERM_NO_ECHO))
    823 		grub_putchar (c);
    824 
    825 	      /* Preceding space characters must be ignored.  */
    826 	      if (c != ' ' || p != cmdline)
    827 		*p++ = c;
    828 	    }
    829 	}
    830 
    831       *p = 0;
    832 
    833       if (! (current_term->flags & TERM_NO_ECHO))
    834 	grub_putchar ('\n');
    835 
    836       setcursor (old_cursor);
    837       return 0;
    838     }
    839 
    840   /* Complicated features are left to real_get_cmdline.  */
    841   ret = real_get_cmdline (prompt, cmdline, maxlen, echo_char, readline);
    842   setcursor (old_cursor);
    843   return ret;
    844 }
    845 
    846 int
    847 safe_parse_maxint (char **str_ptr, int *myint_ptr)
    848 {
    849   char *ptr = *str_ptr;
    850   int myint = 0;
    851   int mult = 10, found = 0;
    852 
    853   /*
    854    *  Is this a hex number?
    855    */
    856   if (*ptr == '0' && tolower (*(ptr + 1)) == 'x')
    857     {
    858       ptr += 2;
    859       mult = 16;
    860     }
    861 
    862   while (1)
    863     {
    864       /* A bit tricky. This below makes use of the equivalence:
    865 	 (A >= B && A <= C) <=> ((A - B) <= (C - B))
    866 	 when C > B and A is unsigned.  */
    867       unsigned int digit;
    868 
    869       digit = tolower (*ptr) - '0';
    870       if (digit > 9)
    871 	{
    872 	  digit -= 'a' - '0';
    873 	  if (mult == 10 || digit > 5)
    874 	    break;
    875 	  digit += 10;
    876 	}
    877 
    878       found = 1;
    879       if (myint > ((MAXINT - digit) / mult))
    880 	{
    881 	  errnum = ERR_NUMBER_OVERFLOW;
    882 	  return 0;
    883 	}
    884       myint = (myint * mult) + digit;
    885       ptr++;
    886     }
    887 
    888   if (!found)
    889     {
    890       errnum = ERR_NUMBER_PARSING;
    891       return 0;
    892     }
    893 
    894   *str_ptr = ptr;
    895   *myint_ptr = myint;
    896 
    897   return 1;
    898 }
    899 #endif /* STAGE1_5 */
    900 
    901 #if !defined(STAGE1_5) || defined(FSYS_FAT)
    902 int
    903 grub_tolower (int c)
    904 {
    905   if (c >= 'A' && c <= 'Z')
    906     return (c + ('a' - 'A'));
    907 
    908   return c;
    909 }
    910 #endif /* ! STAGE1_5 || FSYS_FAT */
    911 
    912 int
    913 grub_isspace (int c)
    914 {
    915   switch (c)
    916     {
    917     case ' ':
    918     case '\t':
    919     case '\r':
    920     case '\n':
    921       return 1;
    922     default:
    923       break;
    924     }
    925 
    926   return 0;
    927 }
    928 
    929 #if !defined(STAGE1_5) || defined(FSYS_ISO9660)
    930 int
    931 grub_memcmp (const char *s1, const char *s2, int n)
    932 {
    933   while (n)
    934     {
    935       if (*s1 < *s2)
    936 	return -1;
    937       else if (*s1 > *s2)
    938 	return 1;
    939       s1++;
    940       s2++;
    941       n--;
    942     }
    943 
    944   return 0;
    945 }
    946 #endif /* ! STAGE1_5 || FSYS_ISO9660 */
    947 
    948 #ifndef STAGE1_5
    949 int
    950 grub_strncat (char *s1, const char *s2, int n)
    951 {
    952   int i = -1;
    953 
    954   while (++i < n && s1[i] != 0);
    955 
    956   while (i < n && (s1[i++] = *(s2++)) != 0);
    957 
    958   s1[n - 1] = 0;
    959 
    960   if (i >= n)
    961     return 0;
    962 
    963   s1[i] = 0;
    964 
    965   return 1;
    966 }
    967 #endif /* ! STAGE1_5 */
    968 
    969 /* XXX: This below is an evil hack. Certainly, we should change the
    970    strategy to determine what should be defined and what shouldn't be
    971    defined for each image. For example, it would be better to create
    972    a static library supporting minimal standard C functions and link
    973    each image with the library. Complicated things should be left to
    974    computer, definitely. -okuji  */
    975 #if !defined(STAGE1_5) || defined(FSYS_VSTAFS)
    976 int
    977 grub_strcmp (const char *s1, const char *s2)
    978 {
    979   while (*s1 || *s2)
    980     {
    981       if (*s1 < *s2)
    982 	return -1;
    983       else if (*s1 > *s2)
    984 	return 1;
    985       s1 ++;
    986       s2 ++;
    987     }
    988 
    989   return 0;
    990 }
    991 #endif /* ! STAGE1_5 || FSYS_VSTAFS */
    992 
    993 #ifndef STAGE1_5
    994 /* Wait for a keypress and return its code.  */
    995 int
    996 getkey (void)
    997 {
    998   return current_term->getkey ();
    999 }
   1000 
   1001 /* Check if a key code is available.  */
   1002 int
   1003 checkkey (void)
   1004 {
   1005   return current_term->checkkey ();
   1006 }
   1007 #endif /* ! STAGE1_5 */
   1008 
   1009 /* Display an ASCII character.  */
   1010 void
   1011 grub_putchar (int c)
   1012 {
   1013   if (c == '\n')
   1014     grub_putchar ('\r');
   1015 #ifndef STAGE1_5
   1016   else if (c == '\t' && current_term->getxy)
   1017     {
   1018       int n;
   1019 
   1020       n = 8 - ((current_term->getxy () >> 8) & 3);
   1021       while (n--)
   1022 	grub_putchar (' ');
   1023 
   1024       return;
   1025     }
   1026 #endif /* ! STAGE1_5 */
   1027 
   1028 #ifdef STAGE1_5
   1029 
   1030   /* In Stage 1.5, only the normal console is supported.  */
   1031   console_putchar (c);
   1032 
   1033 #else /* ! STAGE1_5 */
   1034 
   1035   if (c == '\n')
   1036     {
   1037       /* Internal `more'-like feature.  */
   1038       if (count_lines >= 0)
   1039 	{
   1040 	  count_lines++;
   1041 	  if (count_lines >= max_lines - 2)
   1042 	    {
   1043 	      int tmp;
   1044 
   1045 	      /* It's important to disable the feature temporarily, because
   1046 		 the following grub_printf call will print newlines.  */
   1047 	      count_lines = -1;
   1048 
   1049 	      if (current_term->setcolorstate)
   1050 		current_term->setcolorstate (COLOR_STATE_HIGHLIGHT);
   1051 
   1052 	      grub_printf ("\n[Hit return to continue]");
   1053 
   1054 	      if (current_term->setcolorstate)
   1055 		current_term->setcolorstate (COLOR_STATE_NORMAL);
   1056 
   1057 	      do
   1058 		{
   1059 		  tmp = ASCII_CHAR (getkey ());
   1060 		}
   1061 	      while (tmp != '\n' && tmp != '\r');
   1062 	      grub_printf ("\r                        \r");
   1063 
   1064 	      /* Restart to count lines.  */
   1065 	      count_lines = 0;
   1066 	      return;
   1067 	    }
   1068 	}
   1069     }
   1070 
   1071   current_term->putchar (c);
   1072 
   1073 #endif /* ! STAGE1_5 */
   1074 }
   1075 
   1076 #ifndef STAGE1_5
   1077 void
   1078 gotoxy (int x, int y)
   1079 {
   1080   current_term->gotoxy (x, y);
   1081 }
   1082 
   1083 int
   1084 getxy (void)
   1085 {
   1086   return current_term->getxy ();
   1087 }
   1088 
   1089 void
   1090 cls (void)
   1091 {
   1092   /* If the terminal is dumb, there is no way to clean the terminal.  */
   1093   if (current_term->flags & TERM_DUMB)
   1094     grub_putchar ('\n');
   1095   else
   1096     current_term->cls ();
   1097 }
   1098 
   1099 int
   1100 setcursor (int on)
   1101 {
   1102   if (current_term->setcursor)
   1103     return current_term->setcursor (on);
   1104 
   1105   return 1;
   1106 }
   1107 #endif /* ! STAGE1_5 */
   1108 
   1109 int
   1110 substring (const char *s1, const char *s2)
   1111 {
   1112   while (*s1 == *s2)
   1113     {
   1114       /* The strings match exactly. */
   1115       if (! *(s1++))
   1116 	return 0;
   1117       s2 ++;
   1118     }
   1119 
   1120   /* S1 is a substring of S2. */
   1121   if (*s1 == 0)
   1122     return -1;
   1123 
   1124   /* S1 isn't a substring. */
   1125   return 1;
   1126 }
   1127 
   1128 #ifndef STAGE1_5
   1129 /* Terminate the string STR with NUL.  */
   1130 int
   1131 nul_terminate (char *str)
   1132 {
   1133   int ch;
   1134 
   1135   while (*str && ! grub_isspace (*str))
   1136     str++;
   1137 
   1138   ch = *str;
   1139   *str = 0;
   1140   return ch;
   1141 }
   1142 
   1143 char *
   1144 grub_strstr (const char *s1, const char *s2)
   1145 {
   1146   while (*s1)
   1147     {
   1148       const char *ptr, *tmp;
   1149 
   1150       ptr = s1;
   1151       tmp = s2;
   1152 
   1153       while (*tmp && *ptr == *tmp)
   1154 	ptr++, tmp++;
   1155 
   1156       if (tmp > s2 && ! *tmp)
   1157 	return (char *) s1;
   1158 
   1159       s1++;
   1160     }
   1161 
   1162   return 0;
   1163 }
   1164 
   1165 int
   1166 grub_strlen (const char *str)
   1167 {
   1168   int len = 0;
   1169 
   1170   while (*str++)
   1171     len++;
   1172 
   1173   return len;
   1174 }
   1175 #endif /* ! STAGE1_5 */
   1176 
   1177 int
   1178 memcheck (int addr, int len)
   1179 {
   1180 #ifdef GRUB_UTIL
   1181   auto int start_addr (void);
   1182   auto int end_addr (void);
   1183 
   1184   auto int start_addr (void)
   1185     {
   1186       int ret;
   1187 # if defined(HAVE_START_SYMBOL)
   1188       asm volatile ("movl	$start, %0" : "=a" (ret));
   1189 # elif defined(HAVE_USCORE_START_SYMBOL)
   1190       asm volatile ("movl	$_start, %0" : "=a" (ret));
   1191 # endif
   1192       return ret;
   1193     }
   1194 
   1195   auto int end_addr (void)
   1196     {
   1197       int ret;
   1198 # if defined(HAVE_END_SYMBOL)
   1199       asm volatile ("movl	$end, %0" : "=a" (ret));
   1200 # elif defined(HAVE_USCORE_END_SYMBOL)
   1201       asm volatile ("movl	$_end, %0" : "=a" (ret));
   1202 # endif
   1203       return ret;
   1204     }
   1205 
   1206   if (start_addr () <= addr && end_addr () > addr + len)
   1207     return ! errnum;
   1208 #endif /* GRUB_UTIL */
   1209 
   1210   if ((addr < RAW_ADDR (0x1000))
   1211       || (addr < RAW_ADDR (0x100000)
   1212 	  && RAW_ADDR (mbi.mem_lower * 1024) < (addr + len))
   1213       || (addr >= RAW_ADDR (0x100000)
   1214 	  && RAW_ADDR (mbi.mem_upper * 1024) < ((addr - 0x100000) + len)))
   1215     errnum = ERR_WONT_FIT;
   1216 
   1217   return ! errnum;
   1218 }
   1219 
   1220 void *
   1221 grub_memmove (void *to, const void *from, int len)
   1222 {
   1223    if (memcheck ((int) to, len))
   1224      {
   1225        /* This assembly code is stolen from
   1226 	  linux-2.2.2/include/asm-i386/string.h. This is not very fast
   1227 	  but compact.  */
   1228        int d0, d1, d2;
   1229 
   1230        if (to < from)
   1231 	 {
   1232 	   asm volatile ("cld\n\t"
   1233 			 "rep\n\t"
   1234 			 "movsb"
   1235 			 : "=&c" (d0), "=&S" (d1), "=&D" (d2)
   1236 			 : "0" (len),"1" (from),"2" (to)
   1237 			 : "memory");
   1238 	 }
   1239        else
   1240 	 {
   1241 	   asm volatile ("std\n\t"
   1242 			 "rep\n\t"
   1243 			 "movsb\n\t"
   1244 			 "cld"
   1245 			 : "=&c" (d0), "=&S" (d1), "=&D" (d2)
   1246 			 : "0" (len),
   1247 			 "1" (len - 1 + (const char *) from),
   1248 			 "2" (len - 1 + (char *) to)
   1249 			 : "memory");
   1250 	 }
   1251      }
   1252 
   1253    return errnum ? NULL : to;
   1254 }
   1255 
   1256 void *
   1257 grub_memset (void *start, int c, int len)
   1258 {
   1259   char *p = start;
   1260 
   1261   if (memcheck ((int) start, len))
   1262     {
   1263       while (len -- > 0)
   1264 	*p ++ = c;
   1265     }
   1266 
   1267   return errnum ? NULL : start;
   1268 }
   1269 
   1270 #ifndef STAGE1_5
   1271 char *
   1272 grub_strcpy (char *dest, const char *src)
   1273 {
   1274   grub_memmove (dest, src, grub_strlen (src) + 1);
   1275   return dest;
   1276 }
   1277 #endif /* ! STAGE1_5 */
   1278 
   1279 #ifndef GRUB_UTIL
   1280 # undef memcpy
   1281 /* GCC emits references to memcpy() for struct copies etc.  */
   1282 void *memcpy (void *dest, const void *src, int n)  __attribute__ ((alias ("grub_memmove")));
   1283 #endif
   1284