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
33struct 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.  */
84struct term_entry *current_term = term_table;
85
86int max_lines = 24;
87int count_lines = -1;
88int use_pager = 1;
89#endif
90
91void
92print_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
103char *
104convert_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
145void
146grub_putstr (const char *str)
147{
148  while (*str)
149    grub_putchar (*str++);
150}
151
152void
153grub_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
191int
192grub_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
236void
237init_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.  */
246static 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.  */
250static char *
251get_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.  */
260static void
261add_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
271static int
272real_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. */
784int
785get_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
846int
847safe_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)
902int
903grub_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
912int
913grub_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)
930int
931grub_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
949int
950grub_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)
976int
977grub_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.  */
995int
996getkey (void)
997{
998  return current_term->getkey ();
999}
1000
1001/* Check if a key code is available.  */
1002int
1003checkkey (void)
1004{
1005  return current_term->checkkey ();
1006}
1007#endif /* ! STAGE1_5 */
1008
1009/* Display an ASCII character.  */
1010void
1011grub_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
1077void
1078gotoxy (int x, int y)
1079{
1080  current_term->gotoxy (x, y);
1081}
1082
1083int
1084getxy (void)
1085{
1086  return current_term->getxy ();
1087}
1088
1089void
1090cls (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
1099int
1100setcursor (int on)
1101{
1102  if (current_term->setcursor)
1103    return current_term->setcursor (on);
1104
1105  return 1;
1106}
1107#endif /* ! STAGE1_5 */
1108
1109int
1110substring (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.  */
1130int
1131nul_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
1143char *
1144grub_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
1165int
1166grub_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
1177int
1178memcheck (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
1220void *
1221grub_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
1256void *
1257grub_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
1271char *
1272grub_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.  */
1282void *memcpy (void *dest, const void *src, int n)  __attribute__ ((alias ("grub_memmove")));
1283#endif
1284