• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
print_error(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 *
convert_to_ascii(char * buf,int c,...)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
grub_putstr(const char * str)146 grub_putstr (const char *str)
147 {
148   while (*str)
149     grub_putchar (*str++);
150 }
151 
152 void
grub_printf(const char * format,...)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
grub_sprintf(char * buffer,const char * format,...)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
init_page(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 *
get_history(int no)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
add_history(const char * cmdline,int no)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
real_get_cmdline(char * prompt,char * cmdline,int maxlen,int echo_char,int readline)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
get_cmdline(char * prompt,char * cmdline,int maxlen,int echo_char,int readline)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
safe_parse_maxint(char ** str_ptr,int * myint_ptr)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
grub_tolower(int c)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
grub_isspace(int c)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
grub_memcmp(const char * s1,const char * s2,int n)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
grub_strncat(char * s1,const char * s2,int n)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
grub_strcmp(const char * s1,const char * s2)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
getkey(void)996 getkey (void)
997 {
998   return current_term->getkey ();
999 }
1000 
1001 /* Check if a key code is available.  */
1002 int
checkkey(void)1003 checkkey (void)
1004 {
1005   return current_term->checkkey ();
1006 }
1007 #endif /* ! STAGE1_5 */
1008 
1009 /* Display an ASCII character.  */
1010 void
grub_putchar(int c)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
gotoxy(int x,int y)1078 gotoxy (int x, int y)
1079 {
1080   current_term->gotoxy (x, y);
1081 }
1082 
1083 int
getxy(void)1084 getxy (void)
1085 {
1086   return current_term->getxy ();
1087 }
1088 
1089 void
cls(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
setcursor(int on)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
substring(const char * s1,const char * s2)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
nul_terminate(char * str)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 *
grub_strstr(const char * s1,const char * s2)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
grub_strlen(const char * str)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
memcheck(int addr,int len)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 *
grub_memmove(void * to,const void * from,int len)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 *
grub_memset(void * start,int c,int len)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 *
grub_strcpy(char * dest,const char * src)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