shellmatta.c 31 KB

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