shellmatta_history.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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_history.c
  10. * @brief history buffer functions of shellmatta
  11. * @author Stefan Strobel <stefan.strobel@shimatta.net>
  12. */
  13. /**
  14. * @addtogroup shellmatta_history
  15. * @{
  16. */
  17. #include "shellmatta_history.h"
  18. #include "shellmatta.h"
  19. #include "shellmatta_utils.h"
  20. /**
  21. * @brief appends a byte to the history ring stack buffer
  22. * @param[in] inst pointer to a shellmatta instance
  23. * @param[in] byte byte to append to the history buffer
  24. */
  25. static void appendHistoryByte(shellmatta_instance_t *inst, char byte)
  26. {
  27. /** -# calculate the new history buffer index */
  28. inst->historyEnd ++;
  29. if(inst->historyEnd >= inst->historyBufferSize)
  30. {
  31. inst->historyEnd = 0u;
  32. }
  33. /** -# append the byte */
  34. inst->historyBuffer[inst->historyEnd] = byte;
  35. /** -# check if we overwrite an existing stored command */
  36. if(inst->historyEnd == inst->historyStart)
  37. {
  38. /** -# move the start pointer to the next termination (0) */
  39. do
  40. {
  41. inst->historyStart ++;
  42. if(inst->historyStart >= inst->historyBufferSize)
  43. {
  44. inst->historyStart = 0u;
  45. }
  46. }while(0u != inst->historyBuffer[inst->historyStart]);
  47. }
  48. }
  49. /**
  50. * @brief reads a byte from the history buffer and decreases the read index
  51. * @param[in] inst pointer to a shellmatta instance
  52. * @param[out] byte pointer to a char where the read out byte will be stored
  53. * @return false: no new byte to read
  54. */
  55. static bool getHistoryByte(shellmatta_instance_t *inst, char *byte)
  56. {
  57. bool ret = false;
  58. /** -# check if we have reached the end of the buffer already */
  59. if(inst->historyRead != inst->historyStart)
  60. {
  61. /** -# read out one byte and decrease the read index */
  62. *byte = inst->historyBuffer[inst->historyRead];
  63. if(0u == inst->historyRead)
  64. {
  65. inst->historyRead = inst->historyBufferSize;
  66. }
  67. inst->historyRead --;
  68. ret = true;
  69. }
  70. return ret;
  71. }
  72. /**
  73. * @brief compares the current buffer to the last command in the history buffer
  74. * @param[in] inst pointer to a shellmatta instance
  75. * @return true: current command is identical to the last one in the history buffer
  76. */
  77. static bool compareLastCommand(shellmatta_instance_t *inst)
  78. {
  79. bool ret = false;
  80. uint32_t i;
  81. uint32_t cnt;
  82. /** -# check if there is anything in the buffer */
  83. if(inst->historyStart != inst->historyEnd)
  84. {
  85. i = inst->historyEnd;
  86. cnt = 0u;
  87. ret = true;
  88. while((true == ret) && (cnt < inst->inputCount))
  89. {
  90. /** -# terminate compare on first mismatch */
  91. if((inst->historyBuffer[i] != inst->buffer[cnt]) || (0u == inst->historyBuffer[i]))
  92. {
  93. ret = false;
  94. }
  95. if(0u == i)
  96. {
  97. i = (inst->historyBufferSize - 1u);
  98. }
  99. else
  100. {
  101. i --;
  102. }
  103. cnt ++;
  104. }
  105. /*! -# check if we are at the end of the command in the buffer - there has to be a terminating 0 */
  106. if(0u != inst->historyBuffer[i])
  107. {
  108. ret = false;
  109. }
  110. }
  111. return ret;
  112. }
  113. /**
  114. * @brief navigates in the history buffer by the given number of commands
  115. * @param[in, out] inst pointer to a shellmatta instance
  116. * @param[in] cnt direction and count to navigate
  117. * @return false: end of buffer reached
  118. */
  119. bool history_navigate(shellmatta_instance_t *inst, int32_t cnt)
  120. {
  121. bool ret = true;
  122. uint32_t tempReadIdx = 0u;
  123. while((cnt > 0) && (true == ret))
  124. {
  125. if(inst->historyRead != inst->historyEnd)
  126. {
  127. inst->historyRead ++;
  128. }
  129. while(inst->historyRead != inst->historyEnd)
  130. {
  131. inst->historyRead ++;
  132. if(inst->historyRead >= inst->historyBufferSize)
  133. {
  134. inst->historyRead -= inst->historyBufferSize;
  135. }
  136. if( (inst->historyRead != inst->historyEnd)
  137. && (0u == inst->historyBuffer[inst->historyRead]))
  138. {
  139. if(0u == inst->historyRead)
  140. {
  141. inst->historyRead = inst->historyBufferSize;
  142. }
  143. inst->historyRead --;
  144. cnt -= 1;
  145. break;
  146. }
  147. }
  148. if(inst->historyRead == inst->historyEnd)
  149. {
  150. ret = false;
  151. }
  152. }
  153. while((cnt < 0) && (true == ret))
  154. {
  155. tempReadIdx = inst->historyRead;
  156. while(inst->historyRead != inst->historyStart)
  157. {
  158. if(0u == inst->historyRead)
  159. {
  160. inst->historyRead = inst->historyBufferSize;
  161. }
  162. inst->historyRead --;
  163. if( (inst->historyRead != inst->historyStart)
  164. && (0u == inst->historyBuffer[inst->historyRead]))
  165. {
  166. if(0u == inst->historyRead)
  167. {
  168. inst->historyRead = inst->historyBufferSize;
  169. }
  170. inst->historyRead --;
  171. cnt += 1;
  172. break;
  173. }
  174. }
  175. if(inst->historyRead == inst->historyStart)
  176. {
  177. inst->historyRead = tempReadIdx;
  178. inst->historyReadUp = false;
  179. ret = false;
  180. }
  181. }
  182. return ret;
  183. }
  184. /**
  185. * @brief stores the current command from the instances buffer into the
  186. * history buffer
  187. * @param[in] inst pointer to a shellmatta instance
  188. */
  189. void history_storeCmd(shellmatta_instance_t *inst)
  190. {
  191. uint32_t i;
  192. /** -# check if we have enough room for the command in the history buffer
  193. * and there is a new command to be stored */
  194. if( (inst->historyBufferSize > inst->inputCount)
  195. && (0u != inst->inputCount)
  196. && (true == inst->dirty)
  197. && (true != compareLastCommand(inst)))
  198. {
  199. /** -# append the command termination */
  200. appendHistoryByte(inst, 0u);
  201. /** -# append the command byte wise in reverse direction */
  202. for(i = inst->inputCount; i > 0u; i --)
  203. {
  204. appendHistoryByte(inst, inst->buffer[i - 1u]);
  205. }
  206. }
  207. inst->dirty = false;
  208. }
  209. /**
  210. * @brief restores the command from the history buffer where the read
  211. * index points on
  212. * @param[in] inst pointer to a shellmatta instance
  213. */
  214. void history_restoreCmd(shellmatta_instance_t *inst)
  215. {
  216. char byte;
  217. bool ret = true;
  218. bool anythingToRestore = false;
  219. ret = getHistoryByte(inst, &byte);
  220. /** -# delete the input if there is data in the history buffer */
  221. if(true == ret)
  222. {
  223. utils_clearInput(inst);
  224. anythingToRestore = true;
  225. }
  226. while((ret == true) && (byte != 0u))
  227. {
  228. inst->buffer[inst->inputCount] = byte;
  229. inst->inputCount ++;
  230. inst->cursor ++;
  231. ret = getHistoryByte(inst, &byte);
  232. }
  233. if(true == anythingToRestore)
  234. {
  235. utils_writeEcho(inst, inst->buffer, inst->inputCount);
  236. inst->dirty = false;
  237. }
  238. (void)history_navigate(inst, 1);
  239. }
  240. /**
  241. * @brief resets the history buffer pointers to show to the most recent
  242. * command again
  243. * @param[in] inst pointer to a shellmatta instance
  244. */
  245. void history_reset(shellmatta_instance_t *inst)
  246. {
  247. inst->historyRead = inst->historyEnd;
  248. inst->historyReadUp = true;
  249. }
  250. /**
  251. * @}
  252. */