fsl_shell.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. /*
  2. * The Clear BSD License
  3. * Copyright (c) 2015, Freescale Semiconductor, Inc.
  4. * Copyright 2016-2017 NXP
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without modification,
  8. * are permitted (subject to the limitations in the disclaimer below) provided
  9. * that the following conditions are met:
  10. *
  11. * o Redistributions of source code must retain the above copyright notice, this list
  12. * of conditions and the following disclaimer.
  13. *
  14. * o Redistributions in binary form must reproduce the above copyright notice, this
  15. * list of conditions and the following disclaimer in the documentation and/or
  16. * other materials provided with the distribution.
  17. *
  18. * o Neither the name of the copyright holder nor the names of its
  19. * contributors may be used to endorse or promote products derived from this
  20. * software without specific prior written permission.
  21. *
  22. * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE.
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  24. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  25. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  26. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
  27. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  28. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  29. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  30. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  31. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  32. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. *
  34. * POSIX getopt for Windows
  35. * Code given out at the 1985 UNIFORUM conference in Dallas.
  36. *
  37. * From std-unix@ut-sally.UUCP (Moderator, John Quarterman) Sun Nov 3 14:34:15 1985
  38. * Relay-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site gatech.CSNET
  39. * Posting-Version: version B 2.10.2 9/18/84; site ut-sally.UUCP
  40. * Path: gatech!akgua!mhuxv!mhuxt!mhuxr!ulysses!allegra!mit-eddie!genrad!panda!talcott!harvard!seismo!ut-sally!std-unix
  41. * From: std-unix@ut-sally.UUCP (Moderator, John Quarterman)
  42. * Newsgroups: mod.std.unix
  43. * Subject: public domain AT&T getopt source
  44. * Message-ID: <3352@ut-sally.UUCP>
  45. * Date: 3 Nov 85 19:34:15 GMT
  46. * Date-Received: 4 Nov 85 12:25:09 GMT
  47. * Organization: IEEE/P1003 Portable Operating System Environment Committee
  48. * Lines: 91
  49. * Approved: jsq@ut-sally.UUC
  50. * Here's something you've all been waiting for: the AT&T public domain
  51. * source for getopt(3). It is the code which was given out at the 1985
  52. * UNIFORUM conference in Dallas. I obtained it by electronic mail
  53. * directly from AT&T. The people there assure me that it is indeed
  54. * in the public domain
  55. * There is no manual page. That is because the one they gave out at
  56. * UNIFORUM was slightly different from the current System V Release 2
  57. * manual page. The difference apparently involved a note about the
  58. * famous rules 5 and 6, recommending using white space between an option
  59. * and its first argument, and not grouping options that have arguments.
  60. * Getopt itself is currently lenient about both of these things White
  61. * space is allowed, but not mandatory, and the last option in a group can
  62. * have an argument. That particular version of the man page evidently
  63. * has no official existence, and my source at AT&T did not send a copy.
  64. * The current SVR2 man page reflects the actual behavor of this getopt.
  65. * However, I am not about to post a copy of anything licensed by AT&T.
  66. */
  67. #include <assert.h>
  68. #include "fsl_shell.h"
  69. /*******************************************************************************
  70. * Definitions
  71. ******************************************************************************/
  72. #define KEY_ESC (0x1BU)
  73. #define KET_DEL (0x7FU)
  74. /*******************************************************************************
  75. * Prototypes
  76. ******************************************************************************/
  77. static int32_t HelpCommand(p_shell_context_t context, int32_t argc, char **argv); /*!< help command */
  78. static int32_t ExitCommand(p_shell_context_t context, int32_t argc, char **argv); /*!< exit command */
  79. static int32_t ParseLine(const char *cmd, uint32_t len, char *argv[SHELL_MAX_ARGS]); /*!< parse line command */
  80. static int32_t StrCompare(const char *str1, const char *str2, int32_t count); /*!< compare string command */
  81. static void ProcessCommand(p_shell_context_t context, const char *cmd); /*!< process a command */
  82. static void GetHistoryCommand(p_shell_context_t context, uint8_t hist_pos); /*!< get commands history */
  83. static void AutoComplete(p_shell_context_t context); /*!< auto complete command */
  84. static uint8_t GetChar(p_shell_context_t context); /*!< get a char from communication interface */
  85. static int32_t StrLen(const char *str); /*!< get string length */
  86. static char *StrCopy(char *dest, const char *src, int32_t count); /*!< string copy */
  87. /*******************************************************************************
  88. * Variables
  89. ******************************************************************************/
  90. static const shell_command_context_t xHelpCommand = {"help", "\r\n\"help\": Lists all the registered commands\r\n",
  91. HelpCommand, 0};
  92. static const shell_command_context_t xExitCommand = {"exit", "\r\n\"exit\": Exit program\r\n", ExitCommand, 0};
  93. static shell_command_context_list_t g_RegisteredCommands;
  94. static char g_paramBuffer[SHELL_BUFFER_SIZE];
  95. /*******************************************************************************
  96. * Code
  97. ******************************************************************************/
  98. void SHELL_Init(
  99. p_shell_context_t context, send_data_cb_t send_cb, recv_data_cb_t recv_cb, printf_data_t shell_printf, char *prompt)
  100. {
  101. assert(send_cb != NULL);
  102. assert(recv_cb != NULL);
  103. assert(prompt != NULL);
  104. assert(shell_printf != NULL);
  105. /* Memset for context */
  106. memset(context, 0, sizeof(shell_context_struct));
  107. context->send_data_func = send_cb;
  108. context->recv_data_func = recv_cb;
  109. context->printf_data_func = shell_printf;
  110. context->prompt = prompt;
  111. SHELL_RegisterCommand(&xHelpCommand);
  112. SHELL_RegisterCommand(&xExitCommand);
  113. }
  114. int32_t SHELL_Main(p_shell_context_t context)
  115. {
  116. uint8_t ch;
  117. int32_t i;
  118. if (!context)
  119. {
  120. return -1;
  121. }
  122. context->exit = false;
  123. context->printf_data_func("\r\nSHELL (build: %s)\r\n", __DATE__);
  124. context->printf_data_func("Copyright (c) 2017 NXP Semiconductor\r\n");
  125. context->printf_data_func(context->prompt);
  126. while (1)
  127. {
  128. if (context->exit)
  129. {
  130. break;
  131. }
  132. ch = GetChar(context);
  133. /* If error occured when getting a char, continue to receive a new char. */
  134. if ((uint8_t)(-1) == ch)
  135. {
  136. continue;
  137. }
  138. /* Special key */
  139. if (ch == KEY_ESC)
  140. {
  141. context->stat = kSHELL_Special;
  142. continue;
  143. }
  144. else if (context->stat == kSHELL_Special)
  145. {
  146. /* Function key */
  147. if (ch == '[')
  148. {
  149. context->stat = kSHELL_Function;
  150. continue;
  151. }
  152. context->stat = kSHELL_Normal;
  153. }
  154. else if (context->stat == kSHELL_Function)
  155. {
  156. context->stat = kSHELL_Normal;
  157. switch ((uint8_t)ch)
  158. {
  159. /* History operation here */
  160. case 'A': /* Up key */
  161. GetHistoryCommand(context, context->hist_current);
  162. if (context->hist_current < (context->hist_count - 1))
  163. {
  164. context->hist_current++;
  165. }
  166. break;
  167. case 'B': /* Down key */
  168. GetHistoryCommand(context, context->hist_current);
  169. if (context->hist_current > 0)
  170. {
  171. context->hist_current--;
  172. }
  173. break;
  174. case 'D': /* Left key */
  175. if (context->c_pos)
  176. {
  177. context->printf_data_func("\b");
  178. context->c_pos--;
  179. }
  180. break;
  181. case 'C': /* Right key */
  182. if (context->c_pos < context->l_pos)
  183. {
  184. context->printf_data_func("%c", context->line[context->c_pos]);
  185. context->c_pos++;
  186. }
  187. break;
  188. default:
  189. break;
  190. }
  191. continue;
  192. }
  193. /* Handle tab key */
  194. else if (ch == '\t')
  195. {
  196. #if SHELL_AUTO_COMPLETE
  197. /* Move the cursor to the beginning of line */
  198. for (i = 0; i < context->c_pos; i++)
  199. {
  200. context->printf_data_func("\b");
  201. }
  202. /* Do auto complete */
  203. AutoComplete(context);
  204. /* Move position to end */
  205. context->c_pos = context->l_pos = StrLen(context->line);
  206. #endif
  207. continue;
  208. }
  209. #if SHELL_SEARCH_IN_HIST
  210. /* Search command in history */
  211. else if ((ch == '`') && (context->l_pos == 0) && (context->line[0] == 0x00))
  212. {
  213. }
  214. #endif
  215. /* Handle backspace key */
  216. else if ((ch == KET_DEL) || (ch == '\b'))
  217. {
  218. /* There must be at last one char */
  219. if (context->c_pos == 0)
  220. {
  221. continue;
  222. }
  223. context->l_pos--;
  224. context->c_pos--;
  225. if (context->l_pos > context->c_pos)
  226. {
  227. memmove(&context->line[context->c_pos], &context->line[context->c_pos + 1],
  228. context->l_pos - context->c_pos);
  229. context->line[context->l_pos] = 0;
  230. context->printf_data_func("\b%s \b", &context->line[context->c_pos]);
  231. /* Reset position */
  232. for (i = context->c_pos; i <= context->l_pos; i++)
  233. {
  234. context->printf_data_func("\b");
  235. }
  236. }
  237. else /* Normal backspace operation */
  238. {
  239. context->printf_data_func("\b \b");
  240. context->line[context->l_pos] = 0;
  241. }
  242. continue;
  243. }
  244. else
  245. {
  246. }
  247. /* Input too long */
  248. if (context->l_pos >= (SHELL_BUFFER_SIZE - 1))
  249. {
  250. context->l_pos = 0;
  251. }
  252. /* Handle end of line, break */
  253. if ((ch == '\r') || (ch == '\n'))
  254. {
  255. static char endoflinechar = 0U;
  256. if ((endoflinechar != 0U) && (endoflinechar != ch))
  257. {
  258. continue;
  259. }
  260. else
  261. {
  262. endoflinechar = ch;
  263. context->printf_data_func("\r\n");
  264. /* If command line is NULL, will start a new transfer */
  265. if (0U == StrLen(context->line))
  266. {
  267. context->printf_data_func(context->prompt);
  268. continue;
  269. }
  270. ProcessCommand(context, context->line);
  271. /* Reset all params */
  272. context->c_pos = context->l_pos = 0;
  273. context->hist_current = 0;
  274. context->printf_data_func(context->prompt);
  275. memset(context->line, 0, sizeof(context->line));
  276. continue;
  277. }
  278. }
  279. /* Normal character */
  280. if (context->c_pos < context->l_pos)
  281. {
  282. memmove(&context->line[context->c_pos + 1], &context->line[context->c_pos],
  283. context->l_pos - context->c_pos);
  284. context->line[context->c_pos] = ch;
  285. context->printf_data_func("%s", &context->line[context->c_pos]);
  286. /* Move the cursor to new position */
  287. for (i = context->c_pos; i < context->l_pos; i++)
  288. {
  289. context->printf_data_func("\b");
  290. }
  291. }
  292. else
  293. {
  294. context->line[context->l_pos] = ch;
  295. context->printf_data_func("%c", ch);
  296. }
  297. ch = 0;
  298. context->l_pos++;
  299. context->c_pos++;
  300. }
  301. return 0;
  302. }
  303. static int32_t HelpCommand(p_shell_context_t context, int32_t argc, char **argv)
  304. {
  305. uint8_t i = 0;
  306. for (i = 0; i < g_RegisteredCommands.numberOfCommandInList; i++)
  307. {
  308. context->printf_data_func(g_RegisteredCommands.CommandList[i]->pcHelpString);
  309. }
  310. return 0;
  311. }
  312. static int32_t ExitCommand(p_shell_context_t context, int32_t argc, char **argv)
  313. {
  314. /* Skip warning */
  315. context->printf_data_func("\r\nSHELL exited\r\n");
  316. context->exit = true;
  317. return 0;
  318. }
  319. static void ProcessCommand(p_shell_context_t context, const char *cmd)
  320. {
  321. static const shell_command_context_t *tmpCommand = NULL;
  322. static const char *tmpCommandString;
  323. int32_t argc;
  324. char *argv[SHELL_BUFFER_SIZE];
  325. uint8_t flag = 1;
  326. uint8_t tmpCommandLen;
  327. uint8_t tmpLen;
  328. uint8_t i = 0;
  329. tmpLen = StrLen(cmd);
  330. argc = ParseLine(cmd, tmpLen, argv);
  331. if ((tmpCommand == NULL) && (argc > 0))
  332. {
  333. for (i = 0; i < g_RegisteredCommands.numberOfCommandInList; i++)
  334. {
  335. tmpCommand = g_RegisteredCommands.CommandList[i];
  336. tmpCommandString = tmpCommand->pcCommand;
  337. tmpCommandLen = StrLen(tmpCommandString);
  338. /* Compare with space or end of string */
  339. if ((cmd[tmpCommandLen] == ' ') || (cmd[tmpCommandLen] == 0x00))
  340. {
  341. if (StrCompare(tmpCommandString, argv[0], tmpCommandLen) == 0)
  342. {
  343. /* support commands with optional number of parameters */
  344. if (tmpCommand->cExpectedNumberOfParameters == SHELL_OPTIONAL_PARAMS)
  345. {
  346. flag = 0;
  347. }
  348. else if ((tmpCommand->cExpectedNumberOfParameters == 0) && (argc == 1))
  349. {
  350. flag = 0;
  351. }
  352. else if (tmpCommand->cExpectedNumberOfParameters > 0)
  353. {
  354. if ((argc - 1) == tmpCommand->cExpectedNumberOfParameters)
  355. {
  356. flag = 0;
  357. }
  358. }
  359. else
  360. {
  361. flag = 1;
  362. }
  363. break;
  364. }
  365. }
  366. }
  367. }
  368. if ((tmpCommand != NULL) && (flag == 1U))
  369. {
  370. context->printf_data_func(
  371. "\r\nIncorrect command parameter(s). Enter \"help\" to view a list of available commands.\r\n\r\n");
  372. tmpCommand = NULL;
  373. }
  374. else if (tmpCommand != NULL)
  375. {
  376. tmpLen = StrLen(cmd);
  377. /* Compare with last command. Push back to history buffer if different */
  378. if (tmpLen != StrCompare(cmd, context->hist_buf[0], StrLen(cmd)))
  379. {
  380. for (i = SHELL_HIST_MAX - 1; i > 0; i--)
  381. {
  382. memset(context->hist_buf[i], '\0', SHELL_BUFFER_SIZE);
  383. tmpLen = StrLen(context->hist_buf[i - 1]);
  384. StrCopy(context->hist_buf[i], context->hist_buf[i - 1], tmpLen);
  385. }
  386. memset(context->hist_buf[0], '\0', SHELL_BUFFER_SIZE);
  387. tmpLen = StrLen(cmd);
  388. StrCopy(context->hist_buf[0], cmd, tmpLen);
  389. if (context->hist_count < SHELL_HIST_MAX)
  390. {
  391. context->hist_count++;
  392. }
  393. }
  394. tmpCommand->pFuncCallBack(context, argc, argv);
  395. tmpCommand = NULL;
  396. }
  397. else
  398. {
  399. context->printf_data_func(
  400. "\r\nCommand not recognised. Enter 'help' to view a list of available commands.\r\n\r\n");
  401. tmpCommand = NULL;
  402. }
  403. }
  404. static void GetHistoryCommand(p_shell_context_t context, uint8_t hist_pos)
  405. {
  406. uint8_t i;
  407. uint32_t tmp;
  408. if (context->hist_buf[0][0] == '\0')
  409. {
  410. context->hist_current = 0;
  411. return;
  412. }
  413. if (hist_pos >= SHELL_HIST_MAX)
  414. {
  415. hist_pos = SHELL_HIST_MAX - 1;
  416. }
  417. tmp = StrLen(context->line);
  418. /* Clear current if have */
  419. if (tmp > 0)
  420. {
  421. memset(context->line, '\0', tmp);
  422. for (i = 0; i < tmp; i++)
  423. {
  424. context->printf_data_func("\b \b");
  425. }
  426. }
  427. context->l_pos = StrLen(context->hist_buf[hist_pos]);
  428. context->c_pos = context->l_pos;
  429. StrCopy(context->line, context->hist_buf[hist_pos], context->l_pos);
  430. context->printf_data_func(context->hist_buf[hist_pos]);
  431. }
  432. static void AutoComplete(p_shell_context_t context)
  433. {
  434. int32_t len;
  435. int32_t minLen;
  436. uint8_t i = 0;
  437. const shell_command_context_t *tmpCommand = NULL;
  438. const char *namePtr;
  439. const char *cmdName;
  440. minLen = 0;
  441. namePtr = NULL;
  442. if (!StrLen(context->line))
  443. {
  444. return;
  445. }
  446. context->printf_data_func("\r\n");
  447. /* Empty tab, list all commands */
  448. if (context->line[0] == '\0')
  449. {
  450. HelpCommand(context, 0, NULL);
  451. return;
  452. }
  453. /* Do auto complete */
  454. for (i = 0; i < g_RegisteredCommands.numberOfCommandInList; i++)
  455. {
  456. tmpCommand = g_RegisteredCommands.CommandList[i];
  457. cmdName = tmpCommand->pcCommand;
  458. if (StrCompare(context->line, cmdName, StrLen(context->line)) == 0)
  459. {
  460. if (minLen == 0)
  461. {
  462. namePtr = cmdName;
  463. minLen = StrLen(namePtr);
  464. /* Show possible matches */
  465. context->printf_data_func("%s\r\n", cmdName);
  466. continue;
  467. }
  468. len = StrCompare(namePtr, cmdName, StrLen(namePtr));
  469. if (len < 0)
  470. {
  471. len = len * (-1);
  472. }
  473. if (len < minLen)
  474. {
  475. minLen = len;
  476. }
  477. }
  478. }
  479. /* Auto complete string */
  480. if (namePtr)
  481. {
  482. StrCopy(context->line, namePtr, minLen);
  483. }
  484. context->printf_data_func("%s%s", context->prompt, context->line);
  485. return;
  486. }
  487. static char *StrCopy(char *dest, const char *src, int32_t count)
  488. {
  489. char *ret = dest;
  490. int32_t i = 0;
  491. for (i = 0; i < count; i++)
  492. {
  493. dest[i] = src[i];
  494. }
  495. return ret;
  496. }
  497. static int32_t StrLen(const char *str)
  498. {
  499. int32_t i = 0;
  500. while (*str)
  501. {
  502. str++;
  503. i++;
  504. }
  505. return i;
  506. }
  507. static int32_t StrCompare(const char *str1, const char *str2, int32_t count)
  508. {
  509. while (count--)
  510. {
  511. if (*str1++ != *str2++)
  512. {
  513. return *(unsigned char *)(str1 - 1) - *(unsigned char *)(str2 - 1);
  514. }
  515. }
  516. return 0;
  517. }
  518. static int32_t ParseLine(const char *cmd, uint32_t len, char *argv[SHELL_MAX_ARGS])
  519. {
  520. uint32_t argc;
  521. char *p;
  522. uint32_t position;
  523. /* Init params */
  524. memset(g_paramBuffer, '\0', len + 1);
  525. StrCopy(g_paramBuffer, cmd, len);
  526. p = g_paramBuffer;
  527. position = 0;
  528. argc = 0;
  529. while (position < len)
  530. {
  531. /* Skip all blanks */
  532. while (((char)(*p) == ' ') && (position < len))
  533. {
  534. *p = '\0';
  535. p++;
  536. position++;
  537. }
  538. /* Process begin of a string */
  539. if (*p == '"')
  540. {
  541. p++;
  542. position++;
  543. argv[argc] = p;
  544. argc++;
  545. /* Skip this string */
  546. while ((*p != '"') && (position < len))
  547. {
  548. p++;
  549. position++;
  550. }
  551. /* Skip '"' */
  552. *p = '\0';
  553. p++;
  554. position++;
  555. }
  556. else /* Normal char */
  557. {
  558. argv[argc] = p;
  559. argc++;
  560. while (((char)*p != ' ') && ((char)*p != '\t') && (position < len))
  561. {
  562. p++;
  563. position++;
  564. }
  565. }
  566. }
  567. return argc;
  568. }
  569. int32_t SHELL_RegisterCommand(const shell_command_context_t *command_context)
  570. {
  571. int32_t result = 0;
  572. /* If have room in command list */
  573. if (g_RegisteredCommands.numberOfCommandInList < SHELL_MAX_CMD)
  574. {
  575. g_RegisteredCommands.CommandList[g_RegisteredCommands.numberOfCommandInList++] = command_context;
  576. }
  577. else
  578. {
  579. result = -1;
  580. }
  581. return result;
  582. }
  583. static uint8_t GetChar(p_shell_context_t context)
  584. {
  585. uint8_t ch;
  586. #if SHELL_USE_FILE_STREAM
  587. ch = fgetc(context->STDIN);
  588. #else
  589. context->recv_data_func(&ch, 1U);
  590. #endif
  591. return ch;
  592. }