xref: /illumos-gate/usr/src/grub/grub-0.97/stage2/char_io.c (revision 9890706ed36aa6bfca8ad283fbe4dac12ecd692b)
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
38 struct term_entry term_table[] =
39   {
40     {
41       "console",
42       0,
43       24,
44       console_putchar,
45       console_checkkey,
46       console_getkey,
47       console_getxy,
48       console_gotoxy,
49       console_cls,
50       console_setcolorstate,
51       console_setcolor,
52       console_setcursor,
53       0,
54       0
55     },
56 #ifdef SUPPORT_SERIAL
57     {
58       "serial",
59       /* A serial device must be initialized.  */
60       TERM_NEED_INIT,
61       24,
62       serial_putchar,
63       serial_checkkey,
64       serial_getkey,
65       serial_getxy,
66       serial_gotoxy,
67       serial_cls,
68       serial_setcolorstate,
69       0,
70       0,
71       0,
72       0
73     },
74     {
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.  */
131 struct term_entry *current_term = term_table;
132 
133 int max_lines = 24;
134 int count_lines = -1;
135 int use_pager = 1;
136 #endif
137 
138 void
print_error(void)139 print_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 
150 char *
convert_to_ascii(char * buf,int c,unsigned long long num)151 convert_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 
192 void
grub_putstr(const char * str)193 grub_putstr (const char *str)
194 {
195   while (*str)
196     grub_putchar (*str++);
197 }
198 
199 static void
grub_vprintf(const char * format,int * dataptr)200 grub_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
256 void
init_page(void)257 init_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.  */
266 static 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.  */
270 static char *
get_history(int no)271 get_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.  */
280 static void
add_history(const char * cmdline,int no)281 add_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 
291 static int
real_get_cmdline(char * prompt,char * cmdline,int maxlen,int echo_char,int readline)292 real_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. */
804 int
get_cmdline(char * prompt,char * cmdline,int maxlen,int echo_char,int readline)805 get_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 
866 int
safe_parse_maxint(char ** str_ptr,int * myint_ptr)867 safe_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)
922 static int
grub_vsprintf(char * buffer,const char * format,int * dataptr)923 grub_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 
999 int
grub_sprintf(char * buffer,const char * format,...)1000 grub_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 
1010 void
noisy_printf(const char * format,...)1011 noisy_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  */
1023 void
grub_printf(const char * format,...)1024 grub_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)
1050 int
grub_tolower(int c)1051 grub_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 
1060 int
grub_isspace(int c)1061 grub_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)
1078 int
grub_memcmp(const char * s1,const char * s2,int n)1079 grub_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
1097 int
grub_strncat(char * s1,const char * s2,int n)1098 grub_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)
1127 int
grub_strcmp(const char * s1,const char * s2)1128 grub_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 
1143 int
grub_strncmp(const char * s1,const char * s2,int n)1144 grub_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.  */
1159 int
getkey(void)1160 getkey (void)
1161 {
1162   return current_term->getkey ();
1163 }
1164 
1165 /* Check if a key code is available.  */
1166 int
checkkey(void)1167 checkkey (void)
1168 {
1169   return current_term->checkkey ();
1170 }
1171 #endif /* ! STAGE1_5 */
1172 
1173 /* Display an ASCII character.  */
1174 void
grub_putchar(int c)1175 grub_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
1242 void
gotoxy(int x,int y)1243 gotoxy (int x, int y)
1244 {
1245   current_term->gotoxy (x, y);
1246 }
1247 
1248 int
getxy(void)1249 getxy (void)
1250 {
1251   return current_term->getxy ();
1252 }
1253 
1254 void
cls(void)1255 cls (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 
1264 int
setcursor(int on)1265 setcursor (int on)
1266 {
1267   if (current_term->setcursor)
1268     return current_term->setcursor (on);
1269 
1270   return 1;
1271 }
1272 #endif /* ! STAGE1_5 */
1273 
1274 int
substring(const char * s1,const char * s2)1275 substring (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)
1294 char *
grub_strstr(const char * s1,const char * s2)1295 grub_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 
1316 int
grub_strlen(const char * str)1317 grub_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.  */
1330 int
nul_terminate(char * str)1331 nul_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 
1343 char *
grub_strchr(char * str,char c)1344 grub_strchr (char *str, char c)
1345 {
1346   for (; *str && (*str != c); str++);
1347 
1348   return (*str ? str : NULL);
1349 }
1350 #endif /* ! STAGE1_5 */
1351 
1352 int
memcheck(unsigned long addr,unsigned long len)1353 memcheck (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 
1398 void
grub_memcpy(void * dest,const void * src,int len)1399 grub_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 
1408 void *
grub_memmove(void * to,const void * from,int len)1409 grub_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 
1445 void *
grub_memset(void * start,int c,int len)1446 grub_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
1460 char *
grub_strcpy(char * dest,const char * src)1461 grub_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.  */
1471 void *memcpy (void *dest, const void *src, int n)  __attribute__ ((alias ("grub_memmove")));
1472 #endif
1473