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