diff options
Diffstat (limited to 'examples/dte/file-history.c')
| -rw-r--r-- | examples/dte/file-history.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/examples/dte/file-history.c b/examples/dte/file-history.c new file mode 100644 index 0000000..8066b94 --- /dev/null +++ b/examples/dte/file-history.c @@ -0,0 +1,153 @@ +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include "file-history.h" +#include "error.h" +#include "util/debug.h" +#include "util/readfile.h" +#include "util/str-util.h" +#include "util/string-view.h" +#include "util/strtonum.h" +#include "util/xmalloc.h" +#include "util/xstdio.h" + +enum { + MAX_ENTRIES = 512 +}; + +void file_history_add(FileHistory *history, unsigned long row, unsigned long col, const char *filename) +{ + BUG_ON(row == 0); + BUG_ON(col == 0); + HashMap *map = &history->entries; + FileHistoryEntry *e = hashmap_get(map, filename); + + if (e) { + if (e == history->last) { + e->row = row; + e->col = col; + return; + } + e->next->prev = e->prev; + if (unlikely(e == history->first)) { + history->first = e->next; + } else { + e->prev->next = e->next; + } + } else { + if (map->count == MAX_ENTRIES) { + // History is full; recycle the oldest entry + FileHistoryEntry *old_first = history->first; + FileHistoryEntry *new_first = old_first->next; + new_first->prev = NULL; + history->first = new_first; + e = hashmap_remove(map, old_first->filename); + BUG_ON(e != old_first); + } else { + e = xnew(FileHistoryEntry, 1); + } + e->filename = xstrdup(filename); + hashmap_insert(map, e->filename, e); + } + + // Insert the entry at the end of the list + FileHistoryEntry *old_last = history->last; + e->next = NULL; + e->prev = old_last; + e->row = row; + e->col = col; + history->last = e; + if (likely(old_last)) { + old_last->next = e; + } else { + history->first = e; + } +} + +static bool parse_ulong_field(StringView *sv, unsigned long *valp) +{ + size_t n = buf_parse_ulong(sv->data, sv->length, valp); + if (n == 0 || *valp == 0 || sv->data[n] != ' ') { + return false; + } + strview_remove_prefix(sv, n + 1); + return true; +} + +void file_history_load(FileHistory *history, char *filename) +{ + BUG_ON(!history); + BUG_ON(!filename); + BUG_ON(history->filename); + + hashmap_init(&history->entries, MAX_ENTRIES); + history->filename = filename; + + char *buf; + const ssize_t ssize = read_file(filename, &buf); + if (ssize < 0) { + if (errno != ENOENT) { + error_msg("Error reading %s: %s", filename, strerror(errno)); + } + return; + } + + for (size_t pos = 0, size = ssize; pos < size; ) { + unsigned long row, col; + StringView line = buf_slice_next_line(buf, &pos, size); + if (unlikely( + !parse_ulong_field(&line, &row) + || !parse_ulong_field(&line, &col) + || line.length < 2 + || line.data[0] != '/' + || buf[pos - 1] != '\n' + )) { + continue; + } + buf[pos - 1] = '\0'; // null-terminate line, by replacing '\n' with '\0' + file_history_add(history, row, col, line.data); + } + + free(buf); +} + +void file_history_save(const FileHistory *history) +{ + const char *filename = history->filename; + if (!filename) { + return; + } + + FILE *f = xfopen(filename, "w", O_CLOEXEC, 0666); + if (!f) { + error_msg("Error creating %s: %s", filename, strerror(errno)); + return; + } + + for (const FileHistoryEntry *e = history->first; e; e = e->next) { + xfprintf(f, "%lu %lu %s\n", e->row, e->col, e->filename); + } + + fclose(f); +} + +bool file_history_find(const FileHistory *history, const char *filename, unsigned long *row, unsigned long *col) +{ + const FileHistoryEntry *e = hashmap_get(&history->entries, filename); + if (!e) { + return false; + } + *row = e->row; + *col = e->col; + return true; +} + +void file_history_free(FileHistory *history) +{ + hashmap_free(&history->entries, free); + free(history->filename); + history->filename = NULL; + history->first = NULL; + history->last = NULL; +} |
