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