123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- /*
- * history.c
- *
- * Simple non-readline hooks for the cli library
- */
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #include <assert.h>
- #include <errno.h>
- #include "private.h"
- #include "lub/string.h"
- #include "tinyrl/history.h"
- struct _tinyrl_history {
- tinyrl_history_entry_t **entries; /* pointer entries */
- unsigned length; /* Number of elements within this array */
- unsigned size; /* Number of slots allocated in this array */
- unsigned current_index;
- unsigned stifle;
- };
- /*------------------------------------- */
- void tinyrl_history_init(tinyrl_history_t * this, unsigned stifle)
- {
- this->entries = NULL;
- this->stifle = stifle;
- this->current_index = 1;
- this->length = 0;
- this->size = 0;
- }
- /*------------------------------------- */
- void tinyrl_history_fini(tinyrl_history_t * this)
- {
- tinyrl_history_entry_t *entry;
- tinyrl_history_iterator_t iter;
- /* release the resource associated with each entry */
- for (entry = tinyrl_history_getfirst(this, &iter);
- entry; entry = tinyrl_history_getnext(&iter)) {
- tinyrl_history_entry_delete(entry);
- }
- /* release the list */
- free(this->entries);
- this->entries = NULL;
- }
- /*------------------------------------- */
- tinyrl_history_t *tinyrl_history_new(unsigned stifle)
- {
- tinyrl_history_t *this = malloc(sizeof(tinyrl_history_t));
- if (NULL != this) {
- tinyrl_history_init(this, stifle);
- }
- return this;
- }
- /*------------------------------------- */
- void tinyrl_history_delete(tinyrl_history_t * this)
- {
- tinyrl_history_fini(this);
- free(this);
- }
- /*
- HISTORY LIST MANAGEMENT
- */
- /*------------------------------------- */
- /* insert a new entry at the current offset */
- static void insert_entry(tinyrl_history_t * this, const char *line)
- {
- tinyrl_history_entry_t *new_entry =
- tinyrl_history_entry_new(line, this->current_index++);
- assert(this->length);
- assert(this->entries);
- if (new_entry) {
- this->entries[this->length - 1] = new_entry;
- }
- }
- /*------------------------------------- */
- /*
- * This frees the specified entries from the
- * entries vector. NB it doesn't perform any shuffling.
- * This function is inclusive of start and end
- */
- static void
- free_entries(const tinyrl_history_t * this, unsigned start, unsigned end)
- {
- unsigned i;
- assert(start <= end);
- assert(end < this->length);
- for (i = start; i <= end; i++) {
- tinyrl_history_entry_t *entry = this->entries[i];
- tinyrl_history_entry_delete(entry);
- entry = NULL;
- }
- }
- /*------------------------------------- */
- /*
- * This removes the specified entries from the
- * entries vector. Shuffling up the array as necessary
- * This function is inclusive of start and end
- */
- static void
- remove_entries(tinyrl_history_t * this, unsigned start, unsigned end)
- {
- unsigned delta = (end - start) + 1; /* number of entries being deleted */
- /* number of entries to shuffle */
- unsigned num_entries = (this->length - end) - 1;
- assert(start <= end);
- assert(end < this->length);
- if (num_entries) {
- /* move the remaining entries down to close the array */
- memmove(&this->entries[start],
- &this->entries[end + 1],
- sizeof(tinyrl_history_entry_t *) * num_entries);
- }
- /* now fix up the length variables */
- this->length -= delta;
- }
- /*------------------------------------- */
- /*
- Search the current history buffer for the specified
- line and if found remove it.
- */
- static bool_t remove_duplicate(tinyrl_history_t * this, const char *line)
- {
- bool_t result = BOOL_FALSE;
- unsigned i;
- for (i = 0; i < this->length; i++) {
- tinyrl_history_entry_t *entry = this->entries[i];
- if (0 == strcmp(line, tinyrl_history_entry__get_line(entry))) {
- free_entries(this, i, i);
- remove_entries(this, i, i);
- result = BOOL_TRUE;
- break;
- }
- }
- return result;
- }
- /*------------------------------------- */
- /*
- add an entry to the end of the current array
- if there is no space returns -1 else 0
- */
- static void append_entry(tinyrl_history_t * this, const char *line)
- {
- if (this->length < this->size) {
- this->length++;
- insert_entry(this, line);
- }
- }
- /*------------------------------------- */
- /*
- add a new history entry replacing the oldest one
- */
- static void add_n_replace(tinyrl_history_t * this, const char *line)
- {
- if (BOOL_FALSE == remove_duplicate(this, line)) {
- /* free the oldest entry */
- free_entries(this, 0, 0);
- /* shuffle the array */
- remove_entries(this, 0, 0);
- }
- /* add the new entry */
- append_entry(this, line);
- }
- /*------------------------------------- */
- /* add a new history entry growing the array if necessary */
- static void add_n_grow(tinyrl_history_t * this, const char *line)
- {
- if (this->size == this->length) {
- /* increment the history memory by 10 entries each time we grow */
- unsigned new_size = this->size + 10;
- size_t nbytes;
- tinyrl_history_entry_t **new_entries;
- nbytes = sizeof(tinyrl_history_entry_t *) * new_size;
- new_entries = realloc(this->entries, nbytes);
- if (NULL != new_entries) {
- this->size = new_size;
- this->entries = new_entries;
- }
- }
- (void)remove_duplicate(this, line);
- append_entry(this, line);
- }
- /*------------------------------------- */
- void tinyrl_history_add(tinyrl_history_t * this, const char *line)
- {
- if (this->length && (this->length == this->stifle)) {
- add_n_replace(this, line);
- } else {
- add_n_grow(this, line);
- }
- }
- /*------------------------------------- */
- tinyrl_history_entry_t *tinyrl_history_remove(tinyrl_history_t * this,
- unsigned offset)
- {
- tinyrl_history_entry_t *result = NULL;
- if (offset < this->length) {
- result = this->entries[offset];
- /* do the biz */
- remove_entries(this, offset, offset);
- }
- return result;
- }
- /*------------------------------------- */
- void tinyrl_history_clear(tinyrl_history_t * this)
- {
- /* free all the entries */
- free_entries(this, 0, this->length - 1);
- /* and shuffle the array */
- remove_entries(this, 0, this->length - 1);
- }
- /*------------------------------------- */
- void tinyrl_history_stifle(tinyrl_history_t * this, unsigned stifle)
- {
- /*
- * if we are stifling (i.e. non zero value) then
- * delete the obsolete entries
- */
- if (stifle) {
- if (stifle < this->length) {
- unsigned num_deletes = this->length - stifle;
- /* free the entries */
- free_entries(this, 0, num_deletes - 1);
- /* shuffle the array shut */
- remove_entries(this, 0, num_deletes - 1);
- }
- this->stifle = stifle;
- }
- }
- /*------------------------------------- */
- unsigned tinyrl_history_unstifle(tinyrl_history_t * this)
- {
- unsigned result = this->stifle;
- this->stifle = 0;
- return result;
- }
- /*------------------------------------- */
- bool_t tinyrl_history_is_stifled(const tinyrl_history_t * this)
- {
- return this->stifle ? BOOL_TRUE : BOOL_FALSE;
- }
- /*
- INFORMATION ABOUT THE HISTORY LIST
- */
- tinyrl_history_entry_t *tinyrl_history_get(const tinyrl_history_t * this,
- unsigned position)
- {
- unsigned i;
- tinyrl_history_entry_t *entry = NULL;
- for (i = 0; i < this->length; i++) {
- entry = this->entries[i];
- if (position == tinyrl_history_entry__get_index(entry)) {
- /* found it */
- break;
- }
- entry = NULL;
- }
- return entry;
- }
- /*------------------------------------- */
- tinyrl_history_expand_t
- tinyrl_history_expand(const tinyrl_history_t * this,
- const char *string, char **output)
- {
- tinyrl_history_expand_t result = tinyrl_history_NO_EXPANSION; /* no expansion */
- const char *p, *start;
- char *buffer = NULL;
- unsigned len;
- for (p = string, start = string, len = 0; *p; p++, len++) {
- /* perform pling substitution */
- if (*p == '!') {
- /* assume the last command to start with... */
- unsigned offset = this->current_index - 1;
- unsigned skip;
- tinyrl_history_entry_t *entry;
- /* this could be an escape sequence */
- if (p[1] != '!') {
- int tmp;
- int res;
- /* read the numeric identifier */
- res = sscanf(p, "!%d", &tmp);
- if ((0 == res) || (EOF == res)) {
- /* error so ignore it */
- break;
- }
- if (tmp < 0) {
- /* this is a relative reference */
- /*lint -e737 Loss of sign in promotion from int to unsigend int */
- offset += tmp; /* adding a negative substracts... */
- /*lint +e737 */
- } else {
- /* this is an absolute reference */
- offset = (unsigned)tmp;
- }
- }
- if (len > 0) {
- /* we need to add in some previous plain text */
- lub_string_catn(&buffer, start, len);
- }
- /* skip the escaped chars */
- p += skip = strspn(p, "!-0123456789");
- /* try and find the history entry */
- entry = tinyrl_history_get(this, offset);
- if (NULL != entry) {
- /* reset the non-escaped references */
- start = p;
- len = 0;
- /* add the expanded text to the buffer */
- result = tinyrl_history_EXPANDED;
- lub_string_cat(&buffer,
- tinyrl_history_entry__get_line
- (entry));
- } else {
- /* we simply leave the unexpanded sequence */
- len += skip;
- }
- }
- }
- /* add any left over plain text */
- lub_string_catn(&buffer, start, len);
- *output = buffer;
- return result;
- }
- /*-------------------------------------*/
- tinyrl_history_entry_t *tinyrl_history_getfirst(const tinyrl_history_t * this,
- tinyrl_history_iterator_t *
- iter)
- {
- tinyrl_history_entry_t *result = NULL;
- iter->history = this;
- iter->offset = 0;
- if (this->length) {
- result = this->entries[iter->offset];
- }
- return result;
- }
- /*-------------------------------------*/
- tinyrl_history_entry_t *tinyrl_history_getnext(tinyrl_history_iterator_t * iter)
- {
- tinyrl_history_entry_t *result = NULL;
- if (iter->offset < iter->history->length - 1) {
- iter->offset++;
- result = iter->history->entries[iter->offset];
- }
- return result;
- }
- /*-------------------------------------*/
- tinyrl_history_entry_t *tinyrl_history_getlast(const tinyrl_history_t * this,
- tinyrl_history_iterator_t * iter)
- {
- iter->history = this;
- iter->offset = this->length;
- return tinyrl_history_getprevious(iter);
- }
- /*-------------------------------------*/
- tinyrl_history_entry_t *tinyrl_history_getprevious(tinyrl_history_iterator_t *
- iter)
- {
- tinyrl_history_entry_t *result = NULL;
- if (iter->offset) {
- iter->offset--;
- result = iter->history->entries[iter->offset];
- }
- return result;
- }
- /*-------------------------------------*/
- /* Save command history to specified file */
- int tinyrl_history_save(const tinyrl_history_t *this, const char *fname)
- {
- tinyrl_history_entry_t *entry;
- tinyrl_history_iterator_t iter;
- FILE *f;
- if (!fname) {
- errno = EINVAL;
- return -1;
- }
- if (!(f = fopen(fname, "w")))
- return -1;
- for (entry = tinyrl_history_getfirst(this, &iter);
- entry; entry = tinyrl_history_getnext(&iter)) {
- if (fprintf(f, "%s\n", tinyrl_history_entry__get_line(entry)) < 0)
- return -1;
- }
- fclose(f);
- return 0;
- }
- /*-------------------------------------*/
- /* Restore command history from specified file */
- int tinyrl_history_restore(tinyrl_history_t *this, const char *fname)
- {
- FILE *f;
- char *p;
- int part_len = 300;
- char *buf;
- int buf_len = part_len;
- int res = 0;
- if (!fname) {
- errno = EINVAL;
- return -1;
- }
- if (!(f = fopen(fname, "r")))
- return 0; /* Can't find history file */
- buf = malloc(buf_len);
- p = buf;
- while (fgets(p, buf_len - (p - buf), f)) {
- char *ptmp = NULL;
- char *el = strchr(buf, '\n');
- if (el) { /* The whole line was readed */
- *el = '\0';
- tinyrl_history_add(this, buf);
- p = buf;
- continue;
- }
- buf_len += part_len;
- ptmp = realloc(buf, buf_len);
- if (!ptmp) {
- res = -1;
- goto end;
- }
- buf = ptmp;
- p = buf + buf_len - part_len - 1;
- }
- end:
- free(buf);
- fclose(f);
- return res;
- }
- /*-------------------------------------*/
|