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