dte test coverage


Directory: ./
File: src/history.c
Date: 2024-12-21 16:03:22
Exec Total Coverage
Lines: 71 84 84.5%
Functions: 6 7 85.7%
Branches: 35 42 83.3%

Line Branch Exec Source
1 #include <errno.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "history.h"
5 #include "error.h"
6 #include "util/arith.h"
7 #include "util/debug.h"
8 #include "util/readfile.h"
9 #include "util/str-util.h"
10 #include "util/xmalloc.h"
11 #include "util/xstdio.h"
12
13 12023 void history_append(History *history, const char *text)
14 {
15 12023 BUG_ON(history->max_entries < 2);
16
2/2
✓ Branch 0 taken 12021 times.
✓ Branch 1 taken 2 times.
12023 if (text[0] == '\0') {
17 return;
18 }
19
20 12021 HashMap *map = &history->entries;
21 12021 HistoryEntry *e = hashmap_get(map, text);
22
23
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 12016 times.
12021 if (e) {
24
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 if (e == history->last) {
25 // Existing entry already at end of list; nothing more to do
26 return;
27 }
28 // Remove existing entry from list
29 4 e->next->prev = e->prev;
30
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (unlikely(e == history->first)) {
31 2 history->first = e->next;
32 } else {
33 2 e->prev->next = e->next;
34 }
35 } else {
36
2/2
✓ Branch 0 taken 11491 times.
✓ Branch 1 taken 525 times.
12016 if (map->count == history->max_entries) {
37 // History is full; recycle oldest entry
38 11491 HistoryEntry *old_first = history->first;
39 11491 HistoryEntry *new_first = old_first->next;
40 11491 new_first->prev = NULL;
41 11491 history->first = new_first;
42 11491 e = hashmap_remove(map, old_first->text);
43 11491 BUG_ON(e != old_first);
44 } else {
45 525 e = xnew(HistoryEntry, 1);
46 }
47 12016 e->text = xstrdup(text);
48 12016 hashmap_insert(map, e->text, e);
49 }
50
51 // Insert entry at end of list
52 12020 HistoryEntry *old_last = history->last;
53 12020 e->next = NULL;
54 12020 e->prev = old_last;
55 12020 history->last = e;
56
2/2
✓ Branch 0 taken 12015 times.
✓ Branch 1 taken 5 times.
12020 if (likely(old_last)) {
57 12015 old_last->next = e;
58 } else {
59 5 history->first = e;
60 }
61 }
62
63 4 bool history_search_forward (
64 const History *history,
65 const HistoryEntry **pos,
66 const char *text
67 ) {
68
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 const HistoryEntry *start = *pos ? (*pos)->prev : history->last;
69 4 const bool always_match = (text[0] == '\0');
70
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 for (const HistoryEntry *e = start; e; e = e->prev) {
71
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
4 if (always_match || str_has_prefix(e->text, text)) {
72 3 *pos = e;
73 3 return true;
74 }
75 }
76 return false;
77 }
78
79 4 bool history_search_backward (
80 const History *history,
81 const HistoryEntry **pos,
82 const char *text
83 ) {
84
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 const HistoryEntry *start = *pos ? (*pos)->next : history->first;
85 4 const bool always_match = (text[0] == '\0');
86
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 for (const HistoryEntry *e = start; e; e = e->next) {
87
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
4 if (always_match || str_has_prefix(e->text, text)) {
88 3 *pos = e;
89 3 return true;
90 }
91 }
92 return false;
93 }
94
95 11 void history_load(History *history, char *filename, size_t size_limit)
96 {
97 11 BUG_ON(history->filename);
98 11 BUG_ON(history->max_entries < 2);
99 11 hashmap_init(&history->entries, history->max_entries);
100 11 history->filename = filename;
101
102 11 char *buf;
103 11 const ssize_t ssize = read_file(filename, &buf, size_limit);
104
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
11 if (ssize < 0) {
105
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (errno != ENOENT) {
106 error_msg("Error reading %s: %s", filename, strerror(errno));
107 }
108 10 return;
109 }
110
111
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 for (size_t pos = 0, size = ssize; pos < size; ) {
112 5 history_append(history, buf_next_line(buf, &pos, size));
113 }
114
115 1 free(buf);
116 }
117
118 1 void history_save(const History *history)
119 {
120 1 const char *filename = history->filename;
121
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!filename) {
122 return;
123 }
124
125 1 FILE *f = xfopen(filename, "w", O_CLOEXEC, 0666);
126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!f) {
127 error_msg("Error creating %s: %s", filename, strerror(errno));
128 return;
129 }
130
131
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 for (const HistoryEntry *e = history->first; e; e = e->next) {
132 3 xfputs(e->text, f);
133 3 xfputc('\n', f);
134 }
135
136 1 fclose(f);
137 }
138
139 20 void history_free(History *history)
140 {
141 20 hashmap_free(&history->entries, free);
142 20 free(history->filename);
143 20 history->filename = NULL;
144 20 history->first = NULL;
145 20 history->last = NULL;
146 20 }
147
148 String history_dump(const History *history)
149 {
150 const size_t nr_entries = history->entries.count;
151 const size_t size = round_size_to_next_multiple(16 * nr_entries, 4096);
152 String buf = string_new(size);
153 size_t n = 0;
154 for (const HistoryEntry *e = history->first; e; e = e->next, n++) {
155 string_append_cstring(&buf, e->text);
156 string_append_byte(&buf, '\n');
157 }
158 BUG_ON(n != nr_entries);
159 return buf;
160 }
161