1 /* 2 * (C) Copyright 2000 3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 * 5 * Add to readline cmdline-editing by 6 * (C) Copyright 2005 7 * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com> 8 * 9 * See file CREDITS for list of people who contributed to this 10 * project. 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License as 14 * published by the Free Software Foundation; either version 2 of 15 * the License, or (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program; if not, write to the Free Software 24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 25 * MA 02111-1307 USA 26 */ 27 28 /* #define DEBUG */ 29 30 #include <common.h> 31 #include <command.h> 32 #include <fdtdec.h> 33 #include <hush.h> 34 #include <malloc.h> 35 #include <menu.h> 36 #include <post.h> 37 #include <version.h> 38 #include <watchdog.h> 39 #include <linux/ctype.h> 40 41 DECLARE_GLOBAL_DATA_PTR; 42 43 /* 44 * Board-specific Platform code can reimplement show_boot_progress () if needed 45 */ 46 void inline __show_boot_progress (int val) {} 47 void show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progress"))); 48 49 #define MAX_DELAY_STOP_STR 32 50 51 #define DEBUG_PARSER 0 /* set to 1 to debug */ 52 53 #define debug_parser(fmt, args...) \ 54 debug_cond(DEBUG_PARSER, fmt, ##args) 55 56 #ifndef DEBUG_BOOTKEYS 57 #define DEBUG_BOOTKEYS 0 58 #endif 59 #define debug_bootkeys(fmt, args...) \ 60 debug_cond(DEBUG_BOOTKEYS, fmt, ##args) 61 62 char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ 63 64 static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen); 65 static const char erase_seq[] = "\b \b"; /* erase sequence */ 66 static const char tab_seq[] = " "; /* used to expand TABs */ 67 68 #ifdef CONFIG_BOOT_RETRY_TIME 69 static uint64_t endtime = 0; /* must be set, default is instant timeout */ 70 static int retry_time = -1; /* -1 so can call readline before main_loop */ 71 #endif 72 73 #define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk()) 74 75 #ifndef CONFIG_BOOT_RETRY_MIN 76 #define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME 77 #endif 78 79 #ifdef CONFIG_MODEM_SUPPORT 80 int do_mdm_init = 0; 81 extern void mdm_init(void); /* defined in board.c */ 82 #endif 83 84 /*************************************************************************** 85 * Watch for 'delay' seconds for autoboot stop or autoboot delay string. 86 * returns: 0 - no key string, allow autoboot 1 - got key string, abort 87 */ 88 #if defined(CONFIG_BOOTDELAY) 89 # if defined(CONFIG_AUTOBOOT_KEYED) 90 static int abortboot_keyed(int bootdelay) 91 { 92 int abort = 0; 93 uint64_t etime = endtick(bootdelay); 94 struct { 95 char* str; 96 u_int len; 97 int retry; 98 } 99 delaykey [] = { 100 { str: getenv ("bootdelaykey"), retry: 1 }, 101 { str: getenv ("bootdelaykey2"), retry: 1 }, 102 { str: getenv ("bootstopkey"), retry: 0 }, 103 { str: getenv ("bootstopkey2"), retry: 0 }, 104 }; 105 106 char presskey [MAX_DELAY_STOP_STR]; 107 u_int presskey_len = 0; 108 u_int presskey_max = 0; 109 u_int i; 110 111 #ifndef CONFIG_ZERO_BOOTDELAY_CHECK 112 if (bootdelay == 0) 113 return 0; 114 #endif 115 116 # ifdef CONFIG_AUTOBOOT_PROMPT 117 printf(CONFIG_AUTOBOOT_PROMPT); 118 # endif 119 120 # ifdef CONFIG_AUTOBOOT_DELAY_STR 121 if (delaykey[0].str == NULL) 122 delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR; 123 # endif 124 # ifdef CONFIG_AUTOBOOT_DELAY_STR2 125 if (delaykey[1].str == NULL) 126 delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2; 127 # endif 128 # ifdef CONFIG_AUTOBOOT_STOP_STR 129 if (delaykey[2].str == NULL) 130 delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR; 131 # endif 132 # ifdef CONFIG_AUTOBOOT_STOP_STR2 133 if (delaykey[3].str == NULL) 134 delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2; 135 # endif 136 137 for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { 138 delaykey[i].len = delaykey[i].str == NULL ? 139 0 : strlen (delaykey[i].str); 140 delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ? 141 MAX_DELAY_STOP_STR : delaykey[i].len; 142 143 presskey_max = presskey_max > delaykey[i].len ? 144 presskey_max : delaykey[i].len; 145 146 debug_bootkeys("%s key:<%s>\n", 147 delaykey[i].retry ? "delay" : "stop", 148 delaykey[i].str ? delaykey[i].str : "NULL"); 149 } 150 151 /* In order to keep up with incoming data, check timeout only 152 * when catch up. 153 */ 154 do { 155 if (tstc()) { 156 if (presskey_len < presskey_max) { 157 presskey [presskey_len ++] = getc(); 158 } 159 else { 160 for (i = 0; i < presskey_max - 1; i ++) 161 presskey [i] = presskey [i + 1]; 162 163 presskey [i] = getc(); 164 } 165 } 166 167 for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { 168 if (delaykey[i].len > 0 && 169 presskey_len >= delaykey[i].len && 170 memcmp (presskey + presskey_len - delaykey[i].len, 171 delaykey[i].str, 172 delaykey[i].len) == 0) { 173 debug_bootkeys("got %skey\n", 174 delaykey[i].retry ? "delay" : 175 "stop"); 176 177 # ifdef CONFIG_BOOT_RETRY_TIME 178 /* don't retry auto boot */ 179 if (! delaykey[i].retry) 180 retry_time = -1; 181 # endif 182 abort = 1; 183 } 184 } 185 } while (!abort && get_ticks() <= etime); 186 187 if (!abort) 188 debug_bootkeys("key timeout\n"); 189 190 #ifdef CONFIG_SILENT_CONSOLE 191 if (abort) 192 gd->flags &= ~GD_FLG_SILENT; 193 #endif 194 195 return abort; 196 } 197 198 # else /* !defined(CONFIG_AUTOBOOT_KEYED) */ 199 200 #ifdef CONFIG_MENUKEY 201 static int menukey = 0; 202 #endif 203 204 static int abortboot_normal(int bootdelay) 205 { 206 int abort = 0; 207 unsigned long ts; 208 209 #ifdef CONFIG_MENUPROMPT 210 printf(CONFIG_MENUPROMPT); 211 #else 212 if (bootdelay >= 0) 213 printf("Hit any key to stop autoboot: %2d ", bootdelay); 214 #endif 215 216 #if defined CONFIG_ZERO_BOOTDELAY_CHECK 217 /* 218 * Check if key already pressed 219 * Don't check if bootdelay < 0 220 */ 221 if (bootdelay >= 0) { 222 if (tstc()) { /* we got a key press */ 223 (void) getc(); /* consume input */ 224 puts ("\b\b\b 0"); 225 abort = 1; /* don't auto boot */ 226 } 227 } 228 #endif 229 230 while ((bootdelay > 0) && (!abort)) { 231 --bootdelay; 232 /* delay 1000 ms */ 233 ts = get_timer(0); 234 do { 235 if (tstc()) { /* we got a key press */ 236 abort = 1; /* don't auto boot */ 237 bootdelay = 0; /* no more delay */ 238 # ifdef CONFIG_MENUKEY 239 menukey = getc(); 240 # else 241 (void) getc(); /* consume input */ 242 # endif 243 break; 244 } 245 udelay(10000); 246 } while (!abort && get_timer(ts) < 1000); 247 248 printf("\b\b\b%2d ", bootdelay); 249 } 250 251 putc('\n'); 252 253 #ifdef CONFIG_SILENT_CONSOLE 254 if (abort) 255 gd->flags &= ~GD_FLG_SILENT; 256 #endif 257 258 return abort; 259 } 260 # endif /* CONFIG_AUTOBOOT_KEYED */ 261 262 static int abortboot(int bootdelay) 263 { 264 #ifdef CONFIG_AUTOBOOT_KEYED 265 return abortboot_keyed(bootdelay); 266 #else 267 return abortboot_normal(bootdelay); 268 #endif 269 } 270 #endif /* CONFIG_BOOTDELAY */ 271 272 /* 273 * Runs the given boot command securely. Specifically: 274 * - Doesn't run the command with the shell (run_command or parse_string_outer), 275 * since that's a lot of code surface that an attacker might exploit. 276 * Because of this, we don't do any argument parsing--the secure boot command 277 * has to be a full-fledged u-boot command. 278 * - Doesn't check for keypresses before booting, since that could be a 279 * security hole; also disables Ctrl-C. 280 * - Doesn't allow the command to return. 281 * 282 * Upon any failures, this function will drop into an infinite loop after 283 * printing the error message to console. 284 */ 285 286 #if defined(CONFIG_BOOTDELAY) && defined(CONFIG_OF_CONTROL) 287 static void secure_boot_cmd(char *cmd) 288 { 289 cmd_tbl_t *cmdtp; 290 int rc; 291 292 if (!cmd) { 293 printf("## Error: Secure boot command not specified\n"); 294 goto err; 295 } 296 297 /* Disable Ctrl-C just in case some command is used that checks it. */ 298 disable_ctrlc(1); 299 300 /* Find the command directly. */ 301 cmdtp = find_cmd(cmd); 302 if (!cmdtp) { 303 printf("## Error: \"%s\" not defined\n", cmd); 304 goto err; 305 } 306 307 /* Run the command, forcing no flags and faking argc and argv. */ 308 rc = (cmdtp->cmd)(cmdtp, 0, 1, &cmd); 309 310 /* Shouldn't ever return from boot command. */ 311 printf("## Error: \"%s\" returned (code %d)\n", cmd, rc); 312 313 err: 314 /* 315 * Not a whole lot to do here. Rebooting won't help much, since we'll 316 * just end up right back here. Just loop. 317 */ 318 hang(); 319 } 320 321 static void process_fdt_options(const void *blob) 322 { 323 ulong addr; 324 325 /* Add an env variable to point to a kernel payload, if available */ 326 addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0); 327 if (addr) 328 setenv_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); 329 330 /* Add an env variable to point to a root disk, if available */ 331 addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0); 332 if (addr) 333 setenv_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); 334 } 335 #endif /* CONFIG_OF_CONTROL */ 336 337 #ifdef CONFIG_BOOTDELAY 338 static void process_boot_delay(void) 339 { 340 #ifdef CONFIG_OF_CONTROL 341 char *env; 342 #endif 343 char *s; 344 int bootdelay; 345 #ifdef CONFIG_BOOTCOUNT_LIMIT 346 unsigned long bootcount = 0; 347 unsigned long bootlimit = 0; 348 #endif /* CONFIG_BOOTCOUNT_LIMIT */ 349 350 #ifdef CONFIG_BOOTCOUNT_LIMIT 351 bootcount = bootcount_load(); 352 bootcount++; 353 bootcount_store (bootcount); 354 setenv_ulong("bootcount", bootcount); 355 bootlimit = getenv_ulong("bootlimit", 10, 0); 356 #endif /* CONFIG_BOOTCOUNT_LIMIT */ 357 358 s = getenv ("bootdelay"); 359 bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; 360 361 debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); 362 363 #if defined(CONFIG_MENU_SHOW) 364 bootdelay = menu_show(bootdelay); 365 #endif 366 # ifdef CONFIG_BOOT_RETRY_TIME 367 init_cmd_timeout (); 368 # endif /* CONFIG_BOOT_RETRY_TIME */ 369 370 #ifdef CONFIG_POST 371 if (gd->flags & GD_FLG_POSTFAIL) { 372 s = getenv("failbootcmd"); 373 } 374 else 375 #endif /* CONFIG_POST */ 376 #ifdef CONFIG_BOOTCOUNT_LIMIT 377 if (bootlimit && (bootcount > bootlimit)) { 378 printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n", 379 (unsigned)bootlimit); 380 s = getenv ("altbootcmd"); 381 } 382 else 383 #endif /* CONFIG_BOOTCOUNT_LIMIT */ 384 s = getenv ("bootcmd"); 385 #ifdef CONFIG_OF_CONTROL 386 /* Allow the fdt to override the boot command */ 387 env = fdtdec_get_config_string(gd->fdt_blob, "bootcmd"); 388 if (env) 389 s = env; 390 391 process_fdt_options(gd->fdt_blob); 392 393 /* 394 * If the bootsecure option was chosen, use secure_boot_cmd(). 395 * Always use 'env' in this case, since bootsecure requres that the 396 * bootcmd was specified in the FDT too. 397 */ 398 if (fdtdec_get_config_int(gd->fdt_blob, "bootsecure", 0)) 399 secure_boot_cmd(env); 400 401 #endif /* CONFIG_OF_CONTROL */ 402 403 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); 404 405 if (bootdelay != -1 && s && !abortboot(bootdelay)) { 406 #ifdef CONFIG_AUTOBOOT_KEYED 407 int prev = disable_ctrlc(1); /* disable Control C checking */ 408 #endif 409 410 run_command_list(s, -1, 0); 411 412 #ifdef CONFIG_AUTOBOOT_KEYED 413 disable_ctrlc(prev); /* restore Control C checking */ 414 #endif 415 } 416 417 #ifdef CONFIG_MENUKEY 418 if (menukey == CONFIG_MENUKEY) { 419 s = getenv("menucmd"); 420 if (s) 421 run_command_list(s, -1, 0); 422 } 423 #endif /* CONFIG_MENUKEY */ 424 } 425 #endif /* CONFIG_BOOTDELAY */ 426 427 void main_loop(void) 428 { 429 #ifndef CONFIG_SYS_HUSH_PARSER 430 static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; 431 int len; 432 int rc = 1; 433 int flag; 434 #endif 435 #ifdef CONFIG_PREBOOT 436 char *p; 437 #endif 438 439 bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); 440 441 #ifdef CONFIG_MODEM_SUPPORT 442 debug("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); 443 if (do_mdm_init) { 444 char *str = strdup(getenv("mdm_cmd")); 445 setenv("preboot", str); /* set or delete definition */ 446 if (str != NULL) 447 free(str); 448 mdm_init(); /* wait for modem connection */ 449 } 450 #endif /* CONFIG_MODEM_SUPPORT */ 451 452 #ifdef CONFIG_VERSION_VARIABLE 453 { 454 setenv("ver", version_string); /* set version variable */ 455 } 456 #endif /* CONFIG_VERSION_VARIABLE */ 457 458 #ifdef CONFIG_SYS_HUSH_PARSER 459 u_boot_hush_start(); 460 #endif 461 462 #if defined(CONFIG_HUSH_INIT_VAR) 463 hush_init_var(); 464 #endif 465 466 #ifdef CONFIG_PREBOOT 467 p = getenv("preboot"); 468 if (p != NULL) { 469 # ifdef CONFIG_AUTOBOOT_KEYED 470 int prev = disable_ctrlc(1); /* disable Control C checking */ 471 # endif 472 473 run_command_list(p, -1, 0); 474 475 # ifdef CONFIG_AUTOBOOT_KEYED 476 disable_ctrlc(prev); /* restore Control C checking */ 477 # endif 478 } 479 #endif /* CONFIG_PREBOOT */ 480 481 #if defined(CONFIG_UPDATE_TFTP) 482 update_tftp(0UL); 483 #endif /* CONFIG_UPDATE_TFTP */ 484 485 #ifdef CONFIG_BOOTDELAY 486 process_boot_delay(); 487 #endif 488 /* 489 * Main Loop for Monitor Command Processing 490 */ 491 #ifdef CONFIG_SYS_HUSH_PARSER 492 parse_file_outer(); 493 /* This point is never reached */ 494 for (;;); 495 #else 496 for (;;) { 497 #ifdef CONFIG_BOOT_RETRY_TIME 498 if (rc >= 0) { 499 /* Saw enough of a valid command to 500 * restart the timeout. 501 */ 502 reset_cmd_timeout(); 503 } 504 #endif 505 len = readline (CONFIG_SYS_PROMPT); 506 507 flag = 0; /* assume no special flags for now */ 508 if (len > 0) 509 strcpy (lastcommand, console_buffer); 510 else if (len == 0) 511 flag |= CMD_FLAG_REPEAT; 512 #ifdef CONFIG_BOOT_RETRY_TIME 513 else if (len == -2) { 514 /* -2 means timed out, retry autoboot 515 */ 516 puts ("\nTimed out waiting for command\n"); 517 # ifdef CONFIG_RESET_TO_RETRY 518 /* Reinit board to run initialization code again */ 519 do_reset (NULL, 0, 0, NULL); 520 # else 521 return; /* retry autoboot */ 522 # endif 523 } 524 #endif 525 526 if (len == -1) 527 puts ("<INTERRUPT>\n"); 528 else 529 rc = run_command(lastcommand, flag); 530 531 if (rc <= 0) { 532 /* invalid command or not repeatable, forget it */ 533 lastcommand[0] = 0; 534 } 535 } 536 #endif /*CONFIG_SYS_HUSH_PARSER*/ 537 } 538 539 #ifdef CONFIG_BOOT_RETRY_TIME 540 /*************************************************************************** 541 * initialize command line timeout 542 */ 543 void init_cmd_timeout(void) 544 { 545 char *s = getenv ("bootretry"); 546 547 if (s != NULL) 548 retry_time = (int)simple_strtol(s, NULL, 10); 549 else 550 retry_time = CONFIG_BOOT_RETRY_TIME; 551 552 if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) 553 retry_time = CONFIG_BOOT_RETRY_MIN; 554 } 555 556 /*************************************************************************** 557 * reset command line timeout to retry_time seconds 558 */ 559 void reset_cmd_timeout(void) 560 { 561 endtime = endtick(retry_time); 562 } 563 #endif 564 565 #ifdef CONFIG_CMDLINE_EDITING 566 567 /* 568 * cmdline-editing related codes from vivi. 569 * Author: Janghoon Lyu <nandy@mizi.com> 570 */ 571 572 #define putnstr(str,n) do { \ 573 printf ("%.*s", (int)n, str); \ 574 } while (0) 575 576 #define CTL_CH(c) ((c) - 'a' + 1) 577 #define CTL_BACKSPACE ('\b') 578 #define DEL ((char)255) 579 #define DEL7 ((char)127) 580 #define CREAD_HIST_CHAR ('!') 581 582 #define getcmd_putch(ch) putc(ch) 583 #define getcmd_getch() getc() 584 #define getcmd_cbeep() getcmd_putch('\a') 585 586 #define HIST_MAX 20 587 #define HIST_SIZE CONFIG_SYS_CBSIZE 588 589 static int hist_max; 590 static int hist_add_idx; 591 static int hist_cur = -1; 592 static unsigned hist_num; 593 594 static char *hist_list[HIST_MAX]; 595 static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ 596 597 #define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1) 598 599 static void hist_init(void) 600 { 601 int i; 602 603 hist_max = 0; 604 hist_add_idx = 0; 605 hist_cur = -1; 606 hist_num = 0; 607 608 for (i = 0; i < HIST_MAX; i++) { 609 hist_list[i] = hist_lines[i]; 610 hist_list[i][0] = '\0'; 611 } 612 } 613 614 static void cread_add_to_hist(char *line) 615 { 616 strcpy(hist_list[hist_add_idx], line); 617 618 if (++hist_add_idx >= HIST_MAX) 619 hist_add_idx = 0; 620 621 if (hist_add_idx > hist_max) 622 hist_max = hist_add_idx; 623 624 hist_num++; 625 } 626 627 static char* hist_prev(void) 628 { 629 char *ret; 630 int old_cur; 631 632 if (hist_cur < 0) 633 return NULL; 634 635 old_cur = hist_cur; 636 if (--hist_cur < 0) 637 hist_cur = hist_max; 638 639 if (hist_cur == hist_add_idx) { 640 hist_cur = old_cur; 641 ret = NULL; 642 } else 643 ret = hist_list[hist_cur]; 644 645 return (ret); 646 } 647 648 static char* hist_next(void) 649 { 650 char *ret; 651 652 if (hist_cur < 0) 653 return NULL; 654 655 if (hist_cur == hist_add_idx) 656 return NULL; 657 658 if (++hist_cur > hist_max) 659 hist_cur = 0; 660 661 if (hist_cur == hist_add_idx) { 662 ret = ""; 663 } else 664 ret = hist_list[hist_cur]; 665 666 return (ret); 667 } 668 669 #ifndef CONFIG_CMDLINE_EDITING 670 static void cread_print_hist_list(void) 671 { 672 int i; 673 unsigned long n; 674 675 n = hist_num - hist_max; 676 677 i = hist_add_idx + 1; 678 while (1) { 679 if (i > hist_max) 680 i = 0; 681 if (i == hist_add_idx) 682 break; 683 printf("%s\n", hist_list[i]); 684 n++; 685 i++; 686 } 687 } 688 #endif /* CONFIG_CMDLINE_EDITING */ 689 690 #define BEGINNING_OF_LINE() { \ 691 while (num) { \ 692 getcmd_putch(CTL_BACKSPACE); \ 693 num--; \ 694 } \ 695 } 696 697 #define ERASE_TO_EOL() { \ 698 if (num < eol_num) { \ 699 printf("%*s", (int)(eol_num - num), ""); \ 700 do { \ 701 getcmd_putch(CTL_BACKSPACE); \ 702 } while (--eol_num > num); \ 703 } \ 704 } 705 706 #define REFRESH_TO_EOL() { \ 707 if (num < eol_num) { \ 708 wlen = eol_num - num; \ 709 putnstr(buf + num, wlen); \ 710 num = eol_num; \ 711 } \ 712 } 713 714 static void cread_add_char(char ichar, int insert, unsigned long *num, 715 unsigned long *eol_num, char *buf, unsigned long len) 716 { 717 unsigned long wlen; 718 719 /* room ??? */ 720 if (insert || *num == *eol_num) { 721 if (*eol_num > len - 1) { 722 getcmd_cbeep(); 723 return; 724 } 725 (*eol_num)++; 726 } 727 728 if (insert) { 729 wlen = *eol_num - *num; 730 if (wlen > 1) { 731 memmove(&buf[*num+1], &buf[*num], wlen-1); 732 } 733 734 buf[*num] = ichar; 735 putnstr(buf + *num, wlen); 736 (*num)++; 737 while (--wlen) { 738 getcmd_putch(CTL_BACKSPACE); 739 } 740 } else { 741 /* echo the character */ 742 wlen = 1; 743 buf[*num] = ichar; 744 putnstr(buf + *num, wlen); 745 (*num)++; 746 } 747 } 748 749 static void cread_add_str(char *str, int strsize, int insert, unsigned long *num, 750 unsigned long *eol_num, char *buf, unsigned long len) 751 { 752 while (strsize--) { 753 cread_add_char(*str, insert, num, eol_num, buf, len); 754 str++; 755 } 756 } 757 758 static int cread_line(const char *const prompt, char *buf, unsigned int *len, 759 int timeout) 760 { 761 unsigned long num = 0; 762 unsigned long eol_num = 0; 763 unsigned long wlen; 764 char ichar; 765 int insert = 1; 766 int esc_len = 0; 767 char esc_save[8]; 768 int init_len = strlen(buf); 769 int first = 1; 770 771 if (init_len) 772 cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); 773 774 while (1) { 775 #ifdef CONFIG_BOOT_RETRY_TIME 776 while (!tstc()) { /* while no incoming data */ 777 if (retry_time >= 0 && get_ticks() > endtime) 778 return (-2); /* timed out */ 779 WATCHDOG_RESET(); 780 } 781 #endif 782 if (first && timeout) { 783 uint64_t etime = endtick(timeout); 784 785 while (!tstc()) { /* while no incoming data */ 786 if (get_ticks() >= etime) 787 return -2; /* timed out */ 788 WATCHDOG_RESET(); 789 } 790 first = 0; 791 } 792 793 ichar = getcmd_getch(); 794 795 if ((ichar == '\n') || (ichar == '\r')) { 796 putc('\n'); 797 break; 798 } 799 800 /* 801 * handle standard linux xterm esc sequences for arrow key, etc. 802 */ 803 if (esc_len != 0) { 804 if (esc_len == 1) { 805 if (ichar == '[') { 806 esc_save[esc_len] = ichar; 807 esc_len = 2; 808 } else { 809 cread_add_str(esc_save, esc_len, insert, 810 &num, &eol_num, buf, *len); 811 esc_len = 0; 812 } 813 continue; 814 } 815 816 switch (ichar) { 817 818 case 'D': /* <- key */ 819 ichar = CTL_CH('b'); 820 esc_len = 0; 821 break; 822 case 'C': /* -> key */ 823 ichar = CTL_CH('f'); 824 esc_len = 0; 825 break; /* pass off to ^F handler */ 826 case 'H': /* Home key */ 827 ichar = CTL_CH('a'); 828 esc_len = 0; 829 break; /* pass off to ^A handler */ 830 case 'A': /* up arrow */ 831 ichar = CTL_CH('p'); 832 esc_len = 0; 833 break; /* pass off to ^P handler */ 834 case 'B': /* down arrow */ 835 ichar = CTL_CH('n'); 836 esc_len = 0; 837 break; /* pass off to ^N handler */ 838 default: 839 esc_save[esc_len++] = ichar; 840 cread_add_str(esc_save, esc_len, insert, 841 &num, &eol_num, buf, *len); 842 esc_len = 0; 843 continue; 844 } 845 } 846 847 switch (ichar) { 848 case 0x1b: 849 if (esc_len == 0) { 850 esc_save[esc_len] = ichar; 851 esc_len = 1; 852 } else { 853 puts("impossible condition #876\n"); 854 esc_len = 0; 855 } 856 break; 857 858 case CTL_CH('a'): 859 BEGINNING_OF_LINE(); 860 break; 861 case CTL_CH('c'): /* ^C - break */ 862 *buf = '\0'; /* discard input */ 863 return (-1); 864 case CTL_CH('f'): 865 if (num < eol_num) { 866 getcmd_putch(buf[num]); 867 num++; 868 } 869 break; 870 case CTL_CH('b'): 871 if (num) { 872 getcmd_putch(CTL_BACKSPACE); 873 num--; 874 } 875 break; 876 case CTL_CH('d'): 877 if (num < eol_num) { 878 wlen = eol_num - num - 1; 879 if (wlen) { 880 memmove(&buf[num], &buf[num+1], wlen); 881 putnstr(buf + num, wlen); 882 } 883 884 getcmd_putch(' '); 885 do { 886 getcmd_putch(CTL_BACKSPACE); 887 } while (wlen--); 888 eol_num--; 889 } 890 break; 891 case CTL_CH('k'): 892 ERASE_TO_EOL(); 893 break; 894 case CTL_CH('e'): 895 REFRESH_TO_EOL(); 896 break; 897 case CTL_CH('o'): 898 insert = !insert; 899 break; 900 case CTL_CH('x'): 901 case CTL_CH('u'): 902 BEGINNING_OF_LINE(); 903 ERASE_TO_EOL(); 904 break; 905 case DEL: 906 case DEL7: 907 case 8: 908 if (num) { 909 wlen = eol_num - num; 910 num--; 911 memmove(&buf[num], &buf[num+1], wlen); 912 getcmd_putch(CTL_BACKSPACE); 913 putnstr(buf + num, wlen); 914 getcmd_putch(' '); 915 do { 916 getcmd_putch(CTL_BACKSPACE); 917 } while (wlen--); 918 eol_num--; 919 } 920 break; 921 case CTL_CH('p'): 922 case CTL_CH('n'): 923 { 924 char * hline; 925 926 esc_len = 0; 927 928 if (ichar == CTL_CH('p')) 929 hline = hist_prev(); 930 else 931 hline = hist_next(); 932 933 if (!hline) { 934 getcmd_cbeep(); 935 continue; 936 } 937 938 /* nuke the current line */ 939 /* first, go home */ 940 BEGINNING_OF_LINE(); 941 942 /* erase to end of line */ 943 ERASE_TO_EOL(); 944 945 /* copy new line into place and display */ 946 strcpy(buf, hline); 947 eol_num = strlen(buf); 948 REFRESH_TO_EOL(); 949 continue; 950 } 951 #ifdef CONFIG_AUTO_COMPLETE 952 case '\t': { 953 int num2, col; 954 955 /* do not autocomplete when in the middle */ 956 if (num < eol_num) { 957 getcmd_cbeep(); 958 break; 959 } 960 961 buf[num] = '\0'; 962 col = strlen(prompt) + eol_num; 963 num2 = num; 964 if (cmd_auto_complete(prompt, buf, &num2, &col)) { 965 col = num2 - num; 966 num += col; 967 eol_num += col; 968 } 969 break; 970 } 971 #endif 972 default: 973 cread_add_char(ichar, insert, &num, &eol_num, buf, *len); 974 break; 975 } 976 } 977 *len = eol_num; 978 buf[eol_num] = '\0'; /* lose the newline */ 979 980 if (buf[0] && buf[0] != CREAD_HIST_CHAR) 981 cread_add_to_hist(buf); 982 hist_cur = hist_add_idx; 983 984 return 0; 985 } 986 987 #endif /* CONFIG_CMDLINE_EDITING */ 988 989 /****************************************************************************/ 990 991 /* 992 * Prompt for input and read a line. 993 * If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0, 994 * time out when time goes past endtime (timebase time in ticks). 995 * Return: number of read characters 996 * -1 if break 997 * -2 if timed out 998 */ 999 int readline (const char *const prompt) 1000 { 1001 /* 1002 * If console_buffer isn't 0-length the user will be prompted to modify 1003 * it instead of entering it from scratch as desired. 1004 */ 1005 console_buffer[0] = '\0'; 1006 1007 return readline_into_buffer(prompt, console_buffer, 0); 1008 } 1009 1010 1011 int readline_into_buffer(const char *const prompt, char *buffer, int timeout) 1012 { 1013 char *p = buffer; 1014 #ifdef CONFIG_CMDLINE_EDITING 1015 unsigned int len = CONFIG_SYS_CBSIZE; 1016 int rc; 1017 static int initted = 0; 1018 1019 /* 1020 * History uses a global array which is not 1021 * writable until after relocation to RAM. 1022 * Revert to non-history version if still 1023 * running from flash. 1024 */ 1025 if (gd->flags & GD_FLG_RELOC) { 1026 if (!initted) { 1027 hist_init(); 1028 initted = 1; 1029 } 1030 1031 if (prompt) 1032 puts (prompt); 1033 1034 rc = cread_line(prompt, p, &len, timeout); 1035 return rc < 0 ? rc : len; 1036 1037 } else { 1038 #endif /* CONFIG_CMDLINE_EDITING */ 1039 char * p_buf = p; 1040 int n = 0; /* buffer index */ 1041 int plen = 0; /* prompt length */ 1042 int col; /* output column cnt */ 1043 char c; 1044 1045 /* print prompt */ 1046 if (prompt) { 1047 plen = strlen (prompt); 1048 puts (prompt); 1049 } 1050 col = plen; 1051 1052 for (;;) { 1053 #ifdef CONFIG_BOOT_RETRY_TIME 1054 while (!tstc()) { /* while no incoming data */ 1055 if (retry_time >= 0 && get_ticks() > endtime) 1056 return (-2); /* timed out */ 1057 WATCHDOG_RESET(); 1058 } 1059 #endif 1060 WATCHDOG_RESET(); /* Trigger watchdog, if needed */ 1061 1062 #ifdef CONFIG_SHOW_ACTIVITY 1063 while (!tstc()) { 1064 show_activity(0); 1065 WATCHDOG_RESET(); 1066 } 1067 #endif 1068 c = getc(); 1069 1070 /* 1071 * Special character handling 1072 */ 1073 switch (c) { 1074 case '\r': /* Enter */ 1075 case '\n': 1076 *p = '\0'; 1077 puts ("\r\n"); 1078 return p - p_buf; 1079 1080 case '\0': /* nul */ 1081 continue; 1082 1083 case 0x03: /* ^C - break */ 1084 p_buf[0] = '\0'; /* discard input */ 1085 return -1; 1086 1087 case 0x15: /* ^U - erase line */ 1088 while (col > plen) { 1089 puts (erase_seq); 1090 --col; 1091 } 1092 p = p_buf; 1093 n = 0; 1094 continue; 1095 1096 case 0x17: /* ^W - erase word */ 1097 p=delete_char(p_buf, p, &col, &n, plen); 1098 while ((n > 0) && (*p != ' ')) { 1099 p=delete_char(p_buf, p, &col, &n, plen); 1100 } 1101 continue; 1102 1103 case 0x08: /* ^H - backspace */ 1104 case 0x7F: /* DEL - backspace */ 1105 p=delete_char(p_buf, p, &col, &n, plen); 1106 continue; 1107 1108 default: 1109 /* 1110 * Must be a normal character then 1111 */ 1112 if (n < CONFIG_SYS_CBSIZE-2) { 1113 if (c == '\t') { /* expand TABs */ 1114 #ifdef CONFIG_AUTO_COMPLETE 1115 /* if auto completion triggered just continue */ 1116 *p = '\0'; 1117 if (cmd_auto_complete(prompt, console_buffer, &n, &col)) { 1118 p = p_buf + n; /* reset */ 1119 continue; 1120 } 1121 #endif 1122 puts (tab_seq+(col&07)); 1123 col += 8 - (col&07); 1124 } else { 1125 char buf[2]; 1126 1127 /* 1128 * Echo input using puts() to force an 1129 * LCD flush if we are using an LCD 1130 */ 1131 ++col; 1132 buf[0] = c; 1133 buf[1] = '\0'; 1134 puts(buf); 1135 } 1136 *p++ = c; 1137 ++n; 1138 } else { /* Buffer full */ 1139 putc ('\a'); 1140 } 1141 } 1142 } 1143 #ifdef CONFIG_CMDLINE_EDITING 1144 } 1145 #endif 1146 } 1147 1148 /****************************************************************************/ 1149 1150 static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen) 1151 { 1152 char *s; 1153 1154 if (*np == 0) { 1155 return (p); 1156 } 1157 1158 if (*(--p) == '\t') { /* will retype the whole line */ 1159 while (*colp > plen) { 1160 puts (erase_seq); 1161 (*colp)--; 1162 } 1163 for (s=buffer; s<p; ++s) { 1164 if (*s == '\t') { 1165 puts (tab_seq+((*colp) & 07)); 1166 *colp += 8 - ((*colp) & 07); 1167 } else { 1168 ++(*colp); 1169 putc (*s); 1170 } 1171 } 1172 } else { 1173 puts (erase_seq); 1174 (*colp)--; 1175 } 1176 (*np)--; 1177 return (p); 1178 } 1179 1180 /****************************************************************************/ 1181 1182 int parse_line (char *line, char *argv[]) 1183 { 1184 int nargs = 0; 1185 1186 debug_parser("parse_line: \"%s\"\n", line); 1187 while (nargs < CONFIG_SYS_MAXARGS) { 1188 1189 /* skip any white space */ 1190 while (isblank(*line)) 1191 ++line; 1192 1193 if (*line == '\0') { /* end of line, no more args */ 1194 argv[nargs] = NULL; 1195 debug_parser("parse_line: nargs=%d\n", nargs); 1196 return nargs; 1197 } 1198 1199 argv[nargs++] = line; /* begin of argument string */ 1200 1201 /* find end of string */ 1202 while (*line && !isblank(*line)) 1203 ++line; 1204 1205 if (*line == '\0') { /* end of line, no more args */ 1206 argv[nargs] = NULL; 1207 debug_parser("parse_line: nargs=%d\n", nargs); 1208 return nargs; 1209 } 1210 1211 *line++ = '\0'; /* terminate current arg */ 1212 } 1213 1214 printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS); 1215 1216 debug_parser("parse_line: nargs=%d\n", nargs); 1217 return (nargs); 1218 } 1219 1220 /****************************************************************************/ 1221 1222 #ifndef CONFIG_SYS_HUSH_PARSER 1223 static void process_macros (const char *input, char *output) 1224 { 1225 char c, prev; 1226 const char *varname_start = NULL; 1227 int inputcnt = strlen (input); 1228 int outputcnt = CONFIG_SYS_CBSIZE; 1229 int state = 0; /* 0 = waiting for '$' */ 1230 1231 /* 1 = waiting for '(' or '{' */ 1232 /* 2 = waiting for ')' or '}' */ 1233 /* 3 = waiting for ''' */ 1234 char *output_start = output; 1235 1236 debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input), 1237 input); 1238 1239 prev = '\0'; /* previous character */ 1240 1241 while (inputcnt && outputcnt) { 1242 c = *input++; 1243 inputcnt--; 1244 1245 if (state != 3) { 1246 /* remove one level of escape characters */ 1247 if ((c == '\\') && (prev != '\\')) { 1248 if (inputcnt-- == 0) 1249 break; 1250 prev = c; 1251 c = *input++; 1252 } 1253 } 1254 1255 switch (state) { 1256 case 0: /* Waiting for (unescaped) $ */ 1257 if ((c == '\'') && (prev != '\\')) { 1258 state = 3; 1259 break; 1260 } 1261 if ((c == '$') && (prev != '\\')) { 1262 state++; 1263 } else { 1264 *(output++) = c; 1265 outputcnt--; 1266 } 1267 break; 1268 case 1: /* Waiting for ( */ 1269 if (c == '(' || c == '{') { 1270 state++; 1271 varname_start = input; 1272 } else { 1273 state = 0; 1274 *(output++) = '$'; 1275 outputcnt--; 1276 1277 if (outputcnt) { 1278 *(output++) = c; 1279 outputcnt--; 1280 } 1281 } 1282 break; 1283 case 2: /* Waiting for ) */ 1284 if (c == ')' || c == '}') { 1285 int i; 1286 char envname[CONFIG_SYS_CBSIZE], *envval; 1287 int envcnt = input - varname_start - 1; /* Varname # of chars */ 1288 1289 /* Get the varname */ 1290 for (i = 0; i < envcnt; i++) { 1291 envname[i] = varname_start[i]; 1292 } 1293 envname[i] = 0; 1294 1295 /* Get its value */ 1296 envval = getenv (envname); 1297 1298 /* Copy into the line if it exists */ 1299 if (envval != NULL) 1300 while ((*envval) && outputcnt) { 1301 *(output++) = *(envval++); 1302 outputcnt--; 1303 } 1304 /* Look for another '$' */ 1305 state = 0; 1306 } 1307 break; 1308 case 3: /* Waiting for ' */ 1309 if ((c == '\'') && (prev != '\\')) { 1310 state = 0; 1311 } else { 1312 *(output++) = c; 1313 outputcnt--; 1314 } 1315 break; 1316 } 1317 prev = c; 1318 } 1319 1320 if (outputcnt) 1321 *output = 0; 1322 else 1323 *(output - 1) = 0; 1324 1325 debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n", 1326 strlen(output_start), output_start); 1327 } 1328 1329 /**************************************************************************** 1330 * returns: 1331 * 1 - command executed, repeatable 1332 * 0 - command executed but not repeatable, interrupted commands are 1333 * always considered not repeatable 1334 * -1 - not executed (unrecognized, bootd recursion or too many args) 1335 * (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is 1336 * considered unrecognized) 1337 * 1338 * WARNING: 1339 * 1340 * We must create a temporary copy of the command since the command we get 1341 * may be the result from getenv(), which returns a pointer directly to 1342 * the environment data, which may change magicly when the command we run 1343 * creates or modifies environment variables (like "bootp" does). 1344 */ 1345 static int builtin_run_command(const char *cmd, int flag) 1346 { 1347 char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */ 1348 char *token; /* start of token in cmdbuf */ 1349 char *sep; /* end of token (separator) in cmdbuf */ 1350 char finaltoken[CONFIG_SYS_CBSIZE]; 1351 char *str = cmdbuf; 1352 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ 1353 int argc, inquotes; 1354 int repeatable = 1; 1355 int rc = 0; 1356 1357 debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd); 1358 if (DEBUG_PARSER) { 1359 /* use puts - string may be loooong */ 1360 puts(cmd ? cmd : "NULL"); 1361 puts("\"\n"); 1362 } 1363 clear_ctrlc(); /* forget any previous Control C */ 1364 1365 if (!cmd || !*cmd) { 1366 return -1; /* empty command */ 1367 } 1368 1369 if (strlen(cmd) >= CONFIG_SYS_CBSIZE) { 1370 puts ("## Command too long!\n"); 1371 return -1; 1372 } 1373 1374 strcpy (cmdbuf, cmd); 1375 1376 /* Process separators and check for invalid 1377 * repeatable commands 1378 */ 1379 1380 debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); 1381 while (*str) { 1382 1383 /* 1384 * Find separator, or string end 1385 * Allow simple escape of ';' by writing "\;" 1386 */ 1387 for (inquotes = 0, sep = str; *sep; sep++) { 1388 if ((*sep=='\'') && 1389 (*(sep-1) != '\\')) 1390 inquotes=!inquotes; 1391 1392 if (!inquotes && 1393 (*sep == ';') && /* separator */ 1394 ( sep != str) && /* past string start */ 1395 (*(sep-1) != '\\')) /* and NOT escaped */ 1396 break; 1397 } 1398 1399 /* 1400 * Limit the token to data between separators 1401 */ 1402 token = str; 1403 if (*sep) { 1404 str = sep + 1; /* start of command for next pass */ 1405 *sep = '\0'; 1406 } 1407 else 1408 str = sep; /* no more commands for next pass */ 1409 debug_parser("token: \"%s\"\n", token); 1410 1411 /* find macros in this token and replace them */ 1412 process_macros (token, finaltoken); 1413 1414 /* Extract arguments */ 1415 if ((argc = parse_line (finaltoken, argv)) == 0) { 1416 rc = -1; /* no command at all */ 1417 continue; 1418 } 1419 1420 if (cmd_process(flag, argc, argv, &repeatable, NULL)) 1421 rc = -1; 1422 1423 /* Did the user stop this? */ 1424 if (had_ctrlc ()) 1425 return -1; /* if stopped then not repeatable */ 1426 } 1427 1428 return rc ? rc : repeatable; 1429 } 1430 #endif 1431 1432 /* 1433 * Run a command using the selected parser. 1434 * 1435 * @param cmd Command to run 1436 * @param flag Execution flags (CMD_FLAG_...) 1437 * @return 0 on success, or != 0 on error. 1438 */ 1439 int run_command(const char *cmd, int flag) 1440 { 1441 #ifndef CONFIG_SYS_HUSH_PARSER 1442 /* 1443 * builtin_run_command can return 0 or 1 for success, so clean up 1444 * its result. 1445 */ 1446 if (builtin_run_command(cmd, flag) == -1) 1447 return 1; 1448 1449 return 0; 1450 #else 1451 return parse_string_outer(cmd, 1452 FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); 1453 #endif 1454 } 1455 1456 #ifndef CONFIG_SYS_HUSH_PARSER 1457 /** 1458 * Execute a list of command separated by ; or \n using the built-in parser. 1459 * 1460 * This function cannot take a const char * for the command, since if it 1461 * finds newlines in the string, it replaces them with \0. 1462 * 1463 * @param cmd String containing list of commands 1464 * @param flag Execution flags (CMD_FLAG_...) 1465 * @return 0 on success, or != 0 on error. 1466 */ 1467 static int builtin_run_command_list(char *cmd, int flag) 1468 { 1469 char *line, *next; 1470 int rcode = 0; 1471 1472 /* 1473 * Break into individual lines, and execute each line; terminate on 1474 * error. 1475 */ 1476 line = next = cmd; 1477 while (*next) { 1478 if (*next == '\n') { 1479 *next = '\0'; 1480 /* run only non-empty commands */ 1481 if (*line) { 1482 debug("** exec: \"%s\"\n", line); 1483 if (builtin_run_command(line, 0) < 0) { 1484 rcode = 1; 1485 break; 1486 } 1487 } 1488 line = next + 1; 1489 } 1490 ++next; 1491 } 1492 if (rcode == 0 && *line) 1493 rcode = (builtin_run_command(line, 0) >= 0); 1494 1495 return rcode; 1496 } 1497 #endif 1498 1499 int run_command_list(const char *cmd, int len, int flag) 1500 { 1501 int need_buff = 1; 1502 char *buff = (char *)cmd; /* cast away const */ 1503 int rcode = 0; 1504 1505 if (len == -1) { 1506 len = strlen(cmd); 1507 #ifdef CONFIG_SYS_HUSH_PARSER 1508 /* hush will never change our string */ 1509 need_buff = 0; 1510 #else 1511 /* the built-in parser will change our string if it sees \n */ 1512 need_buff = strchr(cmd, '\n') != NULL; 1513 #endif 1514 } 1515 if (need_buff) { 1516 buff = malloc(len + 1); 1517 if (!buff) 1518 return 1; 1519 memcpy(buff, cmd, len); 1520 buff[len] = '\0'; 1521 } 1522 #ifdef CONFIG_SYS_HUSH_PARSER 1523 rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON); 1524 #else 1525 /* 1526 * This function will overwrite any \n it sees with a \0, which 1527 * is why it can't work with a const char *. Here we are making 1528 * using of internal knowledge of this function, to avoid always 1529 * doing a malloc() which is actually required only in a case that 1530 * is pretty rare. 1531 */ 1532 rcode = builtin_run_command_list(buff, flag); 1533 if (need_buff) 1534 free(buff); 1535 #endif 1536 1537 return rcode; 1538 } 1539 1540 /****************************************************************************/ 1541 1542 #if defined(CONFIG_CMD_RUN) 1543 int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) 1544 { 1545 int i; 1546 1547 if (argc < 2) 1548 return CMD_RET_USAGE; 1549 1550 for (i=1; i<argc; ++i) { 1551 char *arg; 1552 1553 if ((arg = getenv (argv[i])) == NULL) { 1554 printf ("## Error: \"%s\" not defined\n", argv[i]); 1555 return 1; 1556 } 1557 1558 if (run_command(arg, flag) != 0) 1559 return 1; 1560 } 1561 return 0; 1562 } 1563 #endif 1564