aboutsummaryrefslogtreecommitdiff
path: root/examples/dte/history.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/dte/history.c')
-rw-r--r--examples/dte/history.c146
1 files changed, 146 insertions, 0 deletions
diff --git a/examples/dte/history.c b/examples/dte/history.c
new file mode 100644
index 0000000..d03a6c2
--- /dev/null
+++ b/examples/dte/history.c
@@ -0,0 +1,146 @@
1#include <errno.h>
2#include <stdlib.h>
3#include <string.h>
4#include "history.h"
5#include "error.h"
6#include "util/debug.h"
7#include "util/readfile.h"
8#include "util/str-util.h"
9#include "util/xmalloc.h"
10#include "util/xstdio.h"
11
12void history_add(History *history, const char *text)
13{
14 BUG_ON(history->max_entries < 2);
15 if (text[0] == '\0') {
16 return;
17 }
18
19 HashMap *map = &history->entries;
20 HistoryEntry *e = hashmap_get(map, text);
21
22 if (e) {
23 if (e == history->last) {
24 // Existing entry already at end of list; nothing more to do
25 return;
26 }
27 // Remove existing entry from list
28 e->next->prev = e->prev;
29 if (unlikely(e == history->first)) {
30 history->first = e->next;
31 } else {
32 e->prev->next = e->next;
33 }
34 } else {
35 if (map->count == history->max_entries) {
36 // History is full; recycle oldest entry
37 HistoryEntry *old_first = history->first;
38 HistoryEntry *new_first = old_first->next;
39 new_first->prev = NULL;
40 history->first = new_first;
41 e = hashmap_remove(map, old_first->text);
42 BUG_ON(e != old_first);
43 } else {
44 e = xnew(HistoryEntry, 1);
45 }
46 e->text = xstrdup(text);
47 hashmap_insert(map, e->text, e);
48 }
49
50 // Insert entry at end of list
51 HistoryEntry *old_last = history->last;
52 e->next = NULL;
53 e->prev = old_last;
54 history->last = e;
55 if (likely(old_last)) {
56 old_last->next = e;
57 } else {
58 history->first = e;
59 }
60}
61
62bool history_search_forward (
63 const History *history,
64 const HistoryEntry **pos,
65 const char *text
66) {
67 const HistoryEntry *start = *pos ? (*pos)->prev : history->last;
68 for (const HistoryEntry *e = start; e; e = e->prev) {
69 if (str_has_prefix(e->text, text)) {
70 *pos = e;
71 return true;
72 }
73 }
74 return false;
75}
76
77bool history_search_backward (
78 const History *history,
79 const HistoryEntry **pos,
80 const char *text
81) {
82 const HistoryEntry *start = *pos ? (*pos)->next : history->first;
83 for (const HistoryEntry *e = start; e; e = e->next) {
84 if (str_has_prefix(e->text, text)) {
85 *pos = e;
86 return true;
87 }
88 }
89 return false;
90}
91
92void history_load(History *history, char *filename)
93{
94 BUG_ON(!history);
95 BUG_ON(!filename);
96 BUG_ON(history->filename);
97 BUG_ON(history->max_entries < 2);
98
99 hashmap_init(&history->entries, history->max_entries);
100 history->filename = filename;
101
102 char *buf;
103 const ssize_t ssize = read_file(filename, &buf);
104 if (ssize < 0) {
105 if (errno != ENOENT) {
106 error_msg("Error reading %s: %s", filename, strerror(errno));
107 }
108 return;
109 }
110
111 for (size_t pos = 0, size = ssize; pos < size; ) {
112 history_add(history, buf_next_line(buf, &pos, size));
113 }
114
115 free(buf);
116}
117
118void history_save(const History *history)
119{
120 const char *filename = history->filename;
121 if (!filename) {
122 return;
123 }
124
125 FILE *f = xfopen(filename, "w", O_CLOEXEC, 0666);
126 if (!f) {
127 error_msg("Error creating %s: %s", filename, strerror(errno));
128 return;
129 }
130
131 for (const HistoryEntry *e = history->first; e; e = e->next) {
132 xfputs(e->text, f);
133 xfputc('\n', f);
134 }
135
136 fclose(f);
137}
138
139void history_free(History *history)
140{
141 hashmap_free(&history->entries, free);
142 free(history->filename);
143 history->filename = NULL;
144 history->first = NULL;
145 history->last = NULL;
146}