history.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. /*
  2. * history.c
  3. *
  4. * Simple non-readline hooks for the cli library
  5. */
  6. #include <string.h>
  7. #include <stdio.h>
  8. #include <assert.h>
  9. #include "private.h"
  10. #include "lub/string.h"
  11. #include <stdlib.h>
  12. #include "tinyrl/history.h"
  13. struct _tinyrl_history {
  14. tinyrl_history_entry_t **entries; /* pointer entries */
  15. unsigned length; /* Number of elements within this array */
  16. unsigned size; /* Number of slots allocated in this array */
  17. unsigned current_index;
  18. unsigned stifle;
  19. };
  20. /*------------------------------------- */
  21. void tinyrl_history_init(tinyrl_history_t * this, unsigned stifle)
  22. {
  23. this->entries = NULL;
  24. this->stifle = stifle;
  25. this->current_index = 1;
  26. this->length = 0;
  27. this->size = 0;
  28. }
  29. /*------------------------------------- */
  30. void tinyrl_history_fini(tinyrl_history_t * this)
  31. {
  32. tinyrl_history_entry_t *entry;
  33. tinyrl_history_iterator_t iter;
  34. /* release the resource associated with each entry */
  35. for (entry = tinyrl_history_getfirst(this, &iter);
  36. entry; entry = tinyrl_history_getnext(&iter)) {
  37. tinyrl_history_entry_delete(entry);
  38. }
  39. /* release the list */
  40. free(this->entries);
  41. this->entries = NULL;
  42. }
  43. /*------------------------------------- */
  44. tinyrl_history_t *tinyrl_history_new(unsigned stifle)
  45. {
  46. tinyrl_history_t *this = malloc(sizeof(tinyrl_history_t));
  47. if (NULL != this) {
  48. tinyrl_history_init(this, stifle);
  49. }
  50. return this;
  51. }
  52. /*------------------------------------- */
  53. void tinyrl_history_delete(tinyrl_history_t * this)
  54. {
  55. tinyrl_history_fini(this);
  56. free(this);
  57. }
  58. /*
  59. HISTORY LIST MANAGEMENT
  60. */
  61. /*------------------------------------- */
  62. /* insert a new entry at the current offset */
  63. static void insert_entry(tinyrl_history_t * this, const char *line)
  64. {
  65. tinyrl_history_entry_t *new_entry =
  66. tinyrl_history_entry_new(line, this->current_index++);
  67. assert(this->length);
  68. assert(this->entries);
  69. if (new_entry) {
  70. this->entries[this->length - 1] = new_entry;
  71. }
  72. }
  73. /*------------------------------------- */
  74. /*
  75. * This frees the specified entries from the
  76. * entries vector. NB it doesn't perform any shuffling.
  77. * This function is inclusive of start and end
  78. */
  79. static void
  80. free_entries(const tinyrl_history_t * this, unsigned start, unsigned end)
  81. {
  82. unsigned i;
  83. assert(start <= end);
  84. assert(end < this->length);
  85. for (i = start; i <= end; i++) {
  86. tinyrl_history_entry_t *entry = this->entries[i];
  87. tinyrl_history_entry_delete(entry);
  88. entry = NULL;
  89. }
  90. }
  91. /*------------------------------------- */
  92. /*
  93. * This removes the specified entries from the
  94. * entries vector. Shuffling up the array as necessary
  95. * This function is inclusive of start and end
  96. */
  97. static void
  98. remove_entries(tinyrl_history_t * this, unsigned start, unsigned end)
  99. {
  100. unsigned delta = (end - start) + 1; /* number of entries being deleted */
  101. /* number of entries to shuffle */
  102. unsigned num_entries = (this->length - end) - 1;
  103. assert(start <= end);
  104. assert(end < this->length);
  105. if (num_entries) {
  106. /* move the remaining entries down to close the array */
  107. memmove(&this->entries[start],
  108. &this->entries[end + 1],
  109. sizeof(tinyrl_history_entry_t *) * num_entries);
  110. }
  111. /* now fix up the length variables */
  112. this->length -= delta;
  113. }
  114. /*------------------------------------- */
  115. /*
  116. Search the current history buffer for the specified
  117. line and if found remove it.
  118. */
  119. static bool_t remove_duplicate(tinyrl_history_t * this, const char *line)
  120. {
  121. bool_t result = BOOL_FALSE;
  122. unsigned i;
  123. for (i = 0; i < this->length; i++) {
  124. tinyrl_history_entry_t *entry = this->entries[i];
  125. if (0 == strcmp(line, tinyrl_history_entry__get_line(entry))) {
  126. free_entries(this, i, i);
  127. remove_entries(this, i, i);
  128. result = BOOL_TRUE;
  129. break;
  130. }
  131. }
  132. return result;
  133. }
  134. /*------------------------------------- */
  135. /*
  136. add an entry to the end of the current array
  137. if there is no space returns -1 else 0
  138. */
  139. static void append_entry(tinyrl_history_t * this, const char *line)
  140. {
  141. if (this->length < this->size) {
  142. this->length++;
  143. insert_entry(this, line);
  144. }
  145. }
  146. /*------------------------------------- */
  147. /*
  148. add a new history entry replacing the oldest one
  149. */
  150. static void add_n_replace(tinyrl_history_t * this, const char *line)
  151. {
  152. if (BOOL_FALSE == remove_duplicate(this, line)) {
  153. /* free the oldest entry */
  154. free_entries(this, 0, 0);
  155. /* shuffle the array */
  156. remove_entries(this, 0, 0);
  157. }
  158. /* add the new entry */
  159. append_entry(this, line);
  160. }
  161. /*------------------------------------- */
  162. /* add a new history entry growing the array if necessary */
  163. static void add_n_grow(tinyrl_history_t * this, const char *line)
  164. {
  165. if (this->size == this->length) {
  166. /* increment the history memory by 10 entries each time we grow */
  167. unsigned new_size = this->size + 10;
  168. size_t nbytes;
  169. tinyrl_history_entry_t **new_entries;
  170. nbytes = sizeof(tinyrl_history_entry_t *) * new_size;
  171. new_entries = realloc(this->entries, nbytes);
  172. if (NULL != new_entries) {
  173. this->size = new_size;
  174. this->entries = new_entries;
  175. }
  176. }
  177. (void)remove_duplicate(this, line);
  178. append_entry(this, line);
  179. }
  180. /*------------------------------------- */
  181. void tinyrl_history_add(tinyrl_history_t * this, const char *line)
  182. {
  183. if (this->length && (this->length == this->stifle)) {
  184. add_n_replace(this, line);
  185. } else {
  186. add_n_grow(this, line);
  187. }
  188. }
  189. /*------------------------------------- */
  190. tinyrl_history_entry_t *tinyrl_history_remove(tinyrl_history_t * this,
  191. unsigned offset)
  192. {
  193. tinyrl_history_entry_t *result = NULL;
  194. if (offset < this->length) {
  195. result = this->entries[offset];
  196. /* do the biz */
  197. remove_entries(this, offset, offset);
  198. }
  199. return result;
  200. }
  201. /*------------------------------------- */
  202. void tinyrl_history_clear(tinyrl_history_t * this)
  203. {
  204. /* free all the entries */
  205. free_entries(this, 0, this->length - 1);
  206. /* and shuffle the array */
  207. remove_entries(this, 0, this->length - 1);
  208. }
  209. /*------------------------------------- */
  210. void tinyrl_history_stifle(tinyrl_history_t * this, unsigned stifle)
  211. {
  212. /*
  213. * if we are stifling (i.e. non zero value) then
  214. * delete the obsolete entries
  215. */
  216. if (stifle) {
  217. if (stifle < this->length) {
  218. unsigned num_deletes = this->length - stifle;
  219. /* free the entries */
  220. free_entries(this, 0, num_deletes - 1);
  221. /* shuffle the array shut */
  222. remove_entries(this, 0, num_deletes - 1);
  223. }
  224. this->stifle = stifle;
  225. }
  226. }
  227. /*------------------------------------- */
  228. unsigned tinyrl_history_unstifle(tinyrl_history_t * this)
  229. {
  230. unsigned result = this->stifle;
  231. this->stifle = 0;
  232. return result;
  233. }
  234. /*------------------------------------- */
  235. bool_t tinyrl_history_is_stifled(const tinyrl_history_t * this)
  236. {
  237. return this->stifle ? BOOL_TRUE : BOOL_FALSE;
  238. }
  239. /*
  240. INFORMATION ABOUT THE HISTORY LIST
  241. */
  242. tinyrl_history_entry_t *tinyrl_history_get(const tinyrl_history_t * this,
  243. unsigned position)
  244. {
  245. unsigned i;
  246. tinyrl_history_entry_t *entry = NULL;
  247. for (i = 0; i < this->length; i++) {
  248. entry = this->entries[i];
  249. if (position == tinyrl_history_entry__get_index(entry)) {
  250. /* found it */
  251. break;
  252. }
  253. entry = NULL;
  254. }
  255. return entry;
  256. }
  257. /*------------------------------------- */
  258. tinyrl_history_expand_t
  259. tinyrl_history_expand(const tinyrl_history_t * this,
  260. const char *string, char **output)
  261. {
  262. tinyrl_history_expand_t result = tinyrl_history_NO_EXPANSION; /* no expansion */
  263. const char *p, *start;
  264. char *buffer = NULL;
  265. unsigned len;
  266. for (p = string, start = string, len = 0; *p; p++, len++) {
  267. /* perform pling substitution */
  268. if (*p == '!') {
  269. /* assume the last command to start with... */
  270. unsigned offset = this->current_index - 1;
  271. unsigned skip;
  272. tinyrl_history_entry_t *entry;
  273. /* this could be an escape sequence */
  274. if (p[1] != '!') {
  275. int tmp;
  276. int res;
  277. /* read the numeric identifier */
  278. res = sscanf(p, "!%d", &tmp);
  279. if ((0 == res) || (EOF == res)) {
  280. /* error so ignore it */
  281. break;
  282. }
  283. if (tmp < 0) {
  284. /* this is a relative reference */
  285. /*lint -e737 Loss of sign in promotion from int to unsigend int */
  286. offset += tmp; /* adding a negative substracts... */
  287. /*lint +e737 */
  288. } else {
  289. /* this is an absolute reference */
  290. offset = (unsigned)tmp;
  291. }
  292. }
  293. if (len > 0) {
  294. /* we need to add in some previous plain text */
  295. lub_string_catn(&buffer, start, len);
  296. }
  297. /* skip the escaped chars */
  298. p += skip = strspn(p, "!-0123456789");
  299. /* try and find the history entry */
  300. entry = tinyrl_history_get(this, offset);
  301. if (NULL != entry) {
  302. /* reset the non-escaped references */
  303. start = p;
  304. len = 0;
  305. /* add the expanded text to the buffer */
  306. result = tinyrl_history_EXPANDED;
  307. lub_string_cat(&buffer,
  308. tinyrl_history_entry__get_line
  309. (entry));
  310. } else {
  311. /* we simply leave the unexpanded sequence */
  312. len += skip;
  313. }
  314. }
  315. }
  316. /* add any left over plain text */
  317. lub_string_catn(&buffer, start, len);
  318. *output = buffer;
  319. return result;
  320. }
  321. /*-------------------------------------*/
  322. tinyrl_history_entry_t *tinyrl_history_getfirst(const tinyrl_history_t * this,
  323. tinyrl_history_iterator_t *
  324. iter)
  325. {
  326. tinyrl_history_entry_t *result = NULL;
  327. iter->history = this;
  328. iter->offset = 0;
  329. if (this->length) {
  330. result = this->entries[iter->offset];
  331. }
  332. return result;
  333. }
  334. /*-------------------------------------*/
  335. tinyrl_history_entry_t *tinyrl_history_getnext(tinyrl_history_iterator_t * iter)
  336. {
  337. tinyrl_history_entry_t *result = NULL;
  338. if (iter->offset < iter->history->length - 1) {
  339. iter->offset++;
  340. result = iter->history->entries[iter->offset];
  341. }
  342. return result;
  343. }
  344. /*-------------------------------------*/
  345. tinyrl_history_entry_t *tinyrl_history_getlast(const tinyrl_history_t * this,
  346. tinyrl_history_iterator_t * iter)
  347. {
  348. iter->history = this;
  349. iter->offset = this->length;
  350. return tinyrl_history_getprevious(iter);
  351. }
  352. /*-------------------------------------*/
  353. tinyrl_history_entry_t *tinyrl_history_getprevious(tinyrl_history_iterator_t *
  354. iter)
  355. {
  356. tinyrl_history_entry_t *result = NULL;
  357. if (iter->offset) {
  358. iter->offset--;
  359. result = iter->history->entries[iter->offset];
  360. }
  361. return result;
  362. }
  363. /*-------------------------------------*/