shell.c 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  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. * home : 0x1b 0x5b 0x31 0x7E
  488. * insert : 0x1b 0x5b 0x32 0x7E
  489. * del : 0x1b 0x5b 0x33 0x7E
  490. * end : 0x1b 0x5b 0x34 0x7E
  491. */
  492. if (ch == 0x1b)
  493. {
  494. shell->stat = WAIT_SPEC_KEY;
  495. continue;
  496. }
  497. else if (shell->stat == WAIT_SPEC_KEY)
  498. {
  499. if (ch == 0x5b || ch == 0x41 || ch == 0x42 || ch == 0x43 || ch == 0x44)
  500. {
  501. shell->stat = WAIT_FUNC_KEY;
  502. continue;
  503. }
  504. shell->stat = WAIT_NORMAL;
  505. }
  506. else if (shell->stat == WAIT_FUNC_KEY)
  507. {
  508. shell->stat = WAIT_NORMAL;
  509. if (ch == 0x41) /* up key */
  510. {
  511. #ifdef FINSH_USING_HISTORY
  512. /* prev history */
  513. if (shell->current_history > 0)
  514. shell->current_history --;
  515. else
  516. {
  517. shell->current_history = 0;
  518. continue;
  519. }
  520. /* copy the history command */
  521. rt_memcpy(shell->line, &shell->cmd_history[shell->current_history][0],
  522. FINSH_CMD_SIZE);
  523. shell->line_curpos = shell->line_position = (rt_uint16_t)strlen(shell->line);
  524. shell_handle_history(shell);
  525. #endif
  526. continue;
  527. }
  528. else if (ch == 0x42) /* down key */
  529. {
  530. #ifdef FINSH_USING_HISTORY
  531. /* next history */
  532. if (shell->current_history < shell->history_count - 1)
  533. shell->current_history ++;
  534. else
  535. {
  536. /* set to the end of history */
  537. if (shell->history_count != 0)
  538. shell->current_history = shell->history_count - 1;
  539. else
  540. continue;
  541. }
  542. rt_memcpy(shell->line, &shell->cmd_history[shell->current_history][0],
  543. FINSH_CMD_SIZE);
  544. shell->line_curpos = shell->line_position = (rt_uint16_t)strlen(shell->line);
  545. shell_handle_history(shell);
  546. #endif
  547. continue;
  548. }
  549. else if (ch == 0x44) /* left key */
  550. {
  551. if (shell->line_curpos)
  552. {
  553. rt_kprintf("\b");
  554. shell->line_curpos --;
  555. }
  556. continue;
  557. }
  558. else if (ch == 0x43) /* right key */
  559. {
  560. if (shell->line_curpos < shell->line_position)
  561. {
  562. rt_kprintf("%c", shell->line[shell->line_curpos]);
  563. shell->line_curpos ++;
  564. }
  565. continue;
  566. }
  567. #if defined(FINSH_USING_WORD_OPERATION)
  568. /* Add Ctrl+Left/Right handling */
  569. else if (ch == '1')
  570. {
  571. /* Read modifier sequence [1;5D/C] */
  572. int next_ch = finsh_getchar();
  573. if (next_ch == ';')
  574. {
  575. next_ch = finsh_getchar();
  576. if (next_ch == '5')
  577. {
  578. next_ch = finsh_getchar();
  579. if (next_ch == 'D') /* Ctrl+Left */
  580. {
  581. int new_pos = find_prev_word_start(shell->line, shell->line_curpos);
  582. if (new_pos != shell->line_curpos)
  583. {
  584. rt_kprintf("\033[%dD", shell->line_curpos - new_pos);
  585. shell->line_curpos = new_pos;
  586. }
  587. continue;
  588. }
  589. else if (next_ch == 'C') /* Ctrl+Right */
  590. {
  591. int new_pos = find_next_word_end(shell->line, shell->line_curpos, shell->line_position);
  592. if (new_pos != shell->line_curpos)
  593. {
  594. rt_kprintf("\033[%dC", new_pos - shell->line_curpos);
  595. shell->line_curpos = new_pos;
  596. }
  597. continue;
  598. }
  599. }
  600. }
  601. }
  602. #endif /*defined(FINSH_USING_WORD_OPERATION) */
  603. #if defined(FINSH_USING_FUNC_EXT)
  604. else if (ch >= 0x31 && ch <= 0x34) /* home(0x31), insert(0x32), del(0x33), end(0x34) */
  605. {
  606. shell->stat = WAIT_EXT_KEY;
  607. shell->line[shell->line_position + 1] = ch; /* store the key code */
  608. continue;
  609. }
  610. }
  611. else if (shell->stat == WAIT_EXT_KEY)
  612. {
  613. shell->stat = WAIT_NORMAL;
  614. if (ch == 0x7E) /* extended key terminator */
  615. {
  616. rt_uint8_t key_code = shell->line[shell->line_position + 1];
  617. if (key_code == 0x31) /* home key */
  618. {
  619. /* move cursor to beginning of line */
  620. while (shell->line_curpos > 0)
  621. {
  622. rt_kprintf("\b");
  623. shell->line_curpos--;
  624. }
  625. }
  626. else if (key_code == 0x32) /* insert key */
  627. {
  628. /* toggle insert mode */
  629. shell->overwrite_mode = !shell->overwrite_mode;
  630. }
  631. else if (key_code == 0x33) /* del key */
  632. {
  633. /* delete character at current cursor position */
  634. if (shell->line_curpos < shell->line_position)
  635. {
  636. int i;
  637. shell->line_position--;
  638. rt_memmove(&shell->line[shell->line_curpos],
  639. &shell->line[shell->line_curpos + 1],
  640. shell->line_position - shell->line_curpos);
  641. shell->line[shell->line_position] = 0;
  642. rt_kprintf("%s ", &shell->line[shell->line_curpos]);
  643. /* move cursor back to original position */
  644. for (i = shell->line_curpos; i <= shell->line_position; i++)
  645. rt_kprintf("\b");
  646. }
  647. }
  648. else if (key_code == 0x34) /* end key */
  649. {
  650. /* move cursor to end of line */
  651. while (shell->line_curpos < shell->line_position)
  652. {
  653. rt_kprintf("%c", shell->line[shell->line_curpos]);
  654. shell->line_curpos++;
  655. }
  656. }
  657. continue;
  658. }
  659. #endif /*defined(FINSH_USING_FUNC_EXT) */
  660. }
  661. /* received null or error */
  662. if (ch == '\0' || ch == 0xFF) continue;
  663. /* handle tab key */
  664. else if (ch == '\t')
  665. {
  666. int i;
  667. /* move the cursor to the beginning of line */
  668. for (i = 0; i < shell->line_curpos; i++)
  669. rt_kprintf("\b");
  670. /* auto complete */
  671. shell_auto_complete(&shell->line[0]);
  672. /* re-calculate position */
  673. shell->line_curpos = shell->line_position = (rt_uint16_t)strlen(shell->line);
  674. continue;
  675. }
  676. /* handle backspace key */
  677. else if (ch == 0x7f || ch == 0x08)
  678. {
  679. /* note that shell->line_curpos >= 0 */
  680. if (shell->line_curpos == 0)
  681. continue;
  682. shell->line_position--;
  683. shell->line_curpos--;
  684. if (shell->line_position > shell->line_curpos)
  685. {
  686. int i;
  687. rt_memmove(&shell->line[shell->line_curpos],
  688. &shell->line[shell->line_curpos + 1],
  689. shell->line_position - shell->line_curpos);
  690. shell->line[shell->line_position] = 0;
  691. rt_kprintf("\b%s \b", &shell->line[shell->line_curpos]);
  692. /* move the cursor to the origin position */
  693. for (i = shell->line_curpos; i <= shell->line_position; i++)
  694. rt_kprintf("\b");
  695. }
  696. else
  697. {
  698. rt_kprintf("\b \b");
  699. shell->line[shell->line_position] = 0;
  700. }
  701. continue;
  702. }
  703. #if defined(FINSH_USING_WORD_OPERATION)
  704. /* Add Ctrl+Backspace handling */
  705. else if (ch == 0x17) /* Ctrl+Backspace (typically ^W) */
  706. {
  707. if (shell->line_curpos == 0) continue;
  708. int start = find_prev_word_start(shell->line, shell->line_curpos);
  709. int del_count = shell->line_curpos - start;
  710. int new_len = shell->line_position - del_count;
  711. /* Delete characters and properly add RT_NULL termination */
  712. rt_memmove(&shell->line[start],
  713. &shell->line[start + del_count],
  714. new_len - start + 1);
  715. /* Clear residual data */
  716. rt_memset(&shell->line[new_len], 0, shell->line_position - new_len);
  717. /* Update positions */
  718. shell->line_position = new_len;
  719. shell->line_curpos = start;
  720. /* Redraw the affected line section */
  721. rt_kprintf("\033[%dD", del_count);
  722. /* Rewrite the remaining content */
  723. rt_kprintf("%.*s", shell->line_position - start, &shell->line[start]);
  724. /* Clear trailing artifacts */
  725. rt_kprintf("\033[K");
  726. if (shell->line_position > start)
  727. {
  728. /* Reset cursor */
  729. rt_kprintf("\033[%dD", shell->line_position - start);
  730. }
  731. continue;
  732. }
  733. #endif /*defined(FINSH_USING_WORD_OPERATION) */
  734. /* handle end of line, break */
  735. if (ch == '\r' || ch == '\n')
  736. {
  737. #ifdef FINSH_USING_HISTORY
  738. shell_push_history(shell);
  739. #endif
  740. if (shell->echo_mode)
  741. rt_kprintf("\n");
  742. msh_exec(shell->line, shell->line_position);
  743. rt_kprintf(FINSH_PROMPT);
  744. rt_memset(shell->line, 0, sizeof(shell->line));
  745. shell->line_curpos = shell->line_position = 0;
  746. continue;
  747. }
  748. /* it's a large line, discard it */
  749. if (shell->line_position >= FINSH_CMD_SIZE)
  750. shell->line_position = 0;
  751. /* normal character */
  752. if (shell->line_curpos < shell->line_position)
  753. {
  754. int i;
  755. #if defined(FINSH_USING_FUNC_EXT)
  756. if (shell->overwrite_mode) /* overwrite mode */
  757. {
  758. /* directly overwrite the character */
  759. shell->line[shell->line_curpos] = ch;
  760. if (shell->echo_mode)
  761. rt_kprintf("%c", ch);
  762. shell->line_curpos++;
  763. }
  764. else /* insert mode */
  765. #endif /*defined(FINSH_USING_FUNC_EXT)*/
  766. {
  767. shell->line_position++;
  768. /* move existing characters to the right */
  769. rt_memmove(&shell->line[shell->line_curpos + 1],
  770. &shell->line[shell->line_curpos],
  771. shell->line_position - shell->line_curpos);
  772. shell->line[shell->line_curpos] = ch;
  773. if (shell->echo_mode)
  774. {
  775. rt_kprintf("%s", &shell->line[shell->line_curpos]);
  776. /* move cursor back to correct position */
  777. for (i = shell->line_curpos + 1; i < shell->line_position; i++)
  778. rt_kprintf("\b");
  779. }
  780. shell->line_curpos++;
  781. }
  782. }
  783. else
  784. {
  785. /* append character at end of line */
  786. shell->line[shell->line_position] = ch;
  787. if (shell->echo_mode)
  788. rt_kprintf("%c", ch);
  789. shell->line_position++;
  790. shell->line_curpos++;
  791. }
  792. ch = 0;
  793. if (shell->line_position >= FINSH_CMD_SIZE)
  794. {
  795. /* clear command line */
  796. shell->line_position = 0;
  797. shell->line_curpos = 0;
  798. }
  799. } /* end of device read */
  800. }
  801. static void finsh_system_function_init(const void *begin, const void *end)
  802. {
  803. _syscall_table_begin = (struct finsh_syscall *) begin;
  804. _syscall_table_end = (struct finsh_syscall *) end;
  805. }
  806. #if defined(__ICCARM__) || defined(__ICCRX__) /* for IAR compiler */
  807. #ifdef FINSH_USING_SYMTAB
  808. #pragma section="FSymTab"
  809. #endif
  810. #elif defined(__ADSPBLACKFIN__) /* for VisaulDSP++ Compiler*/
  811. #ifdef FINSH_USING_SYMTAB
  812. extern "asm" int __fsymtab_start;
  813. extern "asm" int __fsymtab_end;
  814. #endif
  815. #elif defined(_MSC_VER)
  816. #pragma section("FSymTab$a", read)
  817. const char __fsym_begin_name[] = "__start";
  818. const char __fsym_begin_desc[] = "begin of finsh";
  819. __declspec(allocate("FSymTab$a")) const struct finsh_syscall __fsym_begin =
  820. {
  821. __fsym_begin_name,
  822. __fsym_begin_desc,
  823. NULL
  824. };
  825. #pragma section("FSymTab$z", read)
  826. const char __fsym_end_name[] = "__end";
  827. const char __fsym_end_desc[] = "end of finsh";
  828. __declspec(allocate("FSymTab$z")) const struct finsh_syscall __fsym_end =
  829. {
  830. __fsym_end_name,
  831. __fsym_end_desc,
  832. NULL
  833. };
  834. #endif
  835. /*
  836. * @ingroup group_finsh
  837. *
  838. * This function will initialize finsh shell
  839. */
  840. int finsh_system_init(void)
  841. {
  842. rt_err_t result = RT_EOK;
  843. rt_thread_t tid;
  844. #ifdef FINSH_USING_SYMTAB
  845. #ifdef __ARMCC_VERSION /* ARM C Compiler */
  846. extern const int FSymTab$$Base;
  847. extern const int FSymTab$$Limit;
  848. finsh_system_function_init(&FSymTab$$Base, &FSymTab$$Limit);
  849. #elif defined (__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */
  850. finsh_system_function_init(__section_begin("FSymTab"),
  851. __section_end("FSymTab"));
  852. #elif defined (__GNUC__) || defined(__TI_COMPILER_VERSION__) || defined(__TASKING__)
  853. /* GNU GCC Compiler and TI CCS */
  854. extern const int __fsymtab_start;
  855. extern const int __fsymtab_end;
  856. finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
  857. #elif defined(__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */
  858. finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
  859. #elif defined(_MSC_VER)
  860. unsigned int *ptr_begin, *ptr_end;
  861. if (shell)
  862. {
  863. rt_kprintf("finsh shell already init.\n");
  864. return RT_EOK;
  865. }
  866. ptr_begin = (unsigned int *)&__fsym_begin;
  867. ptr_begin += (sizeof(struct finsh_syscall) / sizeof(unsigned int));
  868. while (*ptr_begin == 0) ptr_begin ++;
  869. ptr_end = (unsigned int *) &__fsym_end;
  870. ptr_end --;
  871. while (*ptr_end == 0) ptr_end --;
  872. finsh_system_function_init(ptr_begin, ptr_end);
  873. #endif
  874. #endif
  875. #ifdef RT_USING_HEAP
  876. /* create or set shell structure */
  877. shell = (struct finsh_shell *)rt_calloc(1, sizeof(struct finsh_shell));
  878. if (shell == RT_NULL)
  879. {
  880. rt_kprintf("no memory for shell\n");
  881. return -1;
  882. }
  883. tid = rt_thread_create(FINSH_THREAD_NAME,
  884. finsh_thread_entry, RT_NULL,
  885. FINSH_THREAD_STACK_SIZE, FINSH_THREAD_PRIORITY, 10);
  886. #else
  887. shell = &_shell;
  888. tid = &finsh_thread;
  889. result = rt_thread_init(&finsh_thread,
  890. FINSH_THREAD_NAME,
  891. finsh_thread_entry, RT_NULL,
  892. &finsh_thread_stack[0], sizeof(finsh_thread_stack),
  893. FINSH_THREAD_PRIORITY, 10);
  894. #endif /* RT_USING_HEAP */
  895. rt_sem_init(&(shell->rx_sem), "shrx", 0, 0);
  896. finsh_set_prompt_mode(1);
  897. if (tid != NULL && result == RT_EOK)
  898. rt_thread_startup(tid);
  899. return 0;
  900. }
  901. INIT_APP_EXPORT(finsh_system_init);
  902. #endif /* RT_USING_FINSH */