chry_shell.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053
  1. /*
  2. * Copyright (c) 2022, Egahp
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdio.h>
  7. #include <stdarg.h>
  8. #include <stddef.h>
  9. #include <stdlib.h>
  10. #include <stdint.h>
  11. #include <stdbool.h>
  12. #include <string.h>
  13. #include <signal.h>
  14. #include "csh.h"
  15. #include "CherryRL/chry_readline.c"
  16. #if defined(CONFIG_CSH_DEBUG) && CONFIG_CSH_DEBUG
  17. #define CHRY_SHELL_PARAM_CHECK(__expr, __ret) \
  18. do { \
  19. if (!(__expr)) { \
  20. return __ret; \
  21. } \
  22. } while (0)
  23. #else
  24. #define CHRY_SHELL_PARAM_CHECK(__expr, __ret) ((void)0)
  25. #endif
  26. #if defined(__CC_ARM) || defined(__CLANG_ARM) || defined(__GNUC__) || defined(__ADSPBLACKFIN__) || defined(__ICCARM__) || defined(__ICCRX__) || defined(__ICCRISCV__)
  27. #ifndef __weak
  28. #define __weak __attribute__((weak))
  29. #endif
  30. #else
  31. #ifndef __weak
  32. #define __weak
  33. #endif
  34. #endif
  35. #define chry_shell_offsetof(type, member) ((size_t) & (((type *)0)->member))
  36. #define chry_shell_container_of(ptr, type, member) ((type *)((char *)(ptr)-chry_shell_offsetof(type, member)))
  37. /*!< prompt segment index */
  38. #define CSH_PROMPT_SEG_USER 0
  39. #define CSH_PROMPT_SEG_HOST 2
  40. #define CSH_PROMPT_SEG_PATH 4
  41. extern void chry_shell_port_default_handler(chry_shell_t *csh, int sig);
  42. extern int chry_shell_port_create_context(chry_shell_t *csh, int argc, const char **argv);
  43. extern int chry_shell_port_hash_strcmp(const char *hash, const char *str);
  44. #if defined(CONFIG_CSH_MULTI_THREAD) && CONFIG_CSH_MULTI_THREAD
  45. /* static const uint8_t sigmap[CSH_SIGNAL_COUNT] = { CSH_SIGINT, CSH_SIGQUIT, CSH_SIGKILL, CSH_SIGTERM, CSH_SIGSTOP, CSH_SIGTSTP, CSH_SIGCONT }; */
  46. #if !defined(CONFIG_CSH_SIGNAL_HANDLER) || (CONFIG_CSH_SIGNAL_HANDLER == 0)
  47. static chry_sighandler_t sighdl[CSH_SIGNAL_COUNT] = {
  48. chry_shell_port_default_handler,
  49. chry_shell_port_default_handler,
  50. chry_shell_port_default_handler,
  51. chry_shell_port_default_handler,
  52. chry_shell_port_default_handler,
  53. chry_shell_port_default_handler,
  54. chry_shell_port_default_handler,
  55. };
  56. #endif
  57. /*****************************************************************************
  58. * @brief default signal handler
  59. *
  60. * @param[in] signum signal number
  61. *
  62. *****************************************************************************/
  63. __weak void chry_shell_port_default_handler(chry_shell_t *csh, int sig)
  64. {
  65. (void)csh;
  66. switch (sig) {
  67. case CSH_SIGINT:
  68. break;
  69. case CSH_SIGQUIT:
  70. break;
  71. case CSH_SIGKILL:
  72. break;
  73. case CSH_SIGTERM:
  74. break;
  75. case CSH_SIGSTOP:
  76. break;
  77. case CSH_SIGTSTP:
  78. break;
  79. case CSH_SIGCONT:
  80. break;
  81. default:
  82. break;
  83. }
  84. }
  85. /*****************************************************************************
  86. * @brief create context to execute
  87. *
  88. * @param[in] csh shell instance
  89. * @param[in] argc argument count
  90. * @param[in] argv argument value
  91. *
  92. * @retval 0:success -1:error
  93. *****************************************************************************/
  94. __weak int chry_shell_port_create_context(chry_shell_t *csh, int argc, const char **argv)
  95. {
  96. (void)csh;
  97. (void)argc;
  98. (void)argv;
  99. return -1;
  100. }
  101. /*****************************************************************************
  102. * @brief conversion signum
  103. *
  104. * @param[in] signum signal number
  105. *
  106. * @retval 0<= signal index -1:not find
  107. *****************************************************************************/
  108. /*
  109. static int chry_shell_conversion_signum(int signum)
  110. {
  111. int sigidx = -1;
  112. for (uint8_t i = 0; i < CSH_SIGNAL_COUNT; i++) {
  113. if (sigmap[i] == signum) {
  114. sigidx = i;
  115. break;
  116. }
  117. }
  118. return sigidx;
  119. }
  120. */
  121. #endif
  122. /*****************************************************************************
  123. * @brief hash str then compare
  124. *
  125. * @param[in] hash hash
  126. * @param[in] str string
  127. *
  128. * @retval 0:Equal 1:hash>str -1:str>hash
  129. *****************************************************************************/
  130. __weak int chry_shell_port_hash_strcmp(const char *hash, const char *str)
  131. {
  132. /*!< simple demo */
  133. return strcmp(hash, str);
  134. }
  135. #if defined(CONFIG_CSH_COMPLETION) && CONFIG_CSH_COMPLETION
  136. /*****************************************************************************
  137. * @brief completion callback
  138. *
  139. * @param[in] rl readline instance
  140. * @param[in] pre pre string
  141. * @param[in] size strlen(pre)
  142. * @param[in] argv argument value (completion)
  143. * @param[in] argl argument length (completion length)
  144. * @param[in] argcmax argument max count (max completion count)
  145. *
  146. * @retval argc:argument count, max is argcmax
  147. *****************************************************************************/
  148. static uint8_t chry_shell_completion_callback(chry_readline_t *rl, char *pre, uint16_t *size, const char **argv, uint8_t *argl, uint8_t argcmax)
  149. {
  150. chry_shell_t *csh = chry_shell_container_of(rl, chry_shell_t, rl);
  151. uint8_t argc = 0;
  152. if (*size == 0) {
  153. return argc;
  154. }
  155. /*!< environment variable */
  156. if (*size && pre[0] == '$') {
  157. pre++; /*!< skip $ */
  158. *size = *size - 1; /*!< skip $ */
  159. for (const chry_sysvar_t *var = csh->var_tbl_beg; var < csh->var_tbl_end; var++) {
  160. if (strncmp(pre, var->name, *size) == 0) {
  161. argv[argc] = var->name;
  162. argl[argc] = strlen(var->name);
  163. argc++;
  164. if (argc >= argcmax) {
  165. break;
  166. }
  167. }
  168. }
  169. return argc;
  170. }
  171. const char *PATH = NULL;
  172. uint8_t Pargc;
  173. uint8_t Pargl[CONFIG_CSH_MAXSEG_PATH + 1];
  174. const char *Pargv[CONFIG_CSH_MAXSEG_PATH + 1];
  175. if (((*size >= 1) && (pre[0] == '/')) ||
  176. ((*size >= 2) && (pre[0] == '.') && (pre[1] == '/')) ||
  177. ((*size >= 3) && (pre[0] == '.') && (pre[1] == '.') && (pre[2] == '/'))) {
  178. /*!< search from the current path */
  179. PATH = csh->path;
  180. } else {
  181. /*!< search from the environment PATH */
  182. for (const chry_sysvar_t *var = csh->var_tbl_beg; var < csh->var_tbl_end; var++) {
  183. if (strcmp("PATH", var->name) == 0) {
  184. PATH = var->var;
  185. break;
  186. }
  187. }
  188. }
  189. while (PATH != NULL) {
  190. Pargc = chry_shell_path_resolve(PATH, pre, Pargv, Pargl, CONFIG_CSH_MAXSEG_PATH + 1);
  191. *size = Pargl[Pargc - 1];
  192. /*!< match from system path */
  193. for (const chry_syscall_t *call = csh->cmd_tbl_beg; call < csh->cmd_tbl_end; call++) {
  194. uint8_t clen = 0;
  195. const char *cpath_next;
  196. const char *cpath_prev = NULL;
  197. const char *cpath = call->path;
  198. bool match = true;
  199. for (uint8_t Pargi = 0;; Pargi++) {
  200. if (Pargi >= Pargc) {
  201. bool exist = false;
  202. for (uint8_t i = 0; i < argc; i++) {
  203. if ((clen == argl[i]) && (strncmp(argv[i], cpath_prev, clen) == 0)) {
  204. exist = true;
  205. break;
  206. }
  207. }
  208. if (exist == false) {
  209. argv[argc] = cpath_prev;
  210. argl[argc] = clen;
  211. argc++;
  212. if (argc >= argcmax) {
  213. return argc;
  214. }
  215. }
  216. break;
  217. }
  218. if ((cpath == NULL) || (match == false)) {
  219. break;
  220. }
  221. cpath++;
  222. if (strncmp(cpath, Pargv[Pargi], Pargl[Pargi]) != 0) {
  223. break;
  224. }
  225. cpath_next = strchr(cpath, '/');
  226. if (cpath_next == NULL) {
  227. clen = strlen(cpath);
  228. } else {
  229. clen = (uint8_t)(cpath_next - cpath);
  230. }
  231. match = (clen == Pargl[Pargi]);
  232. cpath_prev = cpath;
  233. if (cpath != call->name) {
  234. cpath = cpath_next == NULL ? (call->name - 1) : cpath_next;
  235. } else {
  236. cpath = cpath_next;
  237. }
  238. }
  239. }
  240. /*!< match from port of file system */
  241. PATH = strchr(PATH, ':');
  242. PATH = (PATH != NULL) ? PATH + 1 : PATH;
  243. }
  244. return argc;
  245. }
  246. #endif
  247. #if defined(CONFIG_CSH_USER_CALLBACK) && CONFIG_CSH_USER_CALLBACK
  248. /*****************************************************************************
  249. * @brief user callback
  250. *
  251. * @param[in] rl readline instance
  252. * @param[in] exec exec code
  253. *
  254. * @retval status
  255. *****************************************************************************/
  256. static int chry_shell_user_callback(chry_readline_t *rl, uint8_t exec)
  257. {
  258. chry_shell_t *csh = chry_shell_container_of(rl, chry_shell_t, rl);
  259. (void)csh;
  260. /*!< user event callback will not output newline automatically */
  261. switch (exec) {
  262. case CHRY_READLINE_EXEC_EOF:
  263. chry_readline_newline(rl);
  264. break;
  265. #if defined(CONFIG_CSH_MULTI_THREAD) && CONFIG_CSH_MULTI_THREAD
  266. #if defined(CONFIG_CSH_SIGNAL_HANDLER) && CONFIG_CSH_SIGNAL_HANDLER
  267. case CHRY_READLINE_EXEC_SIGINT:
  268. csh->sighdl[0](csh, CSH_SIGINT);
  269. return 0;
  270. case CHRY_READLINE_EXEC_SIGQUIT:
  271. csh->sighdl[1](csh, CSH_SIGQUIT);
  272. return 0;
  273. case CHRY_READLINE_EXEC_SIGCONT:
  274. csh->sighdl[6](csh, CSH_SIGCONT);
  275. return 1;
  276. case CHRY_READLINE_EXEC_SIGSTOP:
  277. csh->sighdl[4](csh, CSH_SIGSTOP);
  278. return 1;
  279. case CHRY_READLINE_EXEC_SIGTSTP:
  280. csh->sighdl[5](csh, CSH_SIGTSTP);
  281. return 1;
  282. #else
  283. case CHRY_READLINE_EXEC_SIGINT:
  284. sighdl[0](csh, CSH_SIGINT);
  285. return 0;
  286. case CHRY_READLINE_EXEC_SIGQUIT:
  287. sighdl[1](csh, CSH_SIGQUIT);
  288. return 0;
  289. case CHRY_READLINE_EXEC_SIGCONT:
  290. sighdl[6](csh, CSH_SIGCONT);
  291. return 1;
  292. case CHRY_READLINE_EXEC_SIGSTOP:
  293. sighdl[4](csh, CSH_SIGSTOP);
  294. return 1;
  295. case CHRY_READLINE_EXEC_SIGTSTP:
  296. sighdl[5](csh, CSH_SIGTSTP);
  297. return 1;
  298. #endif
  299. #else
  300. case CHRY_READLINE_EXEC_SIGINT:
  301. return 0;
  302. case CHRY_READLINE_EXEC_SIGQUIT:
  303. return 0;
  304. case CHRY_READLINE_EXEC_SIGCONT:
  305. return 1;
  306. case CHRY_READLINE_EXEC_SIGSTOP:
  307. return 1;
  308. case CHRY_READLINE_EXEC_SIGTSTP:
  309. return 1;
  310. #endif
  311. case CHRY_READLINE_EXEC_F1 ... CHRY_READLINE_EXEC_F12:
  312. chry_readline_newline(rl);
  313. break;
  314. case CHRY_READLINE_EXEC_USER ... CHRY_READLINE_EXEC_END:
  315. chry_readline_newline(rl);
  316. break;
  317. default:
  318. return -1;
  319. }
  320. /*!< return 1 will not refresh */
  321. /*!< return 0 to refresh whole line (include prompt) */
  322. /*!< return -1 to end readline (error) */
  323. return 0;
  324. }
  325. #endif
  326. /*****************************************************************************
  327. * @brief init shell
  328. *
  329. * @param[in] csh shell instance
  330. * @param[in] init init config
  331. *
  332. * @retval 0:Success -1:Error
  333. *****************************************************************************/
  334. int chry_shell_init(chry_shell_t *csh, const chry_shell_init_t *init)
  335. {
  336. CHRY_SHELL_PARAM_CHECK(NULL != csh, -1);
  337. CHRY_SHELL_PARAM_CHECK(NULL != init, -1);
  338. CHRY_SHELL_PARAM_CHECK(NULL != init->sget, -1);
  339. CHRY_SHELL_PARAM_CHECK(NULL != init->sput, -1);
  340. #if defined(CONFIG_CSH_PROMPTEDIT) && CONFIG_CSH_PROMPTEDIT
  341. CHRY_SHELL_PARAM_CHECK(NULL != init->prompt_buffer, -1);
  342. #endif
  343. #if defined(CONFIG_CSH_HISTORY) && CONFIG_CSH_HISTORY
  344. CHRY_SHELL_PARAM_CHECK(NULL != init->history_buffer, -1);
  345. #endif
  346. #if defined(CONFIG_CSH_LNBUFF_STATIC) && CONFIG_CSH_LNBUFF_STATIC
  347. CHRY_SHELL_PARAM_CHECK(NULL != init->line_buffer, -1);
  348. #endif
  349. int ret = 0;
  350. chry_readline_init_t rl_init;
  351. /*!< do readline init */
  352. #if defined(CONFIG_CSH_PROMPTEDIT) && CONFIG_CSH_PROMPTEDIT
  353. rl_init.prompt = init->prompt_buffer;
  354. rl_init.pptsize = init->prompt_buffer_size;
  355. #else
  356. rl_init.prompt = "$ ";
  357. rl_init.pptsize = 3;
  358. #endif
  359. rl_init.history = init->history_buffer;
  360. rl_init.histsize = init->history_buffer_size;
  361. rl_init.sput = init->sput;
  362. rl_init.sget = init->sget;
  363. if (chry_readline_init(&csh->rl, &rl_init)) {
  364. return -1;
  365. }
  366. csh->cmd_tbl_beg = init->command_table_beg;
  367. csh->cmd_tbl_end = init->command_table_end;
  368. csh->var_tbl_beg = init->variable_table_beg;
  369. csh->var_tbl_end = init->variable_table_end;
  370. #if defined(CONFIG_CSH_LNBUFF_STATIC) && CONFIG_CSH_LNBUFF_STATIC
  371. csh->linebuff = init->line_buffer;
  372. csh->buffsize = init->line_buffer_size;
  373. csh->linesize = 0;
  374. #endif
  375. csh->exec_code = 0;
  376. csh->exec = CSH_STATUS_EXEC_IDLE;
  377. /*!< set default user id */
  378. if ((init->uid >= CONFIG_CSH_MAX_USER) || (init->uid < 0)) {
  379. return -1;
  380. }
  381. csh->uid = init->uid;
  382. /*!< set host name */
  383. if (strnlen(init->host, 255) == 255) {
  384. return -1;
  385. }
  386. csh->host = init->host;
  387. /*!< set users name */
  388. for (uint8_t i = 0; i < CONFIG_CSH_MAX_USER; i++) {
  389. if (strnlen(init->user[i], 255) == 255) {
  390. return -1;
  391. }
  392. if (strnlen(init->hash[i], 255) == 255) {
  393. return -1;
  394. }
  395. csh->user[i] = init->user[i];
  396. csh->hash[i] = init->hash[i];
  397. }
  398. /*!< set default path */
  399. #if defined(CONFIG_CSH_MAXLEN_PATH) && CONFIG_CSH_MAXLEN_PATH
  400. csh->path[0] = '/';
  401. csh->path[1] = '\0';
  402. #else
  403. csh->path = "/";
  404. #endif
  405. csh->data = NULL;
  406. csh->user_data = init->user_data;
  407. #if defined(CONFIG_CSH_COMPLETION) && CONFIG_CSH_COMPLETION
  408. chry_readline_set_completion_cb(&csh->rl, chry_shell_completion_callback);
  409. #endif
  410. #if defined(CONFIG_CSH_USER_CALLBACK) && CONFIG_CSH_USER_CALLBACK
  411. chry_readline_set_user_cb(&csh->rl, chry_shell_user_callback);
  412. #endif
  413. #if defined(CONFIG_CSH_PROMPTEDIT) && CONFIG_CSH_PROMPTEDIT
  414. ret |= chry_readline_prompt_edit(&csh->rl, CSH_PROMPT_SEG_USER, (chry_readline_sgr_t){ .foreground = CHRY_READLINE_SGR_GREEN, .bold = 1 }.raw, csh->user[csh->uid]);
  415. ret |= chry_readline_prompt_edit(&csh->rl, CSH_PROMPT_SEG_USER + 1, (chry_readline_sgr_t){ .foreground = CHRY_READLINE_SGR_GREEN, .bold = 1 }.raw, "@");
  416. ret |= chry_readline_prompt_edit(&csh->rl, CSH_PROMPT_SEG_HOST, (chry_readline_sgr_t){ .foreground = CHRY_READLINE_SGR_GREEN, .bold = 1 }.raw, csh->host);
  417. ret |= chry_readline_prompt_edit(&csh->rl, CSH_PROMPT_SEG_HOST + 1, 0, ":");
  418. ret |= chry_readline_prompt_edit(&csh->rl, CSH_PROMPT_SEG_PATH, (chry_readline_sgr_t){ .foreground = CHRY_READLINE_SGR_BLUE, .bold = 1 }.raw, csh->path);
  419. ret |= chry_readline_prompt_edit(&csh->rl, CSH_PROMPT_SEG_PATH + 1, 0, "$ ");
  420. #else
  421. #endif
  422. return ret;
  423. }
  424. /*****************************************************************************
  425. * @brief execute task internal
  426. *
  427. * @param[in] csh shell instance
  428. *
  429. *****************************************************************************/
  430. static void chry_shell_task_exec_internal(chry_shell_t *csh, int argc, const char **argv)
  431. {
  432. volatile uint8_t *pexec = (void *)&csh->exec;
  433. volatile int *pcode = (void *)&csh->exec_code;
  434. /*!< if stage find */
  435. if (*pexec == CSH_STATUS_EXEC_FIND) {
  436. /*!< stage prepare */
  437. *pexec = CSH_STATUS_EXEC_PREP;
  438. *pcode = ((chry_syscall_func_t)argv[argc + 2])(argc, (void *)argv);
  439. /*!< stage done */
  440. *pexec = CSH_STATUS_EXEC_DONE;
  441. }
  442. }
  443. /*****************************************************************************
  444. * @brief execute task
  445. *
  446. * @param[in] csh shell instance
  447. *
  448. *****************************************************************************/
  449. void chry_shell_task_exec(chry_shell_t *csh)
  450. {
  451. CHRY_SHELL_PARAM_CHECK(NULL != csh, );
  452. (void)csh;
  453. #if defined(CONFIG_CSH_MULTI_THREAD) && CONFIG_CSH_MULTI_THREAD
  454. chry_shell_task_exec_internal(csh, csh->exec_argc, &csh->exec_argv[0]);
  455. #endif
  456. }
  457. /*****************************************************************************
  458. * @brief read eval print loop task
  459. *
  460. * @param[in] csh shell instance
  461. *
  462. * @retval 0:Success -1:Error 1:Continue
  463. *****************************************************************************/
  464. int chry_shell_task_repl(chry_shell_t *csh)
  465. {
  466. CHRY_SHELL_PARAM_CHECK(NULL != csh, -1);
  467. char *csh_linebuff;
  468. uint16_t *csh_linesize;
  469. #if defined(CONFIG_CSH_LNBUFF_STATIC) && CONFIG_CSH_LNBUFF_STATIC
  470. csh_linebuff = csh->linebuff;
  471. csh_linesize = &csh->linesize;
  472. char *line = chry_readline(&csh->rl, csh_linebuff, csh->buffsize, csh_linesize);
  473. #else
  474. char linebuff[CONFIG_CSH_LNBUFF_SIZE];
  475. uint16_t linesize;
  476. csh_linebuff = linebuff;
  477. csh_linesize = &linesize;
  478. char *line = chry_readline(&csh->rl, csh_linebuff, CONFIG_CSH_LNBUFF_SIZE, csh_linesize);
  479. #endif
  480. if (line == NULL) {
  481. return -1;
  482. } else if (line == (void *)-1) {
  483. return 1;
  484. } else if (*csh_linesize) {
  485. int *argc;
  486. const char **argv;
  487. volatile uint8_t *pexec = (void *)&csh->exec;
  488. volatile uint32_t *pcode = (void *)&csh->exec_code;
  489. *pcode = 0xBAD2BE8E; /*!< a magic number */
  490. #if defined(CONFIG_CSH_MULTI_THREAD) && CONFIG_CSH_MULTI_THREAD
  491. argc = &csh->exec_argc;
  492. argv = &csh->exec_argv[0];
  493. #else
  494. int csh_exec_argc;
  495. const char *csh_exec_argv[CONFIG_CSH_MAX_ARG + 3];
  496. argc = &csh_exec_argc;
  497. argv = &csh_exec_argv[0];
  498. #endif
  499. *argc = chry_shell_parse(line, *csh_linesize, argv, CONFIG_CSH_MAX_ARG + 1);
  500. /*!< compile environment variable */
  501. for (uint8_t i = 0; i < *argc;) {
  502. if (argv[i][0] == '$') {
  503. const char *param = chry_shell_getenv(csh, argv[i] + 1);
  504. if (param == NULL) {
  505. memmove(&argv[i], &argv[i + 1], (*argc - i) * sizeof(char *));
  506. *argc = (*argc > 0) ? *argc - 1 : 0;
  507. } else {
  508. argv[i] = param;
  509. i++;
  510. }
  511. } else {
  512. i++;
  513. }
  514. }
  515. argv[*argc + 1] = (void *)csh;
  516. argv[*argc + 2] = NULL;
  517. if (*argc) {
  518. uint8_t size = strlen(argv[0]);
  519. const char *PATH = NULL;
  520. uint8_t Pargc;
  521. uint8_t Pargl[CONFIG_CSH_MAXSEG_PATH + 1];
  522. const char *Pargv[CONFIG_CSH_MAXSEG_PATH + 1];
  523. if (((size >= 1) && (argv[0][0] == '/')) ||
  524. ((size >= 2) && (argv[0][0] == '.') && (argv[0][1] == '/')) ||
  525. ((size >= 3) && (argv[0][0] == '.') && (argv[0][1] == '.') && (argv[0][2] == '/'))) {
  526. /*!< search from the current path */
  527. PATH = csh->path;
  528. } else {
  529. /*!< search from the environment PATH */
  530. for (const chry_sysvar_t *var = csh->var_tbl_beg; var < csh->var_tbl_end; var++) {
  531. if (strcmp("PATH", var->name) == 0) {
  532. PATH = var->var;
  533. break;
  534. }
  535. }
  536. }
  537. while (PATH != NULL) {
  538. Pargc = chry_shell_path_resolve(PATH, argv[0], Pargv, Pargl, CONFIG_CSH_MAXSEG_PATH + 1);
  539. /*!< match from system path */
  540. for (const chry_syscall_t *call = csh->cmd_tbl_beg; call < csh->cmd_tbl_end; call++) {
  541. uint8_t clen = 0;
  542. const char *cpath_next;
  543. const char *cpath = call->path;
  544. for (uint8_t Pargi = 0;; Pargi++) {
  545. if (Pargi >= Pargc) {
  546. if (cpath == NULL) {
  547. /*!< match success */
  548. argv[*argc + 2] = (void *)call->func;
  549. goto found;
  550. } else {
  551. /*!< argv[0] end */
  552. break;
  553. }
  554. }
  555. if (cpath == NULL) {
  556. /*!< path+name end */
  557. break;
  558. }
  559. cpath++;
  560. if (strncmp(cpath, Pargv[Pargi], Pargl[Pargi]) != 0) {
  561. /*!< match failed */
  562. break;
  563. }
  564. cpath_next = strchr(cpath, '/');
  565. if (cpath_next == NULL) {
  566. clen = strlen(cpath);
  567. } else {
  568. clen = (uint8_t)(cpath_next - cpath);
  569. }
  570. if (clen != Pargl[Pargi]) {
  571. /*!< len not equal */
  572. break;
  573. }
  574. if (cpath != call->name) {
  575. cpath = cpath_next == NULL ? (call->name - 1) : cpath_next;
  576. } else {
  577. cpath = cpath_next;
  578. }
  579. }
  580. }
  581. /*!< match from port of file system */
  582. PATH = strchr(PATH, ':');
  583. PATH = (PATH != NULL) ? PATH + 1 : PATH;
  584. }
  585. found:
  586. if (NULL == argv[*argc + 2]) {
  587. *pexec = CSH_STATUS_EXEC_IDLE;
  588. csh->rl.sput(&csh->rl, argv[0], strlen(argv[0]));
  589. csh->rl.sput(&csh->rl, ": command not found" CONFIG_CSH_NEWLINE CONFIG_CSH_NEWLINE,
  590. 19 + (sizeof(CONFIG_CSH_NEWLINE) ? (sizeof(CONFIG_CSH_NEWLINE) - 1) * 2 : 0));
  591. } else {
  592. #if defined(CONFIG_CSH_MULTI_THREAD) && CONFIG_CSH_MULTI_THREAD
  593. *pexec = CSH_STATUS_EXEC_FIND;
  594. chry_readline_ignore(&csh->rl, true); /*!< accepts only signal inputs */
  595. chry_readline_auto_refresh(&csh->rl, false);
  596. /*!< reset signal handler to default */
  597. for (uint8_t i = 0; i < CSH_SIGNAL_COUNT; i++) {
  598. #if defined(CONFIG_CSH_SIGNAL_HANDLER) && CONFIG_CSH_SIGNAL_HANDLER
  599. csh->sighdl[i] = chry_shell_port_default_handler;
  600. #else
  601. sighdl[i] = chry_shell_port_default_handler;
  602. #endif
  603. }
  604. /*!< try to create execute context */
  605. if (0 != chry_shell_port_create_context(csh, *argc, argv)) {
  606. *pexec = CSH_STATUS_EXEC_IDLE;
  607. csh->rl.sput(&csh->rl, argv[0], strlen(argv[0]));
  608. csh->rl.sput(&csh->rl, ": context creation error" CONFIG_CSH_NEWLINE CONFIG_CSH_NEWLINE,
  609. 24 + (sizeof(CONFIG_CSH_NEWLINE) ? (sizeof(CONFIG_CSH_NEWLINE) - 1) * 2 : 0));
  610. }
  611. #else
  612. *pexec = CSH_STATUS_EXEC_FIND;
  613. chry_shell_task_exec_internal(csh, *argc, argv);
  614. *pexec = CSH_STATUS_EXEC_IDLE;
  615. #endif
  616. }
  617. }
  618. return 0;
  619. }
  620. return 0;
  621. }
  622. /*****************************************************************************
  623. * @brief parse line to argc,argv[]
  624. *
  625. * @param[in] line read line
  626. * @param[in] linesize strlen(line)
  627. * @param[out] argv argument value
  628. * @param[out] argcmax max argument count (max segment count)
  629. *
  630. * @retval argc:argument count, max is argcmax
  631. *****************************************************************************/
  632. int chry_shell_parse(char *line, uint32_t linesize, const char **argv, uint8_t argcmax)
  633. {
  634. int argc = 0;
  635. bool ignore = false;
  636. bool start = false;
  637. bool escape = false;
  638. argcmax = (argcmax > 0) ? argcmax - 1 : argcmax;
  639. while (linesize) {
  640. char c = *line;
  641. if (c == '\0') {
  642. break;
  643. } else if ((escape == false) && (c == '\\')) {
  644. escape = true;
  645. memmove(line, line + 1, linesize);
  646. line--;
  647. } else if ((escape == false) && (ignore == false) && (c == ' ')) {
  648. start = false;
  649. *line = '\0';
  650. } else if ((escape == false) && (c == '"')) {
  651. memmove(line, line + 1, linesize);
  652. line--;
  653. ignore = !ignore;
  654. } else if (!start) {
  655. argv[argc] = line;
  656. if (argc < argcmax) {
  657. argc++;
  658. } else {
  659. break;
  660. }
  661. start = true;
  662. escape = false;
  663. } else {
  664. escape = false;
  665. }
  666. line++;
  667. linesize--;
  668. }
  669. *line = '\0';
  670. argv[argc] = NULL;
  671. return argc;
  672. }
  673. /*****************************************************************************
  674. * @brief resolve path to argc,argv[],argl[]
  675. *
  676. * @param[in] cur current path
  677. * @param[in] path path
  678. * @param[out] argv argument value (path segment)
  679. * @param[out] argl argument length (path segment length)
  680. * @param[out] argcmax argument max count (max segment count)
  681. *
  682. * @retval argc:argument count, max is argcmax
  683. *****************************************************************************/
  684. int chry_shell_path_resolve(const char *cur, const char *path, const char **argv, uint8_t *argl, uint8_t argcmax)
  685. {
  686. uint8_t len;
  687. uint8_t argc = 0;
  688. bool expr;
  689. if (cur == NULL || path == NULL) {
  690. return argc;
  691. }
  692. expr = (*path == '/') || ((cur[0] == '/') && (cur[1] == '\0'));
  693. if (expr) {
  694. cur = path;
  695. }
  696. argcmax = (argcmax > 0) ? argcmax - 1 : argcmax;
  697. resolve:
  698. while ((cur != NULL) && (*cur != '\0') && (*cur != ':')) {
  699. while (*cur == '/') {
  700. cur++;
  701. }
  702. argv[argc] = cur;
  703. cur = strchr(cur, '/');
  704. if (cur == NULL) {
  705. len = strlen(argv[argc]);
  706. } else {
  707. len = (uint8_t)(cur - argv[argc]);
  708. const char *sep = memchr(argv[argc], ':', len);
  709. if (sep != NULL) {
  710. len = (uint8_t)(sep - argv[argc]);
  711. cur = NULL;
  712. }
  713. }
  714. if (len == 1) {
  715. bool match_dot1 = argv[argc][0] == '.';
  716. if (!match_dot1) {
  717. argl[argc] = len;
  718. argc++;
  719. }
  720. } else if (len == 2) {
  721. bool match_dot1 = argv[argc][0] == '.';
  722. bool match_dot2 = argv[argc][1] == '.';
  723. if (match_dot1 && match_dot2) {
  724. if (argc > 0) {
  725. argc--;
  726. }
  727. } else {
  728. argl[argc] = len;
  729. argc++;
  730. }
  731. } else {
  732. argl[argc] = len;
  733. argc++;
  734. }
  735. if (argc >= argcmax) {
  736. argv[argc] = NULL;
  737. argl[argc] = 0;
  738. return argcmax;
  739. }
  740. }
  741. if (!expr) {
  742. cur = path;
  743. expr = true;
  744. goto resolve;
  745. }
  746. argv[argc] = NULL;
  747. argl[argc] = 0;
  748. return argc;
  749. }
  750. /*****************************************************************************
  751. * @brief set host
  752. *
  753. * @param[in] csh shell instance
  754. * @param[in] host pointer of host
  755. *
  756. * @retval 0:Success -1:Error
  757. *****************************************************************************/
  758. int chry_shell_set_host(chry_shell_t *csh, const char *host)
  759. {
  760. CHRY_SHELL_PARAM_CHECK(NULL != csh, -1);
  761. CHRY_SHELL_PARAM_CHECK(NULL != host, -1);
  762. if (strnlen(host, 255) == 255) {
  763. return -1;
  764. }
  765. csh->host = host;
  766. return chry_readline_prompt_edit(&csh->rl, CSH_PROMPT_SEG_HOST, (chry_readline_sgr_t){ .foreground = CHRY_READLINE_SGR_GREEN, .bold = 1 }.raw, csh->host);
  767. }
  768. /*****************************************************************************
  769. * @brief set user
  770. *
  771. * @param[in] csh shell instance
  772. * @param[in] uid user id
  773. * @param[in] user pointer of user
  774. * @param[in] user pointer of user password hash
  775. *
  776. * @retval 0:Success -1:Error
  777. *****************************************************************************/
  778. int chry_shell_set_user(chry_shell_t *csh, uint8_t uid, const char *user, const char *hash)
  779. {
  780. CHRY_SHELL_PARAM_CHECK(NULL != csh, -1);
  781. CHRY_SHELL_PARAM_CHECK(NULL != user, -1);
  782. if (uid < CONFIG_CSH_MAX_USER) {
  783. if (strnlen(user, 255) == 255) {
  784. return -1;
  785. }
  786. if (strnlen(hash, 255) == 255) {
  787. return -1;
  788. }
  789. csh->user[uid] = user;
  790. csh->hash[uid] = hash;
  791. if (uid == csh->uid) {
  792. return chry_readline_prompt_edit(&csh->rl, CSH_PROMPT_SEG_USER, (chry_readline_sgr_t){ .foreground = CHRY_READLINE_SGR_GREEN, .bold = 1 }.raw, csh->user[uid]);
  793. }
  794. return 0;
  795. }
  796. return -1;
  797. }
  798. /*****************************************************************************
  799. * @brief set path
  800. *
  801. * @param[in] csh shell instance
  802. * @param[in] size strlen(path) + 1, include \0.
  803. * ignore when CONFIG_CSH_MAXLEN_PATH = 0
  804. * @param[in] path shell instance
  805. *
  806. * @retval 0:Success -1:Error
  807. *****************************************************************************/
  808. int chry_shell_set_path(chry_shell_t *csh, uint8_t size, const char *path)
  809. {
  810. CHRY_SHELL_PARAM_CHECK(NULL != csh, -1);
  811. CHRY_SHELL_PARAM_CHECK(NULL != path, -1);
  812. #if defined(CONFIG_CSH_MAXLEN_PATH) && CONFIG_CSH_MAXLEN_PATH
  813. if (size > CONFIG_CSH_MAXLEN_PATH) {
  814. return -1;
  815. }
  816. memcpy(csh->path, path, size);
  817. csh->path[size] = '\0';
  818. #else
  819. if (strnlen(path, 255) == 255) {
  820. return -1;
  821. }
  822. csh->path = path;
  823. #endif
  824. return chry_readline_prompt_edit(&csh->rl, CSH_PROMPT_SEG_PATH, (chry_readline_sgr_t){ .foreground = CHRY_READLINE_SGR_BLUE, .bold = 1 }.raw, csh->path);
  825. }
  826. /*****************************************************************************
  827. * @brief get path
  828. *
  829. * @param[in] csh shell instance
  830. * @param[in] size path buffer size
  831. * @param[in] path path buffer
  832. *
  833. *****************************************************************************/
  834. void chry_shell_get_path(chry_shell_t *csh, uint8_t size, char *path)
  835. {
  836. CHRY_SHELL_PARAM_CHECK(NULL != csh, );
  837. CHRY_SHELL_PARAM_CHECK(NULL != path, );
  838. #if defined(CONFIG_CSH_MAXLEN_PATH) && CONFIG_CSH_MAXLEN_PATH
  839. size = size < CONFIG_CSH_MAXLEN_PATH ? size : CONFIG_CSH_MAXLEN_PATH;
  840. #endif
  841. strncpy(path, csh->path, size);
  842. path[size - 1] = '\0';
  843. }
  844. /*****************************************************************************
  845. * @brief substitute user
  846. *
  847. * @param[in] csh shell instance
  848. * @param[in] uid user id
  849. * @param[in] password password
  850. *
  851. * @retval 0:Success -1:Error
  852. *****************************************************************************/
  853. int chry_shell_substitute_user(chry_shell_t *csh, uint8_t uid, const char *password)
  854. {
  855. CHRY_SHELL_PARAM_CHECK(NULL != csh, -1);
  856. if (uid < CONFIG_CSH_MAX_USER) {
  857. if (csh->hash[uid]) {
  858. if (chry_shell_port_hash_strcmp(csh->hash[uid], password) != 0) {
  859. return -1;
  860. }
  861. }
  862. csh->uid = uid;
  863. return chry_readline_prompt_edit(&csh->rl, CSH_PROMPT_SEG_USER, (chry_readline_sgr_t){ .foreground = CHRY_READLINE_SGR_GREEN, .bold = 1 }.raw, csh->user[uid]);
  864. }
  865. return -1;
  866. }
  867. /*****************************************************************************
  868. * @brief get environment variable
  869. *
  870. * @param[in] csh shell instance
  871. * @param[in] name env name
  872. *
  873. * @retval env string pointer or NULL
  874. * @note env string maybe read-only
  875. *****************************************************************************/
  876. char *chry_shell_getenv(chry_shell_t *csh, const char *name)
  877. {
  878. CHRY_SHELL_PARAM_CHECK(NULL != csh, NULL);
  879. char *env = NULL;
  880. for (const chry_sysvar_t *var = csh->var_tbl_beg; var < csh->var_tbl_end; var++) {
  881. if (strcmp(name, var->name) == 0) {
  882. env = var->var;
  883. break;
  884. }
  885. }
  886. return env;
  887. }
  888. /*****************************************************************************
  889. * @brief print from shell
  890. *
  891. * @param[in] csh shell instance
  892. * @param[in] fmt string format
  893. *
  894. * @retval size that has printed
  895. *****************************************************************************/
  896. int csh_printf(chry_shell_t *csh, const char *fmt, ...)
  897. {
  898. int n;
  899. char shell_printf_buffer[CONFIG_CSH_PRINT_BUFFER_SIZE];
  900. va_list args;
  901. va_start (args, fmt);
  902. n = vsnprintf(shell_printf_buffer, sizeof(shell_printf_buffer), fmt, args);
  903. if (n > (int)sizeof(shell_printf_buffer)) {
  904. csh->rl.sput(&csh->rl, shell_printf_buffer, sizeof(shell_printf_buffer));
  905. } else if (n > 0) {
  906. csh->rl.sput(&csh->rl, shell_printf_buffer, n);
  907. }
  908. va_end(args);
  909. return n;
  910. }