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 * SPDX-License-Identifier: GPL-2.0+ 10 */ 11 12 #include <common.h> 13 #include <cli.h> 14 #include <watchdog.h> 15 16 DECLARE_GLOBAL_DATA_PTR; 17 18 static const char erase_seq[] = "\b \b"; /* erase sequence */ 19 static const char tab_seq[] = " "; /* used to expand TABs */ 20 21 #ifdef CONFIG_BOOT_RETRY_TIME 22 static uint64_t endtime; /* must be set, default is instant timeout */ 23 static int retry_time = -1; /* -1 so can call readline before main_loop */ 24 #endif 25 26 char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ 27 28 #ifndef CONFIG_BOOT_RETRY_MIN 29 #define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME 30 #endif 31 32 static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen) 33 { 34 char *s; 35 36 if (*np == 0) 37 return p; 38 39 if (*(--p) == '\t') { /* will retype the whole line */ 40 while (*colp > plen) { 41 puts(erase_seq); 42 (*colp)--; 43 } 44 for (s = buffer; s < p; ++s) { 45 if (*s == '\t') { 46 puts(tab_seq + ((*colp) & 07)); 47 *colp += 8 - ((*colp) & 07); 48 } else { 49 ++(*colp); 50 putc(*s); 51 } 52 } 53 } else { 54 puts(erase_seq); 55 (*colp)--; 56 } 57 (*np)--; 58 59 return p; 60 } 61 62 #ifdef CONFIG_CMDLINE_EDITING 63 64 /* 65 * cmdline-editing related codes from vivi. 66 * Author: Janghoon Lyu <nandy@mizi.com> 67 */ 68 69 #define putnstr(str, n) printf("%.*s", (int)n, str) 70 71 #define CTL_CH(c) ((c) - 'a' + 1) 72 #define CTL_BACKSPACE ('\b') 73 #define DEL ((char)255) 74 #define DEL7 ((char)127) 75 #define CREAD_HIST_CHAR ('!') 76 77 #define getcmd_putch(ch) putc(ch) 78 #define getcmd_getch() getc() 79 #define getcmd_cbeep() getcmd_putch('\a') 80 81 #define HIST_MAX 20 82 #define HIST_SIZE CONFIG_SYS_CBSIZE 83 84 static int hist_max; 85 static int hist_add_idx; 86 static int hist_cur = -1; 87 static unsigned hist_num; 88 89 static char *hist_list[HIST_MAX]; 90 static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ 91 92 #define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1) 93 94 static void hist_init(void) 95 { 96 int i; 97 98 hist_max = 0; 99 hist_add_idx = 0; 100 hist_cur = -1; 101 hist_num = 0; 102 103 for (i = 0; i < HIST_MAX; i++) { 104 hist_list[i] = hist_lines[i]; 105 hist_list[i][0] = '\0'; 106 } 107 } 108 109 static void cread_add_to_hist(char *line) 110 { 111 strcpy(hist_list[hist_add_idx], line); 112 113 if (++hist_add_idx >= HIST_MAX) 114 hist_add_idx = 0; 115 116 if (hist_add_idx > hist_max) 117 hist_max = hist_add_idx; 118 119 hist_num++; 120 } 121 122 static char *hist_prev(void) 123 { 124 char *ret; 125 int old_cur; 126 127 if (hist_cur < 0) 128 return NULL; 129 130 old_cur = hist_cur; 131 if (--hist_cur < 0) 132 hist_cur = hist_max; 133 134 if (hist_cur == hist_add_idx) { 135 hist_cur = old_cur; 136 ret = NULL; 137 } else { 138 ret = hist_list[hist_cur]; 139 } 140 141 return ret; 142 } 143 144 static char *hist_next(void) 145 { 146 char *ret; 147 148 if (hist_cur < 0) 149 return NULL; 150 151 if (hist_cur == hist_add_idx) 152 return NULL; 153 154 if (++hist_cur > hist_max) 155 hist_cur = 0; 156 157 if (hist_cur == hist_add_idx) 158 ret = ""; 159 else 160 ret = hist_list[hist_cur]; 161 162 return ret; 163 } 164 165 #ifndef CONFIG_CMDLINE_EDITING 166 static void cread_print_hist_list(void) 167 { 168 int i; 169 unsigned long n; 170 171 n = hist_num - hist_max; 172 173 i = hist_add_idx + 1; 174 while (1) { 175 if (i > hist_max) 176 i = 0; 177 if (i == hist_add_idx) 178 break; 179 printf("%s\n", hist_list[i]); 180 n++; 181 i++; 182 } 183 } 184 #endif /* CONFIG_CMDLINE_EDITING */ 185 186 #define BEGINNING_OF_LINE() { \ 187 while (num) { \ 188 getcmd_putch(CTL_BACKSPACE); \ 189 num--; \ 190 } \ 191 } 192 193 #define ERASE_TO_EOL() { \ 194 if (num < eol_num) { \ 195 printf("%*s", (int)(eol_num - num), ""); \ 196 do { \ 197 getcmd_putch(CTL_BACKSPACE); \ 198 } while (--eol_num > num); \ 199 } \ 200 } 201 202 #define REFRESH_TO_EOL() { \ 203 if (num < eol_num) { \ 204 wlen = eol_num - num; \ 205 putnstr(buf + num, wlen); \ 206 num = eol_num; \ 207 } \ 208 } 209 210 static void cread_add_char(char ichar, int insert, unsigned long *num, 211 unsigned long *eol_num, char *buf, unsigned long len) 212 { 213 unsigned long wlen; 214 215 /* room ??? */ 216 if (insert || *num == *eol_num) { 217 if (*eol_num > len - 1) { 218 getcmd_cbeep(); 219 return; 220 } 221 (*eol_num)++; 222 } 223 224 if (insert) { 225 wlen = *eol_num - *num; 226 if (wlen > 1) 227 memmove(&buf[*num+1], &buf[*num], wlen-1); 228 229 buf[*num] = ichar; 230 putnstr(buf + *num, wlen); 231 (*num)++; 232 while (--wlen) 233 getcmd_putch(CTL_BACKSPACE); 234 } else { 235 /* echo the character */ 236 wlen = 1; 237 buf[*num] = ichar; 238 putnstr(buf + *num, wlen); 239 (*num)++; 240 } 241 } 242 243 static void cread_add_str(char *str, int strsize, int insert, 244 unsigned long *num, unsigned long *eol_num, 245 char *buf, unsigned long len) 246 { 247 while (strsize--) { 248 cread_add_char(*str, insert, num, eol_num, buf, len); 249 str++; 250 } 251 } 252 253 static int cread_line(const char *const prompt, char *buf, unsigned int *len, 254 int timeout) 255 { 256 unsigned long num = 0; 257 unsigned long eol_num = 0; 258 unsigned long wlen; 259 char ichar; 260 int insert = 1; 261 int esc_len = 0; 262 char esc_save[8]; 263 int init_len = strlen(buf); 264 int first = 1; 265 266 if (init_len) 267 cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); 268 269 while (1) { 270 #ifdef CONFIG_BOOT_RETRY_TIME 271 while (!tstc()) { /* while no incoming data */ 272 if (retry_time >= 0 && get_ticks() > endtime) 273 return -2; /* timed out */ 274 WATCHDOG_RESET(); 275 } 276 #endif 277 if (first && timeout) { 278 uint64_t etime = endtick(timeout); 279 280 while (!tstc()) { /* while no incoming data */ 281 if (get_ticks() >= etime) 282 return -2; /* timed out */ 283 WATCHDOG_RESET(); 284 } 285 first = 0; 286 } 287 288 ichar = getcmd_getch(); 289 290 if ((ichar == '\n') || (ichar == '\r')) { 291 putc('\n'); 292 break; 293 } 294 295 /* 296 * handle standard linux xterm esc sequences for arrow key, etc. 297 */ 298 if (esc_len != 0) { 299 if (esc_len == 1) { 300 if (ichar == '[') { 301 esc_save[esc_len] = ichar; 302 esc_len = 2; 303 } else { 304 cread_add_str(esc_save, esc_len, 305 insert, &num, &eol_num, 306 buf, *len); 307 esc_len = 0; 308 } 309 continue; 310 } 311 312 switch (ichar) { 313 case 'D': /* <- key */ 314 ichar = CTL_CH('b'); 315 esc_len = 0; 316 break; 317 case 'C': /* -> key */ 318 ichar = CTL_CH('f'); 319 esc_len = 0; 320 break; /* pass off to ^F handler */ 321 case 'H': /* Home key */ 322 ichar = CTL_CH('a'); 323 esc_len = 0; 324 break; /* pass off to ^A handler */ 325 case 'A': /* up arrow */ 326 ichar = CTL_CH('p'); 327 esc_len = 0; 328 break; /* pass off to ^P handler */ 329 case 'B': /* down arrow */ 330 ichar = CTL_CH('n'); 331 esc_len = 0; 332 break; /* pass off to ^N handler */ 333 default: 334 esc_save[esc_len++] = ichar; 335 cread_add_str(esc_save, esc_len, insert, 336 &num, &eol_num, buf, *len); 337 esc_len = 0; 338 continue; 339 } 340 } 341 342 switch (ichar) { 343 case 0x1b: 344 if (esc_len == 0) { 345 esc_save[esc_len] = ichar; 346 esc_len = 1; 347 } else { 348 puts("impossible condition #876\n"); 349 esc_len = 0; 350 } 351 break; 352 353 case CTL_CH('a'): 354 BEGINNING_OF_LINE(); 355 break; 356 case CTL_CH('c'): /* ^C - break */ 357 *buf = '\0'; /* discard input */ 358 return -1; 359 case CTL_CH('f'): 360 if (num < eol_num) { 361 getcmd_putch(buf[num]); 362 num++; 363 } 364 break; 365 case CTL_CH('b'): 366 if (num) { 367 getcmd_putch(CTL_BACKSPACE); 368 num--; 369 } 370 break; 371 case CTL_CH('d'): 372 if (num < eol_num) { 373 wlen = eol_num - num - 1; 374 if (wlen) { 375 memmove(&buf[num], &buf[num+1], wlen); 376 putnstr(buf + num, wlen); 377 } 378 379 getcmd_putch(' '); 380 do { 381 getcmd_putch(CTL_BACKSPACE); 382 } while (wlen--); 383 eol_num--; 384 } 385 break; 386 case CTL_CH('k'): 387 ERASE_TO_EOL(); 388 break; 389 case CTL_CH('e'): 390 REFRESH_TO_EOL(); 391 break; 392 case CTL_CH('o'): 393 insert = !insert; 394 break; 395 case CTL_CH('x'): 396 case CTL_CH('u'): 397 BEGINNING_OF_LINE(); 398 ERASE_TO_EOL(); 399 break; 400 case DEL: 401 case DEL7: 402 case 8: 403 if (num) { 404 wlen = eol_num - num; 405 num--; 406 memmove(&buf[num], &buf[num+1], wlen); 407 getcmd_putch(CTL_BACKSPACE); 408 putnstr(buf + num, wlen); 409 getcmd_putch(' '); 410 do { 411 getcmd_putch(CTL_BACKSPACE); 412 } while (wlen--); 413 eol_num--; 414 } 415 break; 416 case CTL_CH('p'): 417 case CTL_CH('n'): 418 { 419 char *hline; 420 421 esc_len = 0; 422 423 if (ichar == CTL_CH('p')) 424 hline = hist_prev(); 425 else 426 hline = hist_next(); 427 428 if (!hline) { 429 getcmd_cbeep(); 430 continue; 431 } 432 433 /* nuke the current line */ 434 /* first, go home */ 435 BEGINNING_OF_LINE(); 436 437 /* erase to end of line */ 438 ERASE_TO_EOL(); 439 440 /* copy new line into place and display */ 441 strcpy(buf, hline); 442 eol_num = strlen(buf); 443 REFRESH_TO_EOL(); 444 continue; 445 } 446 #ifdef CONFIG_AUTO_COMPLETE 447 case '\t': { 448 int num2, col; 449 450 /* do not autocomplete when in the middle */ 451 if (num < eol_num) { 452 getcmd_cbeep(); 453 break; 454 } 455 456 buf[num] = '\0'; 457 col = strlen(prompt) + eol_num; 458 num2 = num; 459 if (cmd_auto_complete(prompt, buf, &num2, &col)) { 460 col = num2 - num; 461 num += col; 462 eol_num += col; 463 } 464 break; 465 } 466 #endif 467 default: 468 cread_add_char(ichar, insert, &num, &eol_num, buf, 469 *len); 470 break; 471 } 472 } 473 *len = eol_num; 474 buf[eol_num] = '\0'; /* lose the newline */ 475 476 if (buf[0] && buf[0] != CREAD_HIST_CHAR) 477 cread_add_to_hist(buf); 478 hist_cur = hist_add_idx; 479 480 return 0; 481 } 482 483 #endif /* CONFIG_CMDLINE_EDITING */ 484 485 /****************************************************************************/ 486 487 int cli_readline(const char *const prompt) 488 { 489 /* 490 * If console_buffer isn't 0-length the user will be prompted to modify 491 * it instead of entering it from scratch as desired. 492 */ 493 console_buffer[0] = '\0'; 494 495 return cli_readline_into_buffer(prompt, console_buffer, 0); 496 } 497 498 499 int cli_readline_into_buffer(const char *const prompt, char *buffer, 500 int timeout) 501 { 502 char *p = buffer; 503 #ifdef CONFIG_CMDLINE_EDITING 504 unsigned int len = CONFIG_SYS_CBSIZE; 505 int rc; 506 static int initted; 507 508 /* 509 * History uses a global array which is not 510 * writable until after relocation to RAM. 511 * Revert to non-history version if still 512 * running from flash. 513 */ 514 if (gd->flags & GD_FLG_RELOC) { 515 if (!initted) { 516 hist_init(); 517 initted = 1; 518 } 519 520 if (prompt) 521 puts(prompt); 522 523 rc = cread_line(prompt, p, &len, timeout); 524 return rc < 0 ? rc : len; 525 526 } else { 527 #endif /* CONFIG_CMDLINE_EDITING */ 528 char *p_buf = p; 529 int n = 0; /* buffer index */ 530 int plen = 0; /* prompt length */ 531 int col; /* output column cnt */ 532 char c; 533 534 /* print prompt */ 535 if (prompt) { 536 plen = strlen(prompt); 537 puts(prompt); 538 } 539 col = plen; 540 541 for (;;) { 542 #ifdef CONFIG_BOOT_RETRY_TIME 543 while (!tstc()) { /* while no incoming data */ 544 if (retry_time >= 0 && get_ticks() > endtime) 545 return -2; /* timed out */ 546 WATCHDOG_RESET(); 547 } 548 #endif 549 WATCHDOG_RESET(); /* Trigger watchdog, if needed */ 550 551 #ifdef CONFIG_SHOW_ACTIVITY 552 while (!tstc()) { 553 show_activity(0); 554 WATCHDOG_RESET(); 555 } 556 #endif 557 c = getc(); 558 559 /* 560 * Special character handling 561 */ 562 switch (c) { 563 case '\r': /* Enter */ 564 case '\n': 565 *p = '\0'; 566 puts("\r\n"); 567 return p - p_buf; 568 569 case '\0': /* nul */ 570 continue; 571 572 case 0x03: /* ^C - break */ 573 p_buf[0] = '\0'; /* discard input */ 574 return -1; 575 576 case 0x15: /* ^U - erase line */ 577 while (col > plen) { 578 puts(erase_seq); 579 --col; 580 } 581 p = p_buf; 582 n = 0; 583 continue; 584 585 case 0x17: /* ^W - erase word */ 586 p = delete_char(p_buf, p, &col, &n, plen); 587 while ((n > 0) && (*p != ' ')) 588 p = delete_char(p_buf, p, &col, &n, plen); 589 continue; 590 591 case 0x08: /* ^H - backspace */ 592 case 0x7F: /* DEL - backspace */ 593 p = delete_char(p_buf, p, &col, &n, plen); 594 continue; 595 596 default: 597 /* 598 * Must be a normal character then 599 */ 600 if (n < CONFIG_SYS_CBSIZE-2) { 601 if (c == '\t') { /* expand TABs */ 602 #ifdef CONFIG_AUTO_COMPLETE 603 /* 604 * if auto completion triggered just 605 * continue 606 */ 607 *p = '\0'; 608 if (cmd_auto_complete(prompt, 609 console_buffer, 610 &n, &col)) { 611 p = p_buf + n; /* reset */ 612 continue; 613 } 614 #endif 615 puts(tab_seq + (col & 07)); 616 col += 8 - (col & 07); 617 } else { 618 char buf[2]; 619 620 /* 621 * Echo input using puts() to force an 622 * LCD flush if we are using an LCD 623 */ 624 ++col; 625 buf[0] = c; 626 buf[1] = '\0'; 627 puts(buf); 628 } 629 *p++ = c; 630 ++n; 631 } else { /* Buffer full */ 632 putc('\a'); 633 } 634 } 635 } 636 #ifdef CONFIG_CMDLINE_EDITING 637 } 638 #endif 639 } 640 641 #ifdef CONFIG_BOOT_RETRY_TIME 642 /*************************************************************************** 643 * initialize command line timeout 644 */ 645 void init_cmd_timeout(void) 646 { 647 char *s = getenv("bootretry"); 648 649 if (s != NULL) 650 retry_time = (int)simple_strtol(s, NULL, 10); 651 else 652 retry_time = CONFIG_BOOT_RETRY_TIME; 653 654 if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) 655 retry_time = CONFIG_BOOT_RETRY_MIN; 656 } 657 658 /*************************************************************************** 659 * reset command line timeout to retry_time seconds 660 */ 661 void reset_cmd_timeout(void) 662 { 663 endtime = endtick(retry_time); 664 } 665 666 void bootretry_dont_retry(void) 667 { 668 retry_time = -1; 669 } 670 671 #endif 672