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 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 * 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 146 grub_putstr (const char *str) 147 { 148 while (*str) 149 grub_putchar (*str++); 150 } 151 152 void 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 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 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 * 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 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 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 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 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 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 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 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 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 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 996 getkey (void) 997 { 998 return current_term->getkey (); 999 } 1000 1001 /* Check if a key code is available. */ 1002 int 1003 checkkey (void) 1004 { 1005 return current_term->checkkey (); 1006 } 1007 #endif /* ! STAGE1_5 */ 1008 1009 /* Display an ASCII character. */ 1010 void 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 1078 gotoxy (int x, int y) 1079 { 1080 current_term->gotoxy (x, y); 1081 } 1082 1083 int 1084 getxy (void) 1085 { 1086 return current_term->getxy (); 1087 } 1088 1089 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 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 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 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 * 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 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 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 * 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 * 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 * 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