shell.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. /*
  2. * Copyright (c) 2006-2025, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2006-04-30 Bernard the first version for FinSH
  9. * 2006-05-08 Bernard change finsh thread stack to 2048
  10. * 2006-06-03 Bernard add support for skyeye
  11. * 2006-09-24 Bernard remove the code related with hardware
  12. * 2010-01-18 Bernard fix down then up key bug.
  13. * 2010-03-19 Bernard fix backspace issue and fix device read in shell.
  14. * 2010-04-01 Bernard add prompt output when start and remove the empty history
  15. * 2011-02-23 Bernard fix variable section end issue of finsh shell
  16. * initialization when use GNU GCC compiler.
  17. * 2016-11-26 armink add password authentication
  18. * 2018-07-02 aozima add custom prompt support.
  19. */
  20. #include <rthw.h>
  21. #include <string.h>
  22. #include <stdio.h>
  23. #ifdef RT_USING_FINSH
  24. #include "shell.h"
  25. #include "msh.h"
  26. #ifdef DFS_USING_POSIX
  27. #include <unistd.h>
  28. #include <fcntl.h>
  29. #endif /* DFS_USING_POSIX */
  30. #ifdef RT_USING_POSIX_STDIO
  31. #include <unistd.h>
  32. #include <posix/stdio.h>
  33. #endif /* RT_USING_POSIX_STDIO */
  34. /* finsh thread */
  35. #ifndef RT_USING_HEAP
  36. static struct rt_thread finsh_thread;
  37. rt_align(RT_ALIGN_SIZE)
  38. static char finsh_thread_stack[FINSH_THREAD_STACK_SIZE];
  39. struct finsh_shell _shell;
  40. #endif
  41. /* finsh symtab */
  42. #ifdef FINSH_USING_SYMTAB
  43. struct finsh_syscall *_syscall_table_begin = NULL;
  44. struct finsh_syscall *_syscall_table_end = NULL;
  45. #endif
  46. struct finsh_shell *shell;
  47. static char *finsh_prompt_custom = RT_NULL;
  48. #if defined(_MSC_VER) || (defined(__GNUC__) && defined(__x86_64__))
  49. struct finsh_syscall *finsh_syscall_next(struct finsh_syscall *call)
  50. {
  51. unsigned int *ptr;
  52. ptr = (unsigned int *)(call + 1);
  53. while ((*ptr == 0) && ((unsigned int *)ptr < (unsigned int *) _syscall_table_end))
  54. ptr ++;
  55. return (struct finsh_syscall *)ptr;
  56. }
  57. #endif /* defined(_MSC_VER) || (defined(__GNUC__) && defined(__x86_64__)) */
  58. #ifdef RT_USING_HEAP
  59. int finsh_set_prompt(const char *prompt)
  60. {
  61. if (finsh_prompt_custom)
  62. {
  63. rt_free(finsh_prompt_custom);
  64. finsh_prompt_custom = RT_NULL;
  65. }
  66. /* strdup */
  67. if (prompt)
  68. {
  69. finsh_prompt_custom = (char *)rt_malloc(strlen(prompt) + 1);
  70. if (finsh_prompt_custom)
  71. {
  72. strcpy(finsh_prompt_custom, prompt);
  73. }
  74. }
  75. return 0;
  76. }
  77. #endif /* RT_USING_HEAP */
  78. #define _MSH_PROMPT "msh "
  79. const char *finsh_get_prompt(void)
  80. {
  81. static char finsh_prompt[RT_CONSOLEBUF_SIZE + 1] = {0};
  82. /* check prompt mode */
  83. if (!shell->prompt_mode)
  84. {
  85. finsh_prompt[0] = '\0';
  86. return finsh_prompt;
  87. }
  88. if (finsh_prompt_custom)
  89. {
  90. strncpy(finsh_prompt, finsh_prompt_custom, sizeof(finsh_prompt) - 1);
  91. }
  92. else
  93. {
  94. strcpy(finsh_prompt, _MSH_PROMPT);
  95. }
  96. #if defined(DFS_USING_POSIX) && defined(DFS_USING_WORKDIR)
  97. /* get current working directory */
  98. getcwd(&finsh_prompt[rt_strlen(finsh_prompt)], RT_CONSOLEBUF_SIZE - rt_strlen(finsh_prompt));
  99. #endif
  100. if (rt_strlen(finsh_prompt) + 2 < RT_CONSOLEBUF_SIZE)
  101. {
  102. finsh_prompt[rt_strlen(finsh_prompt)] = '>';
  103. finsh_prompt[rt_strlen(finsh_prompt) + 1] = '\0';
  104. }
  105. return finsh_prompt;
  106. }
  107. /**
  108. * @ingroup group_finsh
  109. *
  110. * This function get the prompt mode of finsh shell.
  111. *
  112. * @return prompt the prompt mode, 0 disable prompt mode, other values enable prompt mode.
  113. */
  114. rt_uint32_t finsh_get_prompt_mode(void)
  115. {
  116. RT_ASSERT(shell != RT_NULL);
  117. return shell->prompt_mode;
  118. }
  119. /**
  120. * @ingroup group_finsh
  121. *
  122. * This function set the prompt mode of finsh shell.
  123. *
  124. * The parameter 0 disable prompt mode, other values enable prompt mode.
  125. *
  126. * @param prompt_mode the prompt mode
  127. */
  128. void finsh_set_prompt_mode(rt_uint32_t prompt_mode)
  129. {
  130. RT_ASSERT(shell != RT_NULL);
  131. shell->prompt_mode = prompt_mode;
  132. }
  133. int finsh_getchar(void)
  134. {
  135. #ifdef RT_USING_DEVICE
  136. char ch = 0;
  137. #ifdef RT_USING_POSIX_STDIO
  138. if(read(rt_posix_stdio_get_console(), &ch, 1) > 0)
  139. {
  140. return ch;
  141. }
  142. else
  143. {
  144. return -1; /* EOF */
  145. }
  146. #else
  147. rt_device_t device;
  148. RT_ASSERT(shell != RT_NULL);
  149. device = shell->device;
  150. if (device == RT_NULL)
  151. {
  152. return -1; /* EOF */
  153. }
  154. while (rt_device_read(device, -1, &ch, 1) != 1)
  155. {
  156. rt_sem_take(&shell->rx_sem, RT_WAITING_FOREVER);
  157. if (shell->device != device)
  158. {
  159. device = shell->device;
  160. if (device == RT_NULL)
  161. {
  162. return -1;
  163. }
  164. }
  165. }
  166. return ch;
  167. #endif /* RT_USING_POSIX_STDIO */
  168. #else
  169. extern signed char rt_hw_console_getchar(void);
  170. return rt_hw_console_getchar();
  171. #endif /* RT_USING_DEVICE */
  172. }
  173. #if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE)
  174. static rt_err_t finsh_rx_ind(rt_device_t dev, rt_size_t size)
  175. {
  176. RT_ASSERT(shell != RT_NULL);
  177. /* release semaphore to let finsh thread rx data */
  178. rt_sem_release(&shell->rx_sem);
  179. return RT_EOK;
  180. }
  181. /**
  182. * @ingroup group_finsh
  183. *
  184. * This function sets the input device of finsh shell.
  185. *
  186. * @param device_name the name of new input device.
  187. */
  188. void finsh_set_device(const char *device_name)
  189. {
  190. rt_device_t dev = RT_NULL;
  191. RT_ASSERT(shell != RT_NULL);
  192. dev = rt_device_find(device_name);
  193. if (dev == RT_NULL)
  194. {
  195. rt_kprintf("finsh: can not find device: %s\n", device_name);
  196. return;
  197. }
  198. /* check whether it's a same device */
  199. if (dev == shell->device) return;
  200. /* open this device and set the new device in finsh shell */
  201. if (rt_device_open(dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | \
  202. RT_DEVICE_FLAG_STREAM) == RT_EOK)
  203. {
  204. if (shell->device != RT_NULL)
  205. {
  206. /* close old finsh device */
  207. rt_device_close(shell->device);
  208. rt_device_set_rx_indicate(shell->device, RT_NULL);
  209. }
  210. /* clear line buffer before switch to new device */
  211. rt_memset(shell->line, 0, sizeof(shell->line));
  212. shell->line_curpos = shell->line_position = 0;
  213. shell->device = dev;
  214. rt_device_set_rx_indicate(dev, finsh_rx_ind);
  215. }
  216. }
  217. /**
  218. * @ingroup group_finsh
  219. *
  220. * This function returns current finsh shell input device.
  221. *
  222. * @return the finsh shell input device name is returned.
  223. */
  224. const char *finsh_get_device()
  225. {
  226. RT_ASSERT(shell != RT_NULL);
  227. return shell->device->parent.name;
  228. }
  229. #endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */
  230. /**
  231. * @ingroup group_finsh
  232. *
  233. * This function set the echo mode of finsh shell.
  234. *
  235. * FINSH_OPTION_ECHO=0x01 is echo mode, other values are none-echo mode.
  236. *
  237. * @param echo the echo mode
  238. */
  239. void finsh_set_echo(rt_uint32_t echo)
  240. {
  241. RT_ASSERT(shell != RT_NULL);
  242. shell->echo_mode = (rt_uint8_t)echo;
  243. }
  244. /**
  245. * @ingroup group_finsh
  246. *
  247. * This function gets the echo mode of finsh shell.
  248. *
  249. * @return the echo mode
  250. */
  251. rt_uint32_t finsh_get_echo()
  252. {
  253. RT_ASSERT(shell != RT_NULL);
  254. return shell->echo_mode;
  255. }
  256. #ifdef FINSH_USING_AUTH
  257. /**
  258. * set a new password for finsh
  259. *
  260. * @param password new password
  261. *
  262. * @return result, RT_EOK on OK, -RT_ERROR on the new password length is less than
  263. * FINSH_PASSWORD_MIN or greater than FINSH_PASSWORD_MAX
  264. */
  265. rt_err_t finsh_set_password(const char *password)
  266. {
  267. rt_base_t level;
  268. rt_size_t pw_len = rt_strlen(password);
  269. if (pw_len < FINSH_PASSWORD_MIN || pw_len > FINSH_PASSWORD_MAX)
  270. return -RT_ERROR;
  271. level = rt_hw_interrupt_disable();
  272. rt_strncpy(shell->password, password, FINSH_PASSWORD_MAX);
  273. rt_hw_interrupt_enable(level);
  274. return RT_EOK;
  275. }
  276. /**
  277. * get the finsh password
  278. *
  279. * @return password
  280. */
  281. const char *finsh_get_password(void)
  282. {
  283. return shell->password;
  284. }
  285. static void finsh_wait_auth(void)
  286. {
  287. int ch;
  288. rt_bool_t input_finish = RT_FALSE;
  289. char password[FINSH_PASSWORD_MAX] = { 0 };
  290. rt_size_t cur_pos = 0;
  291. /* password not set */
  292. if (rt_strlen(finsh_get_password()) == 0) return;
  293. while (1)
  294. {
  295. rt_kprintf("Password for login: ");
  296. while (!input_finish)
  297. {
  298. while (1)
  299. {
  300. /* read one character from device */
  301. ch = (int)finsh_getchar();
  302. if (ch < 0)
  303. {
  304. continue;
  305. }
  306. if (ch >= ' ' && ch <= '~' && cur_pos < FINSH_PASSWORD_MAX)
  307. {
  308. /* change the printable characters to '*' */
  309. rt_kprintf("*");
  310. password[cur_pos++] = ch;
  311. }
  312. else if (ch == '\b' && cur_pos > 0)
  313. {
  314. /* backspace */
  315. cur_pos--;
  316. password[cur_pos] = '\0';
  317. rt_kprintf("\b \b");
  318. }
  319. else if (ch == '\r' || ch == '\n')
  320. {
  321. rt_kprintf("\n");
  322. input_finish = RT_TRUE;
  323. break;
  324. }
  325. }
  326. }
  327. if (!rt_strncmp(shell->password, password, FINSH_PASSWORD_MAX)) return;
  328. else
  329. {
  330. /* authentication failed, delay 2S for retry */
  331. rt_thread_delay(2 * RT_TICK_PER_SECOND);
  332. rt_kprintf("Sorry, try again.\n");
  333. cur_pos = 0;
  334. input_finish = RT_FALSE;
  335. rt_memset(password, '\0', FINSH_PASSWORD_MAX);
  336. }
  337. }
  338. }
  339. #endif /* FINSH_USING_AUTH */
  340. static void shell_auto_complete(char *prefix)
  341. {
  342. rt_kprintf("\n");
  343. msh_auto_complete(prefix);
  344. #ifdef FINSH_USING_OPTION_COMPLETION
  345. msh_opt_auto_complete(prefix);
  346. #endif
  347. rt_kprintf("%s%s", FINSH_PROMPT, prefix);
  348. }
  349. #ifdef FINSH_USING_HISTORY
  350. static rt_bool_t shell_handle_history(struct finsh_shell *shell)
  351. {
  352. #if defined(_WIN32)
  353. int i;
  354. rt_kprintf("\r");
  355. for (i = 0; i <= 60; i++)
  356. putchar(' ');
  357. rt_kprintf("\r");
  358. #else
  359. rt_kprintf("\033[2K\r");
  360. #endif
  361. rt_kprintf("%s%s", FINSH_PROMPT, shell->line);
  362. return RT_FALSE;
  363. }
  364. static void shell_push_history(struct finsh_shell *shell)
  365. {
  366. if (shell->line_position != 0)
  367. {
  368. /* push history */
  369. if (shell->history_count >= FINSH_HISTORY_LINES)
  370. {
  371. /* if current cmd is same as last cmd, don't push */
  372. if (memcmp(&shell->cmd_history[FINSH_HISTORY_LINES - 1], shell->line, FINSH_CMD_SIZE))
  373. {
  374. /* move history */
  375. int index;
  376. for (index = 0; index < FINSH_HISTORY_LINES - 1; index ++)
  377. {
  378. rt_memcpy(&shell->cmd_history[index][0],
  379. &shell->cmd_history[index + 1][0], FINSH_CMD_SIZE);
  380. }
  381. rt_memset(&shell->cmd_history[index][0], 0, FINSH_CMD_SIZE);
  382. rt_memcpy(&shell->cmd_history[index][0], shell->line, shell->line_position);
  383. /* it's the maximum history */
  384. shell->history_count = FINSH_HISTORY_LINES;
  385. }
  386. }
  387. else
  388. {
  389. /* if current cmd is same as last cmd, don't push */
  390. if (shell->history_count == 0 || memcmp(&shell->cmd_history[shell->history_count - 1], shell->line, FINSH_CMD_SIZE))
  391. {
  392. shell->current_history = shell->history_count;
  393. rt_memset(&shell->cmd_history[shell->history_count][0], 0, FINSH_CMD_SIZE);
  394. rt_memcpy(&shell->cmd_history[shell->history_count][0], shell->line, shell->line_position);
  395. /* increase count and set current history position */
  396. shell->history_count ++;
  397. }
  398. }
  399. }
  400. shell->current_history = shell->history_count;
  401. }
  402. #endif
  403. #if defined(FINSH_USING_WORD_OPERATION)
  404. static int find_prev_word_start(const char *line, int curpos)
  405. {
  406. if (curpos <= 0) return 0;
  407. /* Skip whitespace */
  408. while (--curpos > 0 && (line[curpos] == ' ' || line[curpos] == '\t'));
  409. /* Find word start */
  410. while (curpos > 0 && !(line[curpos] == ' ' || line[curpos] == '\t'))
  411. curpos--;
  412. return (curpos <= 0) ? 0 : curpos + 1;
  413. }
  414. static int find_next_word_end(const char *line, int curpos, int max)
  415. {
  416. if (curpos >= max) return max;
  417. /* Skip to next word */
  418. while (curpos < max && (line[curpos] == ' ' || line[curpos] == '\t'))
  419. curpos++;
  420. /* Find word end */
  421. while (curpos < max && !(line[curpos] == ' ' || line[curpos] == '\t'))
  422. curpos++;
  423. return curpos;
  424. }
  425. #endif /* defined(FINSH_USING_WORD_OPERATION) */
  426. #ifdef RT_USING_HOOK
  427. static void (*_finsh_thread_entry_hook)(void);
  428. /**
  429. * @ingroup group_finsh
  430. *
  431. * @brief This function set a hook function at the entry of finsh thread
  432. *
  433. * @param hook the function point to be called
  434. */
  435. void finsh_thread_entry_sethook(void (*hook)(void))
  436. {
  437. _finsh_thread_entry_hook = hook;
  438. }
  439. #endif /* RT_USING_HOOK */
  440. static void finsh_thread_entry(void *parameter)
  441. {
  442. int ch;
  443. RT_OBJECT_HOOK_CALL(_finsh_thread_entry_hook, ());
  444. /* normal is echo mode */
  445. #ifndef FINSH_ECHO_DISABLE_DEFAULT
  446. shell->echo_mode = 1;
  447. #else
  448. shell->echo_mode = 0;
  449. #endif
  450. #if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE)
  451. /* set console device as shell device */
  452. if (shell->device == RT_NULL)
  453. {
  454. rt_device_t console = rt_console_get_device();
  455. if (console)
  456. {
  457. finsh_set_device(console->parent.name);
  458. }
  459. }
  460. #endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */
  461. #ifdef FINSH_USING_AUTH
  462. /* set the default password when the password isn't setting */
  463. if (rt_strlen(finsh_get_password()) == 0)
  464. {
  465. if (finsh_set_password(FINSH_DEFAULT_PASSWORD) != RT_EOK)
  466. {
  467. rt_kprintf("Finsh password set failed.\n");
  468. }
  469. }
  470. /* waiting authenticate success */
  471. finsh_wait_auth();
  472. #endif
  473. rt_kprintf(FINSH_PROMPT);
  474. while (1)
  475. {
  476. ch = (int)finsh_getchar();
  477. if (ch < 0)
  478. {
  479. continue;
  480. }
  481. /*
  482. * handle control key
  483. * up key : 0x1b 0x5b 0x41
  484. * down key: 0x1b 0x5b 0x42
  485. * right key:0x1b 0x5b 0x43
  486. * left key: 0x1b 0x5b 0x44
  487. */
  488. if (ch == 0x1b)
  489. {
  490. shell->stat = WAIT_SPEC_KEY;
  491. continue;
  492. }
  493. else if (shell->stat == WAIT_SPEC_KEY)
  494. {
  495. if (ch == 0x5b || ch == 0x41 || ch == 0x42 || ch == 0x43 || ch == 0x44)
  496. {
  497. shell->stat = WAIT_FUNC_KEY;
  498. continue;
  499. }
  500. shell->stat = WAIT_NORMAL;
  501. }
  502. else if (shell->stat == WAIT_FUNC_KEY)
  503. {
  504. shell->stat = WAIT_NORMAL;
  505. if (ch == 0x41) /* up key */
  506. {
  507. #ifdef FINSH_USING_HISTORY
  508. /* prev history */
  509. if (shell->current_history > 0)
  510. shell->current_history --;
  511. else
  512. {
  513. shell->current_history = 0;
  514. continue;
  515. }
  516. /* copy the history command */
  517. rt_memcpy(shell->line, &shell->cmd_history[shell->current_history][0],
  518. FINSH_CMD_SIZE);
  519. shell->line_curpos = shell->line_position = (rt_uint16_t)strlen(shell->line);
  520. shell_handle_history(shell);
  521. #endif
  522. continue;
  523. }
  524. else if (ch == 0x42) /* down key */
  525. {
  526. #ifdef FINSH_USING_HISTORY
  527. /* next history */
  528. if (shell->current_history < shell->history_count - 1)
  529. shell->current_history ++;
  530. else
  531. {
  532. /* set to the end of history */
  533. if (shell->history_count != 0)
  534. shell->current_history = shell->history_count - 1;
  535. else
  536. continue;
  537. }
  538. rt_memcpy(shell->line, &shell->cmd_history[shell->current_history][0],
  539. FINSH_CMD_SIZE);
  540. shell->line_curpos = shell->line_position = (rt_uint16_t)strlen(shell->line);
  541. shell_handle_history(shell);
  542. #endif
  543. continue;
  544. }
  545. else if (ch == 0x44) /* left key */
  546. {
  547. if (shell->line_curpos)
  548. {
  549. rt_kprintf("\b");
  550. shell->line_curpos --;
  551. }
  552. continue;
  553. }
  554. else if (ch == 0x43) /* right key */
  555. {
  556. if (shell->line_curpos < shell->line_position)
  557. {
  558. rt_kprintf("%c", shell->line[shell->line_curpos]);
  559. shell->line_curpos ++;
  560. }
  561. continue;
  562. }
  563. #if defined(FINSH_USING_WORD_OPERATION)
  564. /* Add Ctrl+Left/Right handling */
  565. else if (ch == '1')
  566. {
  567. /* Read modifier sequence [1;5D/C] */
  568. int next_ch = finsh_getchar();
  569. if (next_ch == ';')
  570. {
  571. next_ch = finsh_getchar();
  572. if (next_ch == '5')
  573. {
  574. next_ch = finsh_getchar();
  575. if (next_ch == 'D') /* Ctrl+Left */
  576. {
  577. int new_pos = find_prev_word_start(shell->line, shell->line_curpos);
  578. if (new_pos != shell->line_curpos)
  579. {
  580. rt_kprintf("\033[%dD", shell->line_curpos - new_pos);
  581. shell->line_curpos = new_pos;
  582. }
  583. continue;
  584. }
  585. else if (next_ch == 'C') /* Ctrl+Right */
  586. {
  587. int new_pos = find_next_word_end(shell->line, shell->line_curpos, shell->line_position);
  588. if (new_pos != shell->line_curpos)
  589. {
  590. rt_kprintf("\033[%dC", new_pos - shell->line_curpos);
  591. shell->line_curpos = new_pos;
  592. }
  593. continue;
  594. }
  595. }
  596. }
  597. }
  598. #endif /*defined(FINSH_USING_WORD_OPERATION) */
  599. }
  600. /* received null or error */
  601. if (ch == '\0' || ch == 0xFF) continue;
  602. /* handle tab key */
  603. else if (ch == '\t')
  604. {
  605. int i;
  606. /* move the cursor to the beginning of line */
  607. for (i = 0; i < shell->line_curpos; i++)
  608. rt_kprintf("\b");
  609. /* auto complete */
  610. shell_auto_complete(&shell->line[0]);
  611. /* re-calculate position */
  612. shell->line_curpos = shell->line_position = (rt_uint16_t)strlen(shell->line);
  613. continue;
  614. }
  615. /* handle backspace key */
  616. else if (ch == 0x7f || ch == 0x08)
  617. {
  618. /* note that shell->line_curpos >= 0 */
  619. if (shell->line_curpos == 0)
  620. continue;
  621. shell->line_position--;
  622. shell->line_curpos--;
  623. if (shell->line_position > shell->line_curpos)
  624. {
  625. int i;
  626. rt_memmove(&shell->line[shell->line_curpos],
  627. &shell->line[shell->line_curpos + 1],
  628. shell->line_position - shell->line_curpos);
  629. shell->line[shell->line_position] = 0;
  630. rt_kprintf("\b%s \b", &shell->line[shell->line_curpos]);
  631. /* move the cursor to the origin position */
  632. for (i = shell->line_curpos; i <= shell->line_position; i++)
  633. rt_kprintf("\b");
  634. }
  635. else
  636. {
  637. rt_kprintf("\b \b");
  638. shell->line[shell->line_position] = 0;
  639. }
  640. continue;
  641. }
  642. #if defined(FINSH_USING_WORD_OPERATION)
  643. /* Add Ctrl+Backspace handling */
  644. else if (ch == 0x17) /* Ctrl+Backspace (typically ^W) */
  645. {
  646. if (shell->line_curpos == 0) continue;
  647. int start = find_prev_word_start(shell->line, shell->line_curpos);
  648. int del_count = shell->line_curpos - start;
  649. int new_len = shell->line_position - del_count;
  650. /* Delete characters and properly add RT_NULL termination */
  651. rt_memmove(&shell->line[start],
  652. &shell->line[start + del_count],
  653. new_len - start + 1);
  654. /* Clear residual data */
  655. rt_memset(&shell->line[new_len], 0, shell->line_position - new_len);
  656. /* Update positions */
  657. shell->line_position = new_len;
  658. shell->line_curpos = start;
  659. /* Redraw the affected line section */
  660. rt_kprintf("\033[%dD", del_count);
  661. /* Rewrite the remaining content */
  662. rt_kprintf("%.*s", shell->line_position - start, &shell->line[start]);
  663. /* Clear trailing artifacts */
  664. rt_kprintf("\033[K");
  665. if (shell->line_position > start)
  666. {
  667. /* Reset cursor */
  668. rt_kprintf("\033[%dD", shell->line_position - start);
  669. }
  670. continue;
  671. }
  672. #endif /*defined(FINSH_USING_WORD_OPERATION) */
  673. /* handle end of line, break */
  674. if (ch == '\r' || ch == '\n')
  675. {
  676. #ifdef FINSH_USING_HISTORY
  677. shell_push_history(shell);
  678. #endif
  679. if (shell->echo_mode)
  680. rt_kprintf("\n");
  681. msh_exec(shell->line, shell->line_position);
  682. rt_kprintf(FINSH_PROMPT);
  683. rt_memset(shell->line, 0, sizeof(shell->line));
  684. shell->line_curpos = shell->line_position = 0;
  685. continue;
  686. }
  687. /* it's a large line, discard it */
  688. if (shell->line_position >= FINSH_CMD_SIZE)
  689. shell->line_position = 0;
  690. /* normal character */
  691. if (shell->line_curpos < shell->line_position)
  692. {
  693. int i;
  694. rt_memmove(&shell->line[shell->line_curpos + 1],
  695. &shell->line[shell->line_curpos],
  696. shell->line_position - shell->line_curpos);
  697. shell->line[shell->line_curpos] = ch;
  698. if (shell->echo_mode)
  699. rt_kprintf("%s", &shell->line[shell->line_curpos]);
  700. /* move the cursor to new position */
  701. for (i = shell->line_curpos; i < shell->line_position; i++)
  702. rt_kprintf("\b");
  703. }
  704. else
  705. {
  706. shell->line[shell->line_position] = ch;
  707. if (shell->echo_mode)
  708. rt_kprintf("%c", ch);
  709. }
  710. ch = 0;
  711. shell->line_position ++;
  712. shell->line_curpos++;
  713. if (shell->line_position >= FINSH_CMD_SIZE)
  714. {
  715. /* clear command line */
  716. shell->line_position = 0;
  717. shell->line_curpos = 0;
  718. }
  719. } /* end of device read */
  720. }
  721. static void finsh_system_function_init(const void *begin, const void *end)
  722. {
  723. _syscall_table_begin = (struct finsh_syscall *) begin;
  724. _syscall_table_end = (struct finsh_syscall *) end;
  725. }
  726. #if defined(__ICCARM__) || defined(__ICCRX__) /* for IAR compiler */
  727. #ifdef FINSH_USING_SYMTAB
  728. #pragma section="FSymTab"
  729. #endif
  730. #elif defined(__ADSPBLACKFIN__) /* for VisaulDSP++ Compiler*/
  731. #ifdef FINSH_USING_SYMTAB
  732. extern "asm" int __fsymtab_start;
  733. extern "asm" int __fsymtab_end;
  734. #endif
  735. #elif defined(_MSC_VER)
  736. #pragma section("FSymTab$a", read)
  737. const char __fsym_begin_name[] = "__start";
  738. const char __fsym_begin_desc[] = "begin of finsh";
  739. __declspec(allocate("FSymTab$a")) const struct finsh_syscall __fsym_begin =
  740. {
  741. __fsym_begin_name,
  742. __fsym_begin_desc,
  743. NULL
  744. };
  745. #pragma section("FSymTab$z", read)
  746. const char __fsym_end_name[] = "__end";
  747. const char __fsym_end_desc[] = "end of finsh";
  748. __declspec(allocate("FSymTab$z")) const struct finsh_syscall __fsym_end =
  749. {
  750. __fsym_end_name,
  751. __fsym_end_desc,
  752. NULL
  753. };
  754. #endif
  755. /*
  756. * @ingroup group_finsh
  757. *
  758. * This function will initialize finsh shell
  759. */
  760. int finsh_system_init(void)
  761. {
  762. rt_err_t result = RT_EOK;
  763. rt_thread_t tid;
  764. #ifdef FINSH_USING_SYMTAB
  765. #ifdef __ARMCC_VERSION /* ARM C Compiler */
  766. extern const int FSymTab$$Base;
  767. extern const int FSymTab$$Limit;
  768. finsh_system_function_init(&FSymTab$$Base, &FSymTab$$Limit);
  769. #elif defined (__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */
  770. finsh_system_function_init(__section_begin("FSymTab"),
  771. __section_end("FSymTab"));
  772. #elif defined (__GNUC__) || defined(__TI_COMPILER_VERSION__) || defined(__TASKING__)
  773. /* GNU GCC Compiler and TI CCS */
  774. extern const int __fsymtab_start;
  775. extern const int __fsymtab_end;
  776. finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
  777. #elif defined(__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */
  778. finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
  779. #elif defined(_MSC_VER)
  780. unsigned int *ptr_begin, *ptr_end;
  781. if (shell)
  782. {
  783. rt_kprintf("finsh shell already init.\n");
  784. return RT_EOK;
  785. }
  786. ptr_begin = (unsigned int *)&__fsym_begin;
  787. ptr_begin += (sizeof(struct finsh_syscall) / sizeof(unsigned int));
  788. while (*ptr_begin == 0) ptr_begin ++;
  789. ptr_end = (unsigned int *) &__fsym_end;
  790. ptr_end --;
  791. while (*ptr_end == 0) ptr_end --;
  792. finsh_system_function_init(ptr_begin, ptr_end);
  793. #endif
  794. #endif
  795. #ifdef RT_USING_HEAP
  796. /* create or set shell structure */
  797. shell = (struct finsh_shell *)rt_calloc(1, sizeof(struct finsh_shell));
  798. if (shell == RT_NULL)
  799. {
  800. rt_kprintf("no memory for shell\n");
  801. return -1;
  802. }
  803. tid = rt_thread_create(FINSH_THREAD_NAME,
  804. finsh_thread_entry, RT_NULL,
  805. FINSH_THREAD_STACK_SIZE, FINSH_THREAD_PRIORITY, 10);
  806. #else
  807. shell = &_shell;
  808. tid = &finsh_thread;
  809. result = rt_thread_init(&finsh_thread,
  810. FINSH_THREAD_NAME,
  811. finsh_thread_entry, RT_NULL,
  812. &finsh_thread_stack[0], sizeof(finsh_thread_stack),
  813. FINSH_THREAD_PRIORITY, 10);
  814. #endif /* RT_USING_HEAP */
  815. rt_sem_init(&(shell->rx_sem), "shrx", 0, 0);
  816. finsh_set_prompt_mode(1);
  817. if (tid != NULL && result == RT_EOK)
  818. rt_thread_startup(tid);
  819. return 0;
  820. }
  821. INIT_APP_EXPORT(finsh_system_init);
  822. #endif /* RT_USING_FINSH */