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