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