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 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 * 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 192 grub_putstr (const char *str) 193 { 194 while (*str) 195 grub_putchar (*str++); 196 } 197 198 static void 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1119 getkey (void) 1120 { 1121 return current_term->getkey (); 1122 } 1123 1124 /* Check if a key code is available. */ 1125 int 1126 checkkey (void) 1127 { 1128 return current_term->checkkey (); 1129 } 1130 #endif /* ! STAGE1_5 */ 1131 1132 /* Display an ASCII character. */ 1133 void 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 1202 gotoxy (int x, int y) 1203 { 1204 current_term->gotoxy (x, y); 1205 } 1206 1207 int 1208 getxy (void) 1209 { 1210 return current_term->getxy (); 1211 } 1212 1213 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 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 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 * 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 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 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 * 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 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 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 * 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 * 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 * 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