shellmatta.c 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. /*
  2. * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
  3. *
  4. * This Source Code Form is subject to the terms of the Mozilla Public
  5. * License, v. 2.0. If a copy of the MPL was not distributed with this
  6. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  7. */
  8. /**
  9. * @file shellmatta.c
  10. * @brief Main implementation of the Shellmatta terminal implementation
  11. * @author Stefan Strobel <stefan.strobel@shimatta.net>
  12. */
  13. /**
  14. * @addtogroup shellmatta_private
  15. * @{
  16. */
  17. #include "shellmatta.h"
  18. #include "shellmatta_autocomplete.h"
  19. #include "shellmatta_history.h"
  20. #include "shellmatta_utils.h"
  21. #include "shellmatta_escape.h"
  22. #include "shellmatta_opt.h"
  23. #include "shellmatta_ymodem.h"
  24. #ifdef SHELLMATTA_TRANSPORT
  25. #include "shellmatta_transport.h"
  26. #endif
  27. #ifdef SHELLMATTA_AUTHENTICATION
  28. #include "shellmatta_auth.h"
  29. #endif
  30. #include <stddef.h>
  31. #include <stdint.h>
  32. #include <string.h>
  33. #include <stdarg.h>
  34. #include <stdio.h>
  35. /**
  36. * @brief processes the passed amount of data - called from the transport layer
  37. * @param[in] handle shellmatta instance handle
  38. * @param[in] data pointer to input data to process
  39. * @param[in] size length of input data to process
  40. * @return errorcode #SHELLMATTA_OK
  41. * #SHELLMATTA_USE_FAULT
  42. */
  43. static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t handle,
  44. char *data,
  45. uint32_t size)
  46. {
  47. shellmatta_cmd_t *cmd;
  48. uint8_t cmdExecuted = 0u;
  49. uint32_t cmdLen;
  50. char *tempString;
  51. shellmatta_retCode_t ret = SHELLMATTA_OK;
  52. shellmatta_retCode_t cmdRet;
  53. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  54. shellmatta_ymodem_state_t yModemState;
  55. /** -# in busy mode - keep calling this command */
  56. if(NULL != inst->busyCmd)
  57. {
  58. /** -# just call the function until it is not busy anymore */
  59. (void)shellmatta_opt_reInit(inst);
  60. ret = inst->busyCmd->cmdFct(handle, inst->buffer, inst->inputCount);
  61. if(SHELLMATTA_BUSY == ret)
  62. {
  63. /** -# do nothing - still busy */
  64. }
  65. else if(SHELLMATTA_CONTINUE == ret)
  66. {
  67. inst->continuousCmd = inst->busyCmd;
  68. inst->busyCmd = NULL;
  69. }
  70. else
  71. {
  72. utils_terminateInput(inst);
  73. }
  74. }
  75. /** -# call continuous function even if there is no data */
  76. else if((0u == size) && (NULL != inst->continuousCmd))
  77. {
  78. /** -# just call the function without any new data */
  79. inst->stdinLength = 0u;
  80. inst->buffer[inst->stdinIdx] = '\0';
  81. (void)shellmatta_opt_reInit(inst);
  82. ret = inst->continuousCmd->cmdFct(handle, inst->buffer, inst->inputCount);
  83. if(SHELLMATTA_CONTINUE == ret)
  84. {
  85. /** -# do nothing just continue */
  86. }
  87. else if(SHELLMATTA_BUSY == ret)
  88. {
  89. inst->busyCmd = inst->continuousCmd;
  90. inst->continuousCmd = NULL;
  91. }
  92. else
  93. {
  94. utils_terminateInput(inst);
  95. }
  96. }
  97. else
  98. {
  99. /* nothing to do here - continue parsing the command */
  100. }
  101. /** -# process byte wise */
  102. for (; (inst->byteCounter < size) && (NULL == inst->busyCmd); inst->byteCounter++)
  103. {
  104. /** -# in continuous mode - pass data directly to the command */
  105. if(NULL != inst->continuousCmd)
  106. {
  107. yModemState = shellmatta_ymodem_get_state(handle);
  108. /* if data is coming in while ymodem module is active, forward data to it */
  109. if (yModemState != INACTIVE)
  110. {
  111. shellmatta_ymodem_receive_packet(handle, data[inst->byteCounter]);
  112. }
  113. /** -# copy data and call command function */
  114. inst->buffer[inst->stdinIdx] = data[inst->byteCounter];
  115. inst->buffer[inst->stdinIdx + 1u] = '\0';
  116. inst->stdinLength = 1u;
  117. (void)shellmatta_opt_reInit(inst);
  118. ret = inst->continuousCmd->cmdFct(handle, inst->buffer, inst->inputCount);
  119. /** -# check if continuous mode is canceled or interrupted by busy mode */
  120. if(SHELLMATTA_BUSY == ret)
  121. {
  122. inst->busyCmd = inst->continuousCmd;
  123. inst->continuousCmd = NULL;
  124. }
  125. else if(('\x03' == data[inst->byteCounter]))
  126. {
  127. /* accept cancel only if ymodem is inactive or between packets */
  128. if (yModemState == INACTIVE || yModemState == WAIT_FOR_START)
  129. {
  130. /** -# cancel continue session */
  131. utils_terminateInput(inst);
  132. if (yModemState == WAIT_FOR_START)
  133. {
  134. /** -# explicitly reset ymodem with cancel, if it was active */
  135. shellmatta_ymodem_reset(handle, true);
  136. }
  137. }
  138. ret = SHELLMATTA_OK;
  139. }
  140. else if(SHELLMATTA_CONTINUE == ret)
  141. {
  142. /** -# do nothing - continue */
  143. }
  144. else
  145. {
  146. utils_terminateInput(inst);
  147. }
  148. }
  149. /** -# handle escape sequences */
  150. else if(inst->escapeCounter != 0u)
  151. {
  152. escape_handleSequence(inst, data[inst->byteCounter]);
  153. }
  154. /** -# handle delimiter as start of processing the command */
  155. else if (inst->delimiter == data[inst->byteCounter])
  156. {
  157. if(0u == inst->hereLength)
  158. {
  159. /**
  160. * @dot
  161. * digraph heredocParser {
  162. * start -> wait [ label="<< in first line - store delimiter" ];
  163. * wait -> wait [ label="delimiter not detected" ];
  164. * wait -> end [ label="delimiter found - remove all heredoc stuff and send the input to the command" ];
  165. * }
  166. * @enddot */
  167. /** -# check for heredoc - add string delimiter to stop strstr from searching too far */
  168. inst->buffer[inst->inputCount] = '\0';
  169. tempString = strstr(inst->buffer, "<<");
  170. if(NULL != tempString)
  171. {
  172. /*' -# check if length of heredoc delimiter is valid */
  173. if(inst->inputCount > ((uint32_t)(tempString - inst->buffer) + 2u))
  174. {
  175. inst->hereStartIdx = (uint32_t)(tempString - inst->buffer);
  176. inst->hereDelimiterIdx = inst->hereStartIdx + 2u;
  177. while((inst->hereDelimiterIdx < inst->inputCount)
  178. && ( ('\0' == inst->buffer[inst->hereDelimiterIdx])
  179. || (' ' == inst->buffer[inst->hereDelimiterIdx])))
  180. {
  181. inst->hereDelimiterIdx ++;
  182. }
  183. inst->hereLength = inst->inputCount - inst->hereDelimiterIdx;
  184. inst->dirty = true;
  185. utils_insertChars(inst, &data[inst->byteCounter], 1u);
  186. inst->lastNewlineIdx = inst->inputCount;
  187. }
  188. else
  189. {
  190. inst->hereLength = 0u;
  191. /** -# store the current command and reset the history buffer */
  192. inst->dirty = true;
  193. history_storeCmd(inst);
  194. history_reset(inst);
  195. }
  196. }
  197. else
  198. {
  199. /** -# store the current command and reset the history buffer */
  200. inst->dirty = true;
  201. history_storeCmd(inst);
  202. history_reset(inst);
  203. }
  204. }
  205. else
  206. {
  207. tempString = &inst->buffer[inst->lastNewlineIdx];
  208. cmdLen = inst->inputCount - inst->lastNewlineIdx;
  209. /** -# skip newline characters before comparison */
  210. while(('\n' == *tempString) || ('\r' == *tempString))
  211. {
  212. tempString ++;
  213. cmdLen --;
  214. }
  215. if( (inst->hereLength == cmdLen)
  216. && (0 == strncmp( &inst->buffer[inst->hereDelimiterIdx],
  217. tempString,
  218. inst->hereLength)))
  219. {
  220. /** -# store the current command and reset the history buffer */
  221. inst->dirty = true;
  222. history_storeCmd(inst);
  223. history_reset(inst);
  224. /** -# process heredoc as stdin like input */
  225. /** -# find start of heredoc data */
  226. inst->stdinIdx = inst->hereDelimiterIdx + inst->hereLength;
  227. while( ('\n' == inst->buffer[inst->stdinIdx])
  228. || ('\r' == inst->buffer[inst->stdinIdx]))
  229. {
  230. inst->stdinIdx ++;
  231. }
  232. /** -# calculate length and terminate stdin string */
  233. if(inst->stdinIdx < inst->lastNewlineIdx)
  234. {
  235. inst->stdinLength = inst->lastNewlineIdx - inst->stdinIdx;
  236. inst->buffer[inst->lastNewlineIdx] = '\0';
  237. }
  238. else
  239. {
  240. inst->stdinLength = 0u;
  241. }
  242. /** -# calculate length and terminate argument string */
  243. inst->inputCount = inst->hereStartIdx;
  244. inst->buffer[inst->hereStartIdx] = '\0';
  245. /** -# terminate heredoc session */
  246. inst->hereLength = 0u;
  247. }
  248. else
  249. {
  250. /** -# the party goes on - just print the delimiter and store the position */
  251. inst->lastNewlineIdx = inst->inputCount;
  252. utils_insertChars(inst, &data[inst->byteCounter], 1u);
  253. }
  254. }
  255. if(0u == inst->hereLength)
  256. {
  257. cmd = inst->cmdList;
  258. /** -# determine the cmd len (chars until first space or \0 is found */
  259. cmdLen = 0u;
  260. while( (cmdLen < inst->inputCount)
  261. && (' ' != inst->buffer[cmdLen])
  262. && ('\r' != inst->buffer[cmdLen])
  263. && ('\n' != inst->buffer[cmdLen])
  264. && ('\0' != inst->buffer[cmdLen]))
  265. {
  266. cmdLen ++;
  267. }
  268. /** -# search for a matching command */
  269. while (NULL != cmd)
  270. {
  271. /** -# compare command and alias string and length */
  272. if ( ((cmdLen == strlen(cmd->cmd))
  273. && (0 == strncmp(inst->buffer, cmd->cmd, cmdLen)))
  274. || ((NULL != cmd->cmdAlias)
  275. && (cmdLen == strlen(cmd->cmdAlias))
  276. && (0 == strncmp(inst->buffer, cmd->cmdAlias, cmdLen))))
  277. {
  278. utils_writeEcho(inst, "\r\n", 2u);
  279. shellmatta_opt_init(inst, cmdLen + 1u);
  280. #ifdef SHELLMATTA_AUTHENTICATION
  281. cmdRet = SHELLMATTA_OK;
  282. if (SHELLMATTA_OK == shellmatta_auth_is_cmd_permitted(inst, cmd))
  283. {
  284. cmdExecuted = 1u;
  285. cmdRet = cmd->cmdFct(handle, inst->buffer, inst->inputCount);
  286. }
  287. #else
  288. cmdExecuted = 1u;
  289. cmdRet = cmd->cmdFct(handle, inst->buffer, inst->inputCount);
  290. #endif
  291. switch(cmdRet)
  292. {
  293. case SHELLMATTA_CONTINUE:
  294. /** -# initialize stdin buffer and continuous cmd */
  295. inst->stdinIdx = inst->bufferSize - 2u;
  296. inst->stdinLength = 0u;
  297. inst->continuousCmd = cmd;
  298. ret = cmdRet;
  299. break;
  300. case SHELLMATTA_BUSY:
  301. inst->busyCmd = cmd;
  302. ret = cmdRet;
  303. break;
  304. default:
  305. /* nothing to do - everything ok */
  306. break;
  307. }
  308. cmd = NULL;
  309. }
  310. else
  311. {
  312. cmd = cmd->next;
  313. }
  314. }
  315. if ((0u == cmdExecuted) && (inst->inputCount > 0))
  316. {
  317. if (inst->echoEnabled)
  318. {
  319. inst->write("\r\nCommand: ", 11u);
  320. inst->write(inst->buffer, inst->inputCount);
  321. inst->write(" not found", 10u);
  322. }
  323. else
  324. {
  325. inst->write("\r\nCommand not found!", 20u);
  326. }
  327. }
  328. /** -# terminate this session if no continuous mode is requested */
  329. if( (NULL == inst->continuousCmd)
  330. && (NULL == inst->busyCmd))
  331. {
  332. utils_terminateInput(inst);
  333. }
  334. }
  335. }
  336. /** -# ignore newline as first character (to be compatible to
  337. * terminals sending newline after return */
  338. else if((0u == inst->inputCount) && ('\n' == data[inst->byteCounter]))
  339. {
  340. /* do nothing */
  341. }
  342. /** -# check for tabulator key - auto complete */
  343. else if('\t' == data[inst->byteCounter])
  344. {
  345. inst->dirty = true;
  346. autocomplete_run(inst);
  347. }
  348. /** -# check for cancel -
  349. * terminate current input and print prompt again */
  350. else if('\x03' == data[inst->byteCounter])
  351. {
  352. inst->dirty = false;
  353. history_reset(inst);
  354. utils_terminateInput(inst);
  355. }
  356. /** -# check for backspace */
  357. else if( ('\b' == data[inst->byteCounter])
  358. || ('\x7f' == data[inst->byteCounter]))
  359. {
  360. inst->dirty = true;
  361. utils_removeChars(inst, 1u, true);
  362. }
  363. /** -# check for start of escape sequence */
  364. else if('\x1b' == data[inst->byteCounter])
  365. {
  366. inst->escapeCounter = 1u;
  367. }
  368. else
  369. {
  370. inst->dirty = true;
  371. utils_insertChars(inst, &data[inst->byteCounter], 1u);
  372. }
  373. /** -# reset tab counter on not a tab */
  374. if ('\t' != data[inst->byteCounter])
  375. {
  376. inst->tabCounter = 0u;
  377. }
  378. }
  379. /** -# initialize the byte buffer if processing of the input is finished */
  380. if(ret != SHELLMATTA_BUSY)
  381. {
  382. inst->byteCounter = 0u;
  383. }
  384. return ret;
  385. }
  386. /**
  387. * @}
  388. * @addtogroup shellmatta_api
  389. * @{
  390. */
  391. /**
  392. * @brief initialize the shellmatta terminal and provide all callbacks
  393. * @param[in,out] inst pointer to a shellmatta instance
  394. * @param[out] handle pointer to shellmatta handle -
  395. * has to be used for all furterh api calls
  396. * @param[in] buffer pointer to the input buffer to use
  397. * @param[in] bufferSize size of the provided input buffer
  398. * @param[in] historyBuffer pointer to the history buffer to use
  399. * NULL in case of no history buffer
  400. * @param[in] historyBufferSize size of the history buffer
  401. * 0 in case of no history buffer
  402. * @param[in] prompt pointer to prompt string - is printed
  403. * after each command
  404. * @param[in] cmdList constant command list if no dynamic
  405. * adding of commands is desired
  406. * @param[in] writeFct function pointer to output function
  407. */
  408. shellmatta_retCode_t shellmatta_doInit(
  409. shellmatta_instance_t *inst,
  410. shellmatta_handle_t *handle,
  411. char *buffer,
  412. uint32_t bufferSize,
  413. char *historyBuffer,
  414. uint32_t historyBufferSize,
  415. const char *prompt,
  416. const shellmatta_cmd_t *cmdList,
  417. shellmatta_write_t writeFct)
  418. {
  419. /** -# check parameters for plausibility */
  420. if( (NULL != inst)
  421. && (NULL != handle)
  422. && (NULL != buffer)
  423. && (0u != bufferSize)
  424. && (NULL != prompt)
  425. && (NULL != writeFct)
  426. && ((NULL != historyBuffer) || (0u == historyBufferSize)))
  427. {
  428. /** -# clear the shellmatta instance */
  429. memset((void *)inst, 0, sizeof(shellmatta_instance_t));
  430. /** -# copy all provided buffers into the shellmatta instance */
  431. inst->buffer = buffer;
  432. inst->bufferSize = bufferSize;
  433. inst->historyBuffer = historyBuffer;
  434. inst->historyBufferSize = historyBufferSize;
  435. inst->historyReadUp = true;
  436. inst->write = writeFct;
  437. inst->prompt = prompt;
  438. inst->echoEnabled = true;
  439. inst->dirty = false;
  440. inst->delimiter = '\r';
  441. inst->mode = SHELLMATTA_MODE_INSERT;
  442. inst->cmdList = &(inst->helpCmd);
  443. inst->ymodemState = INACTIVE;
  444. shellmatta_opt_init(inst, 0u);
  445. /** -# copy the help command structure to this instance */
  446. memcpy(&(inst->helpCmd), &helpCmd, sizeof(shellmatta_cmd_t));
  447. #ifdef SHELLMATTA_AUTHENTICATION
  448. /** -# copy the auth commands to the instance */
  449. memcpy(&(inst->loginCmd), &shellmatta_auth_loginCmd, sizeof(shellmatta_cmd_t));
  450. memcpy(&(inst->logoutCmd), &shellmatta_auth_logoutCmd, sizeof(shellmatta_cmd_t));
  451. #endif
  452. if(NULL != cmdList)
  453. {
  454. #ifndef SHELLMATTA_AUTHENTICATION
  455. inst->helpCmd.next = (shellmatta_cmd_t *) cmdList;
  456. #else
  457. inst->logoutCmd.next = (shellmatta_cmd_t *) cmdList;
  458. #endif
  459. inst->cmdListIsConst = true;
  460. }
  461. inst->magic = SHELLMATTA_MAGIC;
  462. *handle = (shellmatta_handle_t)inst;
  463. #ifdef SHELLMATTA_TRANSPORT
  464. /* init transport layer */
  465. inst->transportEnabled = true;
  466. inst->transportBusyMark = 0u;
  467. shellmatta_transport_init(&inst->transportLayer, inst->write);
  468. #endif
  469. /** -# print the first prompt */
  470. utils_terminateInput(inst);
  471. }
  472. return SHELLMATTA_OK;
  473. }
  474. /**
  475. * @brief resets the whole shellmatta instance
  476. * @param[in] handle shellmatta instance handle
  477. * @param[in] printPrompt print a new command prompt
  478. *
  479. * This function can be used e.g. when working with connection based interfaces (e.g. sockets) to clear
  480. * the shell from old content when a new connection is opened.
  481. * It resets all internal states - the buffers are left as they are - they will be overwritten.
  482. * The history buffer is deleted as well.
  483. */
  484. shellmatta_retCode_t shellmatta_resetShell(shellmatta_handle_t handle, bool printPrompt)
  485. {
  486. shellmatta_instance_t *inst = (shellmatta_instance_t *)handle;
  487. shellmatta_retCode_t ret = SHELLMATTA_OK;
  488. /** -# check if the instance is plausible */
  489. if( (NULL != handle)
  490. && (SHELLMATTA_MAGIC == inst->magic))
  491. {
  492. inst->inputCount = 0u;
  493. inst->byteCounter = 0u;
  494. inst->continuousCmd = NULL;
  495. inst->busyCmd = NULL;
  496. inst->lastNewlineIdx = 0u;
  497. inst->cursor = 0u;
  498. inst->stdinIdx = 0u;
  499. inst->stdinLength = 0u;
  500. inst->historyStart = 0u;
  501. inst->historyEnd = 0u;
  502. inst->historyRead = 0u;
  503. inst->historyReadUp = true;
  504. inst->dirty = false;
  505. inst->tabCounter = 0u;
  506. inst->escapeCounter = 0u;
  507. inst->hereStartIdx = 0u;
  508. inst->hereDelimiterIdx = 0u;
  509. inst->hereLength = 0u;
  510. inst->ymodemState = INACTIVE;
  511. shellmatta_opt_init(inst, 0u);
  512. #ifdef SHELLMATTA_AUTHENTICATION
  513. inst->userId = 0u;
  514. inst->userPointer = NULL;
  515. #endif
  516. if(true == printPrompt)
  517. {
  518. /** -# print a prompt if requested */
  519. utils_terminateInput(inst);
  520. }
  521. }
  522. else
  523. {
  524. ret = SHELLMATTA_USE_FAULT;
  525. }
  526. return ret;
  527. }
  528. /**
  529. * @brief adds a command to the command list alphabetically ordered
  530. * @param[in] handle shellmatta instance handle
  531. * @param[in] cmd pointer to the command to add type #shellmatta_cmd_t
  532. * @return errorcode #SHELLMATTA_OK
  533. * #SHELLMATTA_USE_FAULT (param err)
  534. * SHELLMATTA_DUPLICATE
  535. *
  536. * The cmd name is mandatory, the rest of the command parameters (alias, helpText and usageText) are optional
  537. * and can be set to NULL if not used.
  538. */
  539. shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cmd_t *cmd)
  540. {
  541. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  542. shellmatta_cmd_t *tempCmd;
  543. shellmatta_cmd_t **prevCmd;
  544. bool cmdPlaced = false;
  545. shellmatta_retCode_t ret = SHELLMATTA_OK;
  546. int cmdDiff;
  547. int aliasDiff;
  548. /** -# check parameters for plausibility */
  549. if( (NULL != inst)
  550. && (SHELLMATTA_MAGIC == inst->magic)
  551. && (false == inst->cmdListIsConst)
  552. && (NULL != cmd)
  553. && (NULL != cmd->cmd))
  554. {
  555. tempCmd = inst->cmdList;
  556. prevCmd = &inst->cmdList;
  557. /** -# register first command as list entry */
  558. if (NULL == tempCmd)
  559. {
  560. inst->cmdList = cmd;
  561. cmd->next = NULL;
  562. }
  563. /** -# append the new command sorted */
  564. else
  565. {
  566. while ((false == cmdPlaced) && (SHELLMATTA_OK == ret))
  567. {
  568. cmdDiff = strcmp(tempCmd->cmd, cmd->cmd);
  569. if( (NULL != cmd->cmdAlias)
  570. && (NULL != tempCmd->cmdAlias))
  571. {
  572. aliasDiff = strcmp(tempCmd->cmdAlias, cmd->cmdAlias);
  573. }
  574. else
  575. {
  576. aliasDiff = 1;
  577. }
  578. /** -# check for a duplicate command */
  579. if((0u == cmdDiff) || (0u == aliasDiff))
  580. {
  581. ret = SHELLMATTA_DUPLICATE;
  582. }
  583. else if(cmdDiff > 0)
  584. {
  585. cmd->next = tempCmd;
  586. *prevCmd = cmd;
  587. cmdPlaced = true;
  588. }
  589. else if(NULL == tempCmd->next)
  590. {
  591. tempCmd->next = cmd;
  592. cmd->next = NULL;
  593. cmdPlaced = true;
  594. }
  595. else
  596. {
  597. /* nothing to do */
  598. }
  599. prevCmd = &(tempCmd->next);
  600. tempCmd = tempCmd->next;
  601. }
  602. }
  603. #ifdef SHELLMATTA_AUTHENTICATION
  604. /** -# append permissions to added command if any */
  605. if (inst->permList)
  606. {
  607. /** -# Search for command in perm list */
  608. for (uint32_t i = 0u; i < inst->permListLength; i++)
  609. {
  610. if (0 == strcmp(cmd->cmd, inst->permList[i].cmd))
  611. {
  612. cmd->authLink = &inst->permList[i];
  613. break;
  614. }
  615. }
  616. }
  617. #endif
  618. }
  619. else
  620. {
  621. ret = SHELLMATTA_USE_FAULT;
  622. }
  623. return ret;
  624. }
  625. /**
  626. * @brief removes a command from the command list
  627. * @param[in] handle shellmatta instance handle
  628. * @param[in] cmd pointer to the command to remove type #shellmatta_cmd_t
  629. * @return errorcode #SHELLMATTA_OK
  630. * #SHELLMATTA_USE_FAULT (param err)
  631. */
  632. shellmatta_retCode_t shellmatta_removeCmd(shellmatta_handle_t handle, const shellmatta_cmd_t *cmd)
  633. {
  634. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  635. shellmatta_cmd_t *prevCmd;
  636. shellmatta_cmd_t *tempCmd;
  637. shellmatta_retCode_t ret = SHELLMATTA_OK;
  638. /** -# check parameters for plausibility */
  639. if( (NULL != inst)
  640. && (SHELLMATTA_MAGIC == inst->magic)
  641. && (false == inst->cmdListIsConst)
  642. && (NULL != cmd)
  643. && (NULL != cmd->cmd))
  644. {
  645. tempCmd = inst->cmdList;
  646. prevCmd = NULL;
  647. /** -# loop through command list */
  648. while(NULL != tempCmd)
  649. {
  650. /** -# compare command strings to find the command to delete */
  651. if (0 == strcmp(tempCmd->cmd, cmd->cmd)
  652. && (strlen(tempCmd->cmd) == strlen(cmd->cmd)))
  653. {
  654. /** -# first command removed */
  655. if(NULL == prevCmd)
  656. {
  657. inst->cmdList = tempCmd->next;
  658. }
  659. /** -# last command removed */
  660. else if(NULL == tempCmd->next)
  661. {
  662. prevCmd->next = NULL;
  663. }
  664. /** -# command removed from the middle of the list */
  665. else
  666. {
  667. prevCmd->next = tempCmd->next;
  668. }
  669. break;
  670. }
  671. prevCmd = tempCmd;
  672. tempCmd = tempCmd->next;
  673. }
  674. }
  675. else
  676. {
  677. ret = SHELLMATTA_USE_FAULT;
  678. }
  679. return ret;
  680. }
  681. /**
  682. * @brief changes configuration of a shellmatta instance
  683. * @param[in] handle shellmatta instance handle
  684. * @param[in] mode insert mode of the shellmatta type #shellmatta_mode_t
  685. * @param[in] echoEnabled true: echo received chars to the output
  686. * @param[in] delimiter delimiter used to detect the end of a cmd (default "\r")
  687. * @return errorcode #SHELLMATTA_OK
  688. * #SHELLMATTA_USE_FAULT (param err)
  689. */
  690. shellmatta_retCode_t shellmatta_configure( shellmatta_handle_t handle,
  691. shellmatta_mode_t mode,
  692. bool echoEnabled,
  693. char delimiter)
  694. {
  695. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  696. shellmatta_retCode_t ret = SHELLMATTA_OK;
  697. /** -# check parameters for plausibility */
  698. if( (NULL != inst)
  699. && (SHELLMATTA_MAGIC == inst->magic)
  700. && ((SHELLMATTA_MODE_INSERT == mode) || (SHELLMATTA_MODE_OVERWRITE == mode)))
  701. {
  702. inst->mode = mode;
  703. inst->echoEnabled = echoEnabled;
  704. inst->delimiter = delimiter;
  705. }
  706. else
  707. {
  708. ret = SHELLMATTA_USE_FAULT;
  709. }
  710. return ret;
  711. }
  712. /**
  713. * @brief processes the passed amount of data
  714. * @param[in] handle shellmatta instance handle
  715. * @param[in] data pointer to input data to process
  716. * @param[in] size length of input data to process
  717. * @return errorcode #SHELLMATTA_OK
  718. * #SHELLMATTA_USE_FAULT
  719. */
  720. shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
  721. char *data,
  722. uint32_t size)
  723. {
  724. shellmatta_retCode_t ret = SHELLMATTA_OK;
  725. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  726. #ifdef SHELLMATTA_TRANSPORT
  727. char *tmpData;
  728. uint32_t tmpSize = 0;
  729. uint32_t i;
  730. bool processingDone = false;
  731. #endif
  732. /** -# check parameters for plausibility */
  733. if( (NULL != inst)
  734. && (SHELLMATTA_MAGIC == inst->magic))
  735. {
  736. #ifdef SHELLMATTA_TRANSPORT
  737. if (inst->transportEnabled)
  738. {
  739. for (i = inst->transportBusyMark; i < size; i ++)
  740. {
  741. ret = shellmatta_transport_process(&inst->transportLayer, data[i], &tmpData, &tmpSize);
  742. if (SHELLMATTA_OK == ret)
  743. {
  744. ret = shellmatta_processDataInt(handle, tmpData, tmpSize);
  745. processingDone = true;
  746. if (SHELLMATTA_BUSY == ret)
  747. {
  748. inst->transportBusyMark = i;
  749. break;
  750. }
  751. else
  752. {
  753. inst->transportBusyMark = 0u;
  754. }
  755. }
  756. else if (SHELLMATTA_ERROR == ret)
  757. {
  758. utils_writeEcho(inst, "crc error\r\n", 11);
  759. utils_terminateInput(inst);
  760. }
  761. else
  762. {
  763. /* nothing to do - transport layer busy */
  764. }
  765. }
  766. /*! -# call the internal processing at least once - for continued and busy commands */
  767. if (true != processingDone)
  768. {
  769. ret = shellmatta_processDataInt(handle, tmpData, 0);
  770. }
  771. if (false == inst->transportLayer.disableAutoFlush)
  772. {
  773. (void)shellmatta_transport_flush(handle);
  774. }
  775. }
  776. else
  777. {
  778. ret = shellmatta_processDataInt(handle, data, size);
  779. }
  780. #else
  781. ret = shellmatta_processDataInt(handle, data, size);
  782. #endif
  783. }
  784. else
  785. {
  786. ret = SHELLMATTA_USE_FAULT;
  787. }
  788. return ret;
  789. }
  790. /**
  791. * @brief simple write function to write a datastream to the output of an instance
  792. * @param[in] handle shellmatta instance handle
  793. * @param[in] data data to be written
  794. * @param[in] length amount of data to be written
  795. * @return
  796. */
  797. shellmatta_retCode_t shellmatta_write( shellmatta_handle_t handle,
  798. char *data,
  799. uint32_t length)
  800. {
  801. shellmatta_retCode_t ret = SHELLMATTA_USE_FAULT;
  802. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  803. /** -# check parameters for plausibility */
  804. if( (NULL != inst)
  805. && (SHELLMATTA_MAGIC == inst->magic))
  806. {
  807. /** -# pass the data to the write function of the instance */
  808. ret = SHELLMATTA_WRITE(data, length);
  809. }
  810. return ret;
  811. }
  812. /**
  813. * @brief reads the stdin like buffer
  814. * @param[in] handle shellmatta instance handle
  815. * @param[out] data stdin data or NULL
  816. * @param[out] length size of the stdin data
  817. * @return
  818. */
  819. shellmatta_retCode_t shellmatta_read( shellmatta_handle_t handle,
  820. char **data,
  821. uint32_t *length)
  822. {
  823. shellmatta_retCode_t ret = SHELLMATTA_USE_FAULT;
  824. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  825. /** -# check parameters for plausibility */
  826. if( (NULL != inst)
  827. && (SHELLMATTA_MAGIC == inst->magic)
  828. && (NULL != data)
  829. && (NULL != length))
  830. {
  831. /** -# return a pointer to the data or NULL */
  832. if(0u == inst->stdinLength)
  833. {
  834. *data = NULL;
  835. }
  836. else
  837. {
  838. *data = &(inst->buffer[inst->stdinIdx]);
  839. }
  840. *length = inst->stdinLength;
  841. }
  842. return ret;
  843. }
  844. #ifndef SHELLMATTA_STRIP_PRINTF
  845. /**
  846. * @brief printf like function to print output to the instances output
  847. * @param[in] handle shellmatta instance handle
  848. * @param[in] fmt format string and parameters
  849. * @return errorcode #SHELLMATTA_OK
  850. * #SHELLMATTA_USE_FAULT (no valid instance)
  851. * #SHELLMATTA_ERROR (buffer overflow or format err)
  852. */
  853. shellmatta_retCode_t shellmatta_printf( shellmatta_handle_t handle,
  854. const char *fmt,
  855. ...)
  856. {
  857. char outputBuffer[SHELLMATTA_OUTPUT_BUFFER_SIZE];
  858. va_list arg;
  859. int length;
  860. shellmatta_retCode_t ret = SHELLMATTA_OK;
  861. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  862. /** -# check parameters for plausibility */
  863. if( (NULL != inst)
  864. && (SHELLMATTA_MAGIC == inst->magic))
  865. {
  866. /** -# assemble output string and write it */
  867. va_start(arg, fmt);
  868. length = vsnprintf(outputBuffer, SHELLMATTA_OUTPUT_BUFFER_SIZE, fmt, arg);
  869. va_end(arg);
  870. if(length < 0)
  871. {
  872. ret = SHELLMATTA_ERROR;
  873. }
  874. else
  875. {
  876. SHELLMATTA_WRITE(outputBuffer, length);
  877. }
  878. }
  879. else
  880. {
  881. ret = SHELLMATTA_USE_FAULT;
  882. }
  883. return ret;
  884. }
  885. #endif
  886. /**
  887. * @brief starts the ymodem receive module
  888. * @param[in] handle shellmatta instance handle
  889. * @param[in] buffer pointer to where the received data is stored
  890. * @param[in] fileSize pointer to where the file size is stored
  891. * @param[in] packetSize pointer to where the packet size is stored
  892. * @param[in] callbacks callback functions for ymodem module
  893. * @note disables the transport layer if active
  894. */
  895. uint8_t shellmatta_ymodem( shellmatta_handle_t handle,
  896. uint8_t* buffer,
  897. uint32_t* fileSize,
  898. uint16_t* packetSize,
  899. shellmatta_ymodem_callbacks_t callbacks)
  900. {
  901. #ifdef SHELLMATTA_TRANSPORT
  902. /* disable transport layer so control symbols won't be caught by it */
  903. ((shellmatta_instance_t*)handle)->transportEnabled = false;
  904. #endif
  905. shellmatta_ymodem_init(handle, buffer, fileSize, packetSize, callbacks);
  906. /* send initial ymodem symbol to start transmission */
  907. shellmatta_ymodem_control(handle, YMODEM_CRC);
  908. return 0;
  909. }
  910. /**
  911. * @brief Resets the ymodem module
  912. * @param[in] doCancel Set this flag to execute the cancel-callback function within the ymodem-reset function
  913. * @note call this function after file transmission is done or cancelled
  914. */
  915. void shellmatta_ymodem_end(shellmatta_handle_t handle, bool doCancel)
  916. {
  917. shellmatta_ymodem_reset(handle, doCancel);
  918. /* clear any possibly leftover inputs */
  919. utils_clearInput((shellmatta_instance_t*)handle);
  920. }
  921. void shellmatta_ymodem_set_enable(bool doEnable)
  922. {
  923. if (doEnable)
  924. {
  925. shellmatta_ymodem_enable();
  926. }
  927. else
  928. {
  929. shellmatta_ymodem_disable();
  930. }
  931. }
  932. bool shellmatta_ymodem_is_active(void)
  933. {
  934. return shellmatta_ymodem_get_current_datatype() != YMODEM_NONE;
  935. }
  936. /** @} */