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