history.c 12 KB

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