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