shell.c 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064
  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(rt_strlen(prompt) + 1);
  70. if (finsh_prompt_custom)
  71. {
  72. rt_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. rt_strncpy(finsh_prompt, finsh_prompt_custom, sizeof(finsh_prompt) - 1);
  91. }
  92. else
  93. {
  94. rt_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 (rt_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 || rt_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. static void finsh_shell_reset_line(struct finsh_shell *shell)
  404. {
  405. rt_memset(shell->line, 0, sizeof(shell->line));
  406. shell->line_position = 0;
  407. shell->line_curpos = 0;
  408. }
  409. static rt_bool_t finsh_shell_check_line(struct finsh_shell *shell)
  410. {
  411. if ((shell->line_position > FINSH_CMD_SIZE) ||
  412. (shell->line_curpos > shell->line_position))
  413. {
  414. finsh_shell_reset_line(shell);
  415. return RT_FALSE;
  416. }
  417. shell->line[FINSH_CMD_SIZE] = '\0';
  418. return RT_TRUE;
  419. }
  420. static void finsh_shell_update_line_length(struct finsh_shell *shell)
  421. {
  422. rt_size_t length;
  423. shell->line[FINSH_CMD_SIZE] = '\0';
  424. length = rt_strnlen(shell->line, FINSH_CMD_SIZE);
  425. shell->line[length] = '\0';
  426. shell->line_curpos = shell->line_position = (rt_uint16_t)length;
  427. }
  428. #if defined(FINSH_USING_WORD_OPERATION)
  429. static int find_prev_word_start(const char *line, int curpos)
  430. {
  431. if (curpos <= 0) return 0;
  432. /* Skip whitespace */
  433. while (--curpos > 0 && (line[curpos] == ' ' || line[curpos] == '\t'));
  434. /* Find word start */
  435. while (curpos > 0 && !(line[curpos] == ' ' || line[curpos] == '\t'))
  436. curpos--;
  437. return (curpos <= 0) ? 0 : curpos + 1;
  438. }
  439. static int find_next_word_end(const char *line, int curpos, int max)
  440. {
  441. if (curpos >= max) return max;
  442. /* Skip to next word */
  443. while (curpos < max && (line[curpos] == ' ' || line[curpos] == '\t'))
  444. curpos++;
  445. /* Find word end */
  446. while (curpos < max && !(line[curpos] == ' ' || line[curpos] == '\t'))
  447. curpos++;
  448. return curpos;
  449. }
  450. #endif /* defined(FINSH_USING_WORD_OPERATION) */
  451. #ifdef RT_USING_HOOK
  452. static void (*_finsh_thread_entry_hook)(void);
  453. /**
  454. * @ingroup group_finsh
  455. *
  456. * @brief This function set a hook function at the entry of finsh thread
  457. *
  458. * @param hook the function point to be called
  459. */
  460. void finsh_thread_entry_sethook(void (*hook)(void))
  461. {
  462. _finsh_thread_entry_hook = hook;
  463. }
  464. #endif /* RT_USING_HOOK */
  465. static void finsh_thread_entry(void *parameter)
  466. {
  467. int ch;
  468. RT_OBJECT_HOOK_CALL(_finsh_thread_entry_hook, ());
  469. /* normal is echo mode */
  470. #ifndef FINSH_ECHO_DISABLE_DEFAULT
  471. shell->echo_mode = 1;
  472. #else
  473. shell->echo_mode = 0;
  474. #endif
  475. #if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE)
  476. /* set console device as shell device */
  477. if (shell->device == RT_NULL)
  478. {
  479. rt_device_t console = rt_console_get_device();
  480. if (console)
  481. {
  482. finsh_set_device(console->parent.name);
  483. }
  484. }
  485. #endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */
  486. #ifdef FINSH_USING_AUTH
  487. /* set the default password when the password isn't setting */
  488. if (rt_strlen(finsh_get_password()) == 0)
  489. {
  490. if (finsh_set_password(FINSH_DEFAULT_PASSWORD) != RT_EOK)
  491. {
  492. rt_kprintf("Finsh password set failed.\n");
  493. }
  494. }
  495. /* waiting authenticate success */
  496. finsh_wait_auth();
  497. #endif
  498. rt_kprintf("%s", FINSH_PROMPT);
  499. while (1)
  500. {
  501. ch = (int)finsh_getchar();
  502. if (ch < 0)
  503. {
  504. continue;
  505. }
  506. /*
  507. * handle control key
  508. * up key : 0x1b 0x5b 0x41
  509. * down key: 0x1b 0x5b 0x42
  510. * right key:0x1b 0x5b 0x43
  511. * left key: 0x1b 0x5b 0x44
  512. * home : 0x1b 0x5b 0x31 0x7E
  513. * insert : 0x1b 0x5b 0x32 0x7E
  514. * del : 0x1b 0x5b 0x33 0x7E
  515. * end : 0x1b 0x5b 0x34 0x7E
  516. */
  517. if (ch == 0x1b)
  518. {
  519. shell->stat = WAIT_SPEC_KEY;
  520. continue;
  521. }
  522. else if (shell->stat == WAIT_SPEC_KEY)
  523. {
  524. if (ch == 0x5b || ch == 0x41 || ch == 0x42 || ch == 0x43 || ch == 0x44)
  525. {
  526. shell->stat = WAIT_FUNC_KEY;
  527. continue;
  528. }
  529. shell->stat = WAIT_NORMAL;
  530. }
  531. else if (shell->stat == WAIT_FUNC_KEY)
  532. {
  533. shell->stat = WAIT_NORMAL;
  534. if (ch == 0x41) /* up key */
  535. {
  536. #ifdef FINSH_USING_HISTORY
  537. /* prev history */
  538. if (shell->current_history > 0)
  539. shell->current_history --;
  540. else
  541. {
  542. shell->current_history = 0;
  543. continue;
  544. }
  545. /* copy the history command */
  546. rt_memcpy(shell->line, &shell->cmd_history[shell->current_history][0],
  547. FINSH_CMD_SIZE);
  548. shell->line_curpos = shell->line_position = (rt_uint16_t)rt_strlen(shell->line);
  549. shell_handle_history(shell);
  550. #endif
  551. continue;
  552. }
  553. else if (ch == 0x42) /* down key */
  554. {
  555. #ifdef FINSH_USING_HISTORY
  556. /* next history */
  557. if (shell->current_history < shell->history_count - 1)
  558. shell->current_history ++;
  559. else
  560. {
  561. /* set to the end of history */
  562. if (shell->history_count != 0)
  563. shell->current_history = shell->history_count - 1;
  564. else
  565. continue;
  566. }
  567. rt_memcpy(shell->line, &shell->cmd_history[shell->current_history][0],
  568. FINSH_CMD_SIZE);
  569. shell->line_curpos = shell->line_position = (rt_uint16_t)rt_strlen(shell->line);
  570. shell_handle_history(shell);
  571. #endif
  572. continue;
  573. }
  574. else if (ch == 0x44) /* left key */
  575. {
  576. if (shell->line_curpos)
  577. {
  578. rt_kprintf("\b");
  579. shell->line_curpos --;
  580. }
  581. continue;
  582. }
  583. else if (ch == 0x43) /* right key */
  584. {
  585. if (shell->line_curpos < shell->line_position)
  586. {
  587. rt_kprintf("%c", shell->line[shell->line_curpos]);
  588. shell->line_curpos ++;
  589. }
  590. continue;
  591. }
  592. #if defined(FINSH_USING_WORD_OPERATION)
  593. /* Add Ctrl+Left/Right handling */
  594. else if (ch == '1')
  595. {
  596. /* Read modifier sequence [1;5D/C] */
  597. int next_ch = finsh_getchar();
  598. if (next_ch == ';')
  599. {
  600. next_ch = finsh_getchar();
  601. if (next_ch == '5')
  602. {
  603. next_ch = finsh_getchar();
  604. if (next_ch == 'D') /* Ctrl+Left */
  605. {
  606. int new_pos = find_prev_word_start(shell->line, shell->line_curpos);
  607. if (new_pos != shell->line_curpos)
  608. {
  609. rt_kprintf("\033[%dD", shell->line_curpos - new_pos);
  610. shell->line_curpos = new_pos;
  611. }
  612. continue;
  613. }
  614. else if (next_ch == 'C') /* Ctrl+Right */
  615. {
  616. int new_pos = find_next_word_end(shell->line, shell->line_curpos, shell->line_position);
  617. if (new_pos != shell->line_curpos)
  618. {
  619. rt_kprintf("\033[%dC", new_pos - shell->line_curpos);
  620. shell->line_curpos = new_pos;
  621. }
  622. continue;
  623. }
  624. }
  625. }
  626. }
  627. #endif /*defined(FINSH_USING_WORD_OPERATION) */
  628. #if defined(FINSH_USING_FUNC_EXT)
  629. else if (ch >= 0x31 && ch <= 0x34) /* home(0x31), insert(0x32), del(0x33), end(0x34) */
  630. {
  631. if (shell->line_position >= FINSH_CMD_SIZE)
  632. {
  633. finsh_shell_reset_line(shell);
  634. continue;
  635. }
  636. shell->stat = WAIT_EXT_KEY;
  637. shell->line[shell->line_position + 1] = ch; /* store the key code */
  638. continue;
  639. }
  640. }
  641. else if (shell->stat == WAIT_EXT_KEY)
  642. {
  643. shell->stat = WAIT_NORMAL;
  644. if (ch == 0x7E) /* extended key terminator */
  645. {
  646. rt_uint8_t key_code = shell->line[shell->line_position + 1];
  647. if (key_code == 0x31) /* home key */
  648. {
  649. /* move cursor to beginning of line */
  650. while (shell->line_curpos > 0)
  651. {
  652. rt_kprintf("\b");
  653. shell->line_curpos--;
  654. }
  655. }
  656. else if (key_code == 0x32) /* insert key */
  657. {
  658. /* toggle insert mode */
  659. shell->overwrite_mode = !shell->overwrite_mode;
  660. }
  661. else if (key_code == 0x33) /* del key */
  662. {
  663. /* delete character at current cursor position */
  664. if (finsh_shell_check_line(shell) &&
  665. (shell->line_curpos < shell->line_position))
  666. {
  667. int i;
  668. shell->line_position--;
  669. rt_memmove(&shell->line[shell->line_curpos],
  670. &shell->line[shell->line_curpos + 1],
  671. shell->line_position - shell->line_curpos);
  672. shell->line[shell->line_position] = 0;
  673. rt_kprintf("%s ", &shell->line[shell->line_curpos]);
  674. /* move cursor back to original position */
  675. for (i = shell->line_curpos; i <= shell->line_position; i++)
  676. rt_kprintf("\b");
  677. }
  678. }
  679. else if (key_code == 0x34) /* end key */
  680. {
  681. /* move cursor to end of line */
  682. while (shell->line_curpos < shell->line_position)
  683. {
  684. rt_kprintf("%c", shell->line[shell->line_curpos]);
  685. shell->line_curpos++;
  686. }
  687. }
  688. continue;
  689. }
  690. #endif /*defined(FINSH_USING_FUNC_EXT) */
  691. }
  692. if (!finsh_shell_check_line(shell))
  693. continue;
  694. /* received null or error */
  695. if (ch == '\0' || ch == 0xFF) continue;
  696. /* handle tab key */
  697. else if (ch == '\t')
  698. {
  699. int i;
  700. /* move the cursor to the beginning of line */
  701. for (i = 0; i < shell->line_curpos; i++)
  702. rt_kprintf("\b");
  703. /* auto complete */
  704. shell_auto_complete(&shell->line[0]);
  705. /* re-calculate position */
  706. finsh_shell_update_line_length(shell);
  707. continue;
  708. }
  709. /* handle backspace key */
  710. else if (ch == 0x7f || ch == 0x08)
  711. {
  712. /* note that shell->line_curpos >= 0 */
  713. if (shell->line_curpos == 0)
  714. continue;
  715. shell->line_position--;
  716. shell->line_curpos--;
  717. if (shell->line_position > shell->line_curpos)
  718. {
  719. int i;
  720. rt_memmove(&shell->line[shell->line_curpos],
  721. &shell->line[shell->line_curpos + 1],
  722. shell->line_position - shell->line_curpos);
  723. shell->line[shell->line_position] = 0;
  724. rt_kprintf("\b%s \b", &shell->line[shell->line_curpos]);
  725. /* move the cursor to the origin position */
  726. for (i = shell->line_curpos; i <= shell->line_position; i++)
  727. rt_kprintf("\b");
  728. }
  729. else
  730. {
  731. rt_kprintf("\b \b");
  732. shell->line[shell->line_position] = 0;
  733. }
  734. continue;
  735. }
  736. #if defined(FINSH_USING_WORD_OPERATION)
  737. /* Add Ctrl+Backspace handling */
  738. else if (ch == 0x17) /* Ctrl+Backspace (typically ^W) */
  739. {
  740. if (shell->line_curpos == 0) continue;
  741. int start = find_prev_word_start(shell->line, shell->line_curpos);
  742. int del_count = shell->line_curpos - start;
  743. int new_len = shell->line_position - del_count;
  744. /* Delete characters and properly add RT_NULL termination */
  745. rt_memmove(&shell->line[start],
  746. &shell->line[start + del_count],
  747. new_len - start + 1);
  748. /* Clear residual data */
  749. rt_memset(&shell->line[new_len], 0, shell->line_position - new_len);
  750. /* Update positions */
  751. shell->line_position = new_len;
  752. shell->line_curpos = start;
  753. /* Redraw the affected line section */
  754. rt_kprintf("\033[%dD", del_count);
  755. /* Rewrite the remaining content */
  756. rt_kprintf("%.*s", shell->line_position - start, &shell->line[start]);
  757. /* Clear trailing artifacts */
  758. rt_kprintf("\033[K");
  759. if (shell->line_position > start)
  760. {
  761. /* Reset cursor */
  762. rt_kprintf("\033[%dD", shell->line_position - start);
  763. }
  764. continue;
  765. }
  766. #endif /*defined(FINSH_USING_WORD_OPERATION) */
  767. /* handle end of line, break */
  768. if (ch == '\r' || ch == '\n')
  769. {
  770. #ifdef FINSH_USING_HISTORY
  771. shell_push_history(shell);
  772. #endif
  773. if (shell->echo_mode)
  774. rt_kprintf("\n");
  775. msh_exec(shell->line, shell->line_position);
  776. rt_kprintf("%s", FINSH_PROMPT);
  777. finsh_shell_reset_line(shell);
  778. continue;
  779. }
  780. /* it's a large line, discard it */
  781. if (shell->line_position >= FINSH_CMD_SIZE)
  782. {
  783. finsh_shell_reset_line(shell);
  784. continue;
  785. }
  786. /* normal character */
  787. if (shell->line_curpos < shell->line_position)
  788. {
  789. int i;
  790. #if defined(FINSH_USING_FUNC_EXT)
  791. if (shell->overwrite_mode) /* overwrite mode */
  792. {
  793. /* directly overwrite the character */
  794. shell->line[shell->line_curpos] = ch;
  795. if (shell->echo_mode)
  796. rt_kprintf("%c", ch);
  797. shell->line_curpos++;
  798. }
  799. else /* insert mode */
  800. #endif /*defined(FINSH_USING_FUNC_EXT)*/
  801. {
  802. shell->line_position++;
  803. /* move existing characters to the right */
  804. rt_memmove(&shell->line[shell->line_curpos + 1],
  805. &shell->line[shell->line_curpos],
  806. shell->line_position - shell->line_curpos);
  807. shell->line[shell->line_curpos] = ch;
  808. if (shell->echo_mode)
  809. {
  810. rt_kprintf("%s", &shell->line[shell->line_curpos]);
  811. /* move cursor back to correct position */
  812. for (i = shell->line_curpos + 1; i < shell->line_position; i++)
  813. rt_kprintf("\b");
  814. }
  815. shell->line_curpos++;
  816. }
  817. }
  818. else
  819. {
  820. /* append character at end of line */
  821. shell->line[shell->line_position] = ch;
  822. if (shell->echo_mode)
  823. rt_kprintf("%c", ch);
  824. shell->line_position++;
  825. shell->line_curpos++;
  826. }
  827. ch = 0;
  828. if (shell->line_position >= FINSH_CMD_SIZE)
  829. {
  830. /* clear command line */
  831. finsh_shell_reset_line(shell);
  832. }
  833. } /* end of device read */
  834. }
  835. static void finsh_system_function_init(const void *begin, const void *end)
  836. {
  837. _syscall_table_begin = (struct finsh_syscall *) begin;
  838. _syscall_table_end = (struct finsh_syscall *) end;
  839. }
  840. #if defined(__ICCARM__) || defined(__ICCRX__) /* for IAR compiler */
  841. #ifdef FINSH_USING_SYMTAB
  842. #pragma section="FSymTab"
  843. #endif
  844. #elif defined(__ADSPBLACKFIN__) /* for VisaulDSP++ Compiler*/
  845. #ifdef FINSH_USING_SYMTAB
  846. extern "asm" int __fsymtab_start;
  847. extern "asm" int __fsymtab_end;
  848. #endif
  849. #elif defined(_MSC_VER)
  850. #pragma section("FSymTab$a", read)
  851. const char __fsym_begin_name[] = "__start";
  852. const char __fsym_begin_desc[] = "begin of finsh";
  853. __declspec(allocate("FSymTab$a")) const struct finsh_syscall __fsym_begin =
  854. {
  855. __fsym_begin_name,
  856. __fsym_begin_desc,
  857. NULL
  858. };
  859. #pragma section("FSymTab$z", read)
  860. const char __fsym_end_name[] = "__end";
  861. const char __fsym_end_desc[] = "end of finsh";
  862. __declspec(allocate("FSymTab$z")) const struct finsh_syscall __fsym_end =
  863. {
  864. __fsym_end_name,
  865. __fsym_end_desc,
  866. NULL
  867. };
  868. #endif
  869. /*
  870. * @ingroup group_finsh
  871. *
  872. * This function will initialize finsh shell
  873. */
  874. int finsh_system_init(void)
  875. {
  876. rt_err_t result = RT_EOK;
  877. rt_thread_t tid;
  878. #ifdef FINSH_USING_SYMTAB
  879. #ifdef __ARMCC_VERSION /* ARM C Compiler */
  880. extern const int FSymTab$$Base;
  881. extern const int FSymTab$$Limit;
  882. finsh_system_function_init(&FSymTab$$Base, &FSymTab$$Limit);
  883. #elif defined (__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */
  884. finsh_system_function_init(__section_begin("FSymTab"),
  885. __section_end("FSymTab"));
  886. #elif defined (__GNUC__) || defined(__TI_COMPILER_VERSION__) || defined(__TASKING__)
  887. /* GNU GCC Compiler and TI CCS */
  888. extern const int __fsymtab_start;
  889. extern const int __fsymtab_end;
  890. finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
  891. #elif defined(__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */
  892. finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
  893. #elif defined(_MSC_VER)
  894. unsigned int *ptr_begin, *ptr_end;
  895. if (shell)
  896. {
  897. rt_kprintf("finsh shell already init.\n");
  898. return RT_EOK;
  899. }
  900. ptr_begin = (unsigned int *)&__fsym_begin;
  901. ptr_begin += (sizeof(struct finsh_syscall) / sizeof(unsigned int));
  902. while (*ptr_begin == 0) ptr_begin ++;
  903. ptr_end = (unsigned int *) &__fsym_end;
  904. ptr_end --;
  905. while (*ptr_end == 0) ptr_end --;
  906. finsh_system_function_init(ptr_begin, ptr_end);
  907. #endif
  908. #endif
  909. #ifdef RT_USING_HEAP
  910. /* create or set shell structure */
  911. shell = (struct finsh_shell *)rt_calloc(1, sizeof(struct finsh_shell));
  912. if (shell == RT_NULL)
  913. {
  914. rt_kprintf("no memory for shell\n");
  915. return -1;
  916. }
  917. tid = rt_thread_create(FINSH_THREAD_NAME,
  918. finsh_thread_entry, RT_NULL,
  919. FINSH_THREAD_STACK_SIZE, FINSH_THREAD_PRIORITY, 10);
  920. #else
  921. shell = &_shell;
  922. tid = &finsh_thread;
  923. result = rt_thread_init(&finsh_thread,
  924. FINSH_THREAD_NAME,
  925. finsh_thread_entry, RT_NULL,
  926. &finsh_thread_stack[0], sizeof(finsh_thread_stack),
  927. FINSH_THREAD_PRIORITY, 10);
  928. #endif /* RT_USING_HEAP */
  929. rt_sem_init(&(shell->rx_sem), "shrx", 0, 0);
  930. finsh_set_prompt_mode(1);
  931. if (tid != NULL && result == RT_EOK)
  932. rt_thread_startup(tid);
  933. return 0;
  934. }
  935. INIT_APP_EXPORT(finsh_system_init);
  936. #endif /* RT_USING_FINSH */