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 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 * 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 177 grub_putstr (const char *str) 178 { 179 while (*str) 180 grub_putchar (*str++); 181 } 182 183 static void 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1104 getkey (void) 1105 { 1106 return current_term->getkey (); 1107 } 1108 1109 /* Check if a key code is available. */ 1110 int 1111 checkkey (void) 1112 { 1113 return current_term->checkkey (); 1114 } 1115 #endif /* ! STAGE1_5 */ 1116 1117 /* Display an ASCII character. */ 1118 void 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 1187 gotoxy (int x, int y) 1188 { 1189 current_term->gotoxy (x, y); 1190 } 1191 1192 int 1193 getxy (void) 1194 { 1195 return current_term->getxy (); 1196 } 1197 1198 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 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 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 * 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 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 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 * 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 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 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 * 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 * 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 * 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