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