cli_readline.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * (C) Copyright 2000
  4. * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
  5. *
  6. * Add to readline cmdline-editing by
  7. * (C) Copyright 2005
  8. * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
  9. */
  10. #include <common.h>
  11. #include <bootretry.h>
  12. #include <cli.h>
  13. #include <command.h>
  14. #include <time.h>
  15. #include <watchdog.h>
  16. #include <asm/global_data.h>
  17. DECLARE_GLOBAL_DATA_PTR;
  18. static const char erase_seq[] = "\b \b"; /* erase sequence */
  19. static const char tab_seq[] = " "; /* used to expand TABs */
  20. char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */
  21. static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen)
  22. {
  23. char *s;
  24. if (*np == 0)
  25. return p;
  26. if (*(--p) == '\t') { /* will retype the whole line */
  27. while (*colp > plen) {
  28. puts(erase_seq);
  29. (*colp)--;
  30. }
  31. for (s = buffer; s < p; ++s) {
  32. if (*s == '\t') {
  33. puts(tab_seq + ((*colp) & 07));
  34. *colp += 8 - ((*colp) & 07);
  35. } else {
  36. ++(*colp);
  37. putc(*s);
  38. }
  39. }
  40. } else {
  41. puts(erase_seq);
  42. (*colp)--;
  43. }
  44. (*np)--;
  45. return p;
  46. }
  47. #ifdef CONFIG_CMDLINE_EDITING
  48. /*
  49. * cmdline-editing related codes from vivi.
  50. * Author: Janghoon Lyu <nandy@mizi.com>
  51. */
  52. #define putnstr(str, n) printf("%.*s", (int)n, str)
  53. #define CTL_BACKSPACE ('\b')
  54. #define DEL ((char)255)
  55. #define DEL7 ((char)127)
  56. #define CREAD_HIST_CHAR ('!')
  57. #define getcmd_putch(ch) putc(ch)
  58. #define getcmd_getch() getchar()
  59. #define getcmd_cbeep() getcmd_putch('\a')
  60. #ifdef CONFIG_SPL_BUILD
  61. #define HIST_MAX 3
  62. #define HIST_SIZE 32
  63. #else
  64. #define HIST_MAX 20
  65. #define HIST_SIZE CONFIG_SYS_CBSIZE
  66. #endif
  67. static int hist_max;
  68. static int hist_add_idx;
  69. static int hist_cur = -1;
  70. static unsigned hist_num;
  71. static char *hist_list[HIST_MAX];
  72. static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */
  73. #define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1)
  74. static void hist_init(void)
  75. {
  76. int i;
  77. hist_max = 0;
  78. hist_add_idx = 0;
  79. hist_cur = -1;
  80. hist_num = 0;
  81. for (i = 0; i < HIST_MAX; i++) {
  82. hist_list[i] = hist_lines[i];
  83. hist_list[i][0] = '\0';
  84. }
  85. }
  86. static void cread_add_to_hist(char *line)
  87. {
  88. strcpy(hist_list[hist_add_idx], line);
  89. if (++hist_add_idx >= HIST_MAX)
  90. hist_add_idx = 0;
  91. if (hist_add_idx > hist_max)
  92. hist_max = hist_add_idx;
  93. hist_num++;
  94. }
  95. static char *hist_prev(void)
  96. {
  97. char *ret;
  98. int old_cur;
  99. if (hist_cur < 0)
  100. return NULL;
  101. old_cur = hist_cur;
  102. if (--hist_cur < 0)
  103. hist_cur = hist_max;
  104. if (hist_cur == hist_add_idx) {
  105. hist_cur = old_cur;
  106. ret = NULL;
  107. } else {
  108. ret = hist_list[hist_cur];
  109. }
  110. return ret;
  111. }
  112. static char *hist_next(void)
  113. {
  114. char *ret;
  115. if (hist_cur < 0)
  116. return NULL;
  117. if (hist_cur == hist_add_idx)
  118. return NULL;
  119. if (++hist_cur > hist_max)
  120. hist_cur = 0;
  121. if (hist_cur == hist_add_idx)
  122. ret = "";
  123. else
  124. ret = hist_list[hist_cur];
  125. return ret;
  126. }
  127. #ifndef CONFIG_CMDLINE_EDITING
  128. static void cread_print_hist_list(void)
  129. {
  130. int i;
  131. unsigned long n;
  132. n = hist_num - hist_max;
  133. i = hist_add_idx + 1;
  134. while (1) {
  135. if (i > hist_max)
  136. i = 0;
  137. if (i == hist_add_idx)
  138. break;
  139. printf("%s\n", hist_list[i]);
  140. n++;
  141. i++;
  142. }
  143. }
  144. #endif /* CONFIG_CMDLINE_EDITING */
  145. #define BEGINNING_OF_LINE() { \
  146. while (num) { \
  147. getcmd_putch(CTL_BACKSPACE); \
  148. num--; \
  149. } \
  150. }
  151. #define ERASE_TO_EOL() { \
  152. if (num < eol_num) { \
  153. printf("%*s", (int)(eol_num - num), ""); \
  154. do { \
  155. getcmd_putch(CTL_BACKSPACE); \
  156. } while (--eol_num > num); \
  157. } \
  158. }
  159. #define REFRESH_TO_EOL() { \
  160. if (num < eol_num) { \
  161. wlen = eol_num - num; \
  162. putnstr(buf + num, wlen); \
  163. num = eol_num; \
  164. } \
  165. }
  166. static void cread_add_char(char ichar, int insert, unsigned long *num,
  167. unsigned long *eol_num, char *buf, unsigned long len)
  168. {
  169. unsigned long wlen;
  170. /* room ??? */
  171. if (insert || *num == *eol_num) {
  172. if (*eol_num > len - 1) {
  173. getcmd_cbeep();
  174. return;
  175. }
  176. (*eol_num)++;
  177. }
  178. if (insert) {
  179. wlen = *eol_num - *num;
  180. if (wlen > 1)
  181. memmove(&buf[*num+1], &buf[*num], wlen-1);
  182. buf[*num] = ichar;
  183. putnstr(buf + *num, wlen);
  184. (*num)++;
  185. while (--wlen)
  186. getcmd_putch(CTL_BACKSPACE);
  187. } else {
  188. /* echo the character */
  189. wlen = 1;
  190. buf[*num] = ichar;
  191. putnstr(buf + *num, wlen);
  192. (*num)++;
  193. }
  194. }
  195. static void cread_add_str(char *str, int strsize, int insert,
  196. unsigned long *num, unsigned long *eol_num,
  197. char *buf, unsigned long len)
  198. {
  199. while (strsize--) {
  200. cread_add_char(*str, insert, num, eol_num, buf, len);
  201. str++;
  202. }
  203. }
  204. static int cread_line(const char *const prompt, char *buf, unsigned int *len,
  205. int timeout)
  206. {
  207. struct cli_ch_state s_cch, *cch = &s_cch;
  208. unsigned long num = 0;
  209. unsigned long eol_num = 0;
  210. unsigned long wlen;
  211. char ichar;
  212. int insert = 1;
  213. int init_len = strlen(buf);
  214. int first = 1;
  215. cli_ch_init(cch);
  216. if (init_len)
  217. cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);
  218. while (1) {
  219. /* Check for saved characters */
  220. ichar = cli_ch_process(cch, 0);
  221. if (!ichar) {
  222. if (bootretry_tstc_timeout())
  223. return -2; /* timed out */
  224. if (first && timeout) {
  225. u64 etime = endtick(timeout);
  226. while (!tstc()) { /* while no incoming data */
  227. if (get_ticks() >= etime)
  228. return -2; /* timed out */
  229. schedule();
  230. }
  231. first = 0;
  232. }
  233. ichar = getcmd_getch();
  234. ichar = cli_ch_process(cch, ichar);
  235. }
  236. /* ichar=0x0 when error occurs in U-Boot getc */
  237. if (!ichar)
  238. continue;
  239. if (ichar == '\n') {
  240. putc('\n');
  241. break;
  242. }
  243. switch (ichar) {
  244. case CTL_CH('a'):
  245. BEGINNING_OF_LINE();
  246. break;
  247. case CTL_CH('c'): /* ^C - break */
  248. *buf = '\0'; /* discard input */
  249. return -1;
  250. case CTL_CH('f'):
  251. if (num < eol_num) {
  252. getcmd_putch(buf[num]);
  253. num++;
  254. }
  255. break;
  256. case CTL_CH('b'):
  257. if (num) {
  258. getcmd_putch(CTL_BACKSPACE);
  259. num--;
  260. }
  261. break;
  262. case CTL_CH('d'):
  263. if (num < eol_num) {
  264. wlen = eol_num - num - 1;
  265. if (wlen) {
  266. memmove(&buf[num], &buf[num+1], wlen);
  267. putnstr(buf + num, wlen);
  268. }
  269. getcmd_putch(' ');
  270. do {
  271. getcmd_putch(CTL_BACKSPACE);
  272. } while (wlen--);
  273. eol_num--;
  274. }
  275. break;
  276. case CTL_CH('k'):
  277. ERASE_TO_EOL();
  278. break;
  279. case CTL_CH('e'):
  280. REFRESH_TO_EOL();
  281. break;
  282. case CTL_CH('o'):
  283. insert = !insert;
  284. break;
  285. case CTL_CH('x'):
  286. case CTL_CH('u'):
  287. BEGINNING_OF_LINE();
  288. ERASE_TO_EOL();
  289. break;
  290. case DEL:
  291. case DEL7:
  292. case 8:
  293. if (num) {
  294. wlen = eol_num - num;
  295. num--;
  296. memmove(&buf[num], &buf[num+1], wlen);
  297. getcmd_putch(CTL_BACKSPACE);
  298. putnstr(buf + num, wlen);
  299. getcmd_putch(' ');
  300. do {
  301. getcmd_putch(CTL_BACKSPACE);
  302. } while (wlen--);
  303. eol_num--;
  304. }
  305. break;
  306. case CTL_CH('p'):
  307. case CTL_CH('n'):
  308. {
  309. char *hline;
  310. if (ichar == CTL_CH('p'))
  311. hline = hist_prev();
  312. else
  313. hline = hist_next();
  314. if (!hline) {
  315. getcmd_cbeep();
  316. continue;
  317. }
  318. /* nuke the current line */
  319. /* first, go home */
  320. BEGINNING_OF_LINE();
  321. /* erase to end of line */
  322. ERASE_TO_EOL();
  323. /* copy new line into place and display */
  324. strcpy(buf, hline);
  325. eol_num = strlen(buf);
  326. REFRESH_TO_EOL();
  327. continue;
  328. }
  329. #ifdef CONFIG_AUTO_COMPLETE
  330. case '\t': {
  331. int num2, col;
  332. /* do not autocomplete when in the middle */
  333. if (num < eol_num) {
  334. getcmd_cbeep();
  335. break;
  336. }
  337. buf[num] = '\0';
  338. col = strlen(prompt) + eol_num;
  339. num2 = num;
  340. if (cmd_auto_complete(prompt, buf, &num2, &col)) {
  341. col = num2 - num;
  342. num += col;
  343. eol_num += col;
  344. }
  345. break;
  346. }
  347. #endif
  348. default:
  349. cread_add_char(ichar, insert, &num, &eol_num, buf,
  350. *len);
  351. break;
  352. }
  353. }
  354. *len = eol_num;
  355. buf[eol_num] = '\0'; /* lose the newline */
  356. if (buf[0] && buf[0] != CREAD_HIST_CHAR)
  357. cread_add_to_hist(buf);
  358. hist_cur = hist_add_idx;
  359. return 0;
  360. }
  361. #endif /* CONFIG_CMDLINE_EDITING */
  362. /****************************************************************************/
  363. int cli_readline(const char *const prompt)
  364. {
  365. /*
  366. * If console_buffer isn't 0-length the user will be prompted to modify
  367. * it instead of entering it from scratch as desired.
  368. */
  369. console_buffer[0] = '\0';
  370. return cli_readline_into_buffer(prompt, console_buffer, 0);
  371. }
  372. int cli_readline_into_buffer(const char *const prompt, char *buffer,
  373. int timeout)
  374. {
  375. char *p = buffer;
  376. #ifdef CONFIG_CMDLINE_EDITING
  377. unsigned int len = CONFIG_SYS_CBSIZE;
  378. int rc;
  379. static int initted;
  380. /*
  381. * History uses a global array which is not
  382. * writable until after relocation to RAM.
  383. * Revert to non-history version if still
  384. * running from flash.
  385. */
  386. if (gd->flags & GD_FLG_RELOC) {
  387. if (!initted) {
  388. hist_init();
  389. initted = 1;
  390. }
  391. if (prompt)
  392. puts(prompt);
  393. rc = cread_line(prompt, p, &len, timeout);
  394. return rc < 0 ? rc : len;
  395. } else {
  396. #endif /* CONFIG_CMDLINE_EDITING */
  397. char *p_buf = p;
  398. int n = 0; /* buffer index */
  399. int plen = 0; /* prompt length */
  400. int col; /* output column cnt */
  401. char c;
  402. /* print prompt */
  403. if (prompt) {
  404. plen = strlen(prompt);
  405. puts(prompt);
  406. }
  407. col = plen;
  408. for (;;) {
  409. if (bootretry_tstc_timeout())
  410. return -2; /* timed out */
  411. schedule(); /* Trigger watchdog, if needed */
  412. c = getchar();
  413. /*
  414. * Special character handling
  415. */
  416. switch (c) {
  417. case '\r': /* Enter */
  418. case '\n':
  419. *p = '\0';
  420. puts("\r\n");
  421. return p - p_buf;
  422. case '\0': /* nul */
  423. continue;
  424. case 0x03: /* ^C - break */
  425. p_buf[0] = '\0'; /* discard input */
  426. return -1;
  427. case 0x15: /* ^U - erase line */
  428. while (col > plen) {
  429. puts(erase_seq);
  430. --col;
  431. }
  432. p = p_buf;
  433. n = 0;
  434. continue;
  435. case 0x17: /* ^W - erase word */
  436. p = delete_char(p_buf, p, &col, &n, plen);
  437. while ((n > 0) && (*p != ' '))
  438. p = delete_char(p_buf, p, &col, &n, plen);
  439. continue;
  440. case 0x08: /* ^H - backspace */
  441. case 0x7F: /* DEL - backspace */
  442. p = delete_char(p_buf, p, &col, &n, plen);
  443. continue;
  444. default:
  445. /*
  446. * Must be a normal character then
  447. */
  448. if (n < CONFIG_SYS_CBSIZE-2) {
  449. if (c == '\t') { /* expand TABs */
  450. #ifdef CONFIG_AUTO_COMPLETE
  451. /*
  452. * if auto completion triggered just
  453. * continue
  454. */
  455. *p = '\0';
  456. if (cmd_auto_complete(prompt,
  457. console_buffer,
  458. &n, &col)) {
  459. p = p_buf + n; /* reset */
  460. continue;
  461. }
  462. #endif
  463. puts(tab_seq + (col & 07));
  464. col += 8 - (col & 07);
  465. } else {
  466. char __maybe_unused buf[2];
  467. /*
  468. * Echo input using puts() to force an
  469. * LCD flush if we are using an LCD
  470. */
  471. ++col;
  472. buf[0] = c;
  473. buf[1] = '\0';
  474. puts(buf);
  475. }
  476. *p++ = c;
  477. ++n;
  478. } else { /* Buffer full */
  479. putc('\a');
  480. }
  481. }
  482. }
  483. #ifdef CONFIG_CMDLINE_EDITING
  484. }
  485. #endif
  486. }