Blob


1 /*
2 * Copyright (c) 2021, 2024 Omar Polo <op@omarpolo.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include "compat.h"
19 #include <stdlib.h>
20 #include <string.h>
22 #include "hist.h"
24 struct hist {
25 TAILQ_HEAD(mhist, hist_item) head;
26 int flags;
27 size_t size;
28 ssize_t off;
29 struct hist_item *cur;
30 };
32 struct hist_item {
33 char *str;
34 size_t line_off;
35 size_t current_off;
36 TAILQ_ENTRY(hist_item) entries;
37 };
39 struct hist *
40 hist_new(int flags)
41 {
42 struct hist *hist;
44 if ((hist = calloc(1, sizeof(*hist))) == NULL)
45 return (NULL);
47 TAILQ_INIT(&hist->head);
48 hist->flags = flags;
49 hist->off = -1;
50 return (hist);
51 }
53 void
54 hist_free(struct hist *hist)
55 {
56 if (hist == NULL)
57 return;
59 hist_erase(hist);
60 free(hist);
61 }
63 static void
64 hist_erase_from(struct hist *hist, struct hist_item *h)
65 {
66 struct hist_item *next;
68 while (h != NULL) {
69 next = TAILQ_NEXT(h, entries);
71 hist->size--;
72 TAILQ_REMOVE(&hist->head, h, entries);
73 free(h->str);
74 free(h);
75 h = next;
76 }
77 }
79 void
80 hist_erase(struct hist *hist)
81 {
82 hist_erase_from(hist, TAILQ_FIRST(&hist->head));
84 hist->size = 0;
85 hist->off = -1;
86 hist->cur = NULL;
87 }
89 size_t
90 hist_size(struct hist *hist)
91 {
92 return (hist->size);
93 }
95 size_t
96 hist_off(struct hist *hist)
97 {
98 if (hist->off == -1)
99 return (0);
100 return (hist->off);
103 const char *
104 hist_cur(struct hist *hist)
106 if (hist->cur == NULL)
107 return (NULL);
108 return (hist->cur->str);
111 int
112 hist_cur_offs(struct hist *hist, size_t *line, size_t *curr)
114 *line = 0;
115 *curr = 0;
117 if (hist->cur == NULL)
118 return (-1);
120 *line = hist->cur->line_off;
121 *curr = hist->cur->current_off;
122 return (0);
125 int
126 hist_set_cur(struct hist *hist, const char *str)
128 char *d;
130 if (hist->cur == NULL)
131 return (-1);
133 if ((d = strdup(str)) == NULL)
134 return (-1);
136 free(hist->cur->str);
137 hist->cur->str = d;
138 return (0);
141 int
142 hist_set_offs(struct hist *hist, size_t line, size_t curr)
144 if (hist->cur == NULL)
145 return (-1);
147 hist->cur->line_off = line;
148 hist->cur->current_off = line;
149 return (0);
152 const char *
153 hist_nth(struct hist *hist, size_t n)
155 size_t i;
156 struct hist_item *h;
158 i = 0;
159 TAILQ_FOREACH(h, &hist->head, entries) {
160 if (i++ == n)
161 return (h->str);
163 return (NULL);
166 const char *
167 hist_prev(struct hist *hist)
169 struct hist_item *h;
170 int wrap = hist->flags & HIST_WRAP;
172 if (hist->cur == NULL && !wrap)
173 return (NULL);
175 if (hist->cur == NULL ||
176 (h = TAILQ_PREV(hist->cur, mhist, entries)) == NULL) {
177 if (!wrap || (h = TAILQ_LAST(&hist->head, mhist)) == NULL)
178 return (NULL);
179 hist->off = hist->size - 1;
180 } else
181 hist->off--;
183 hist->cur = h;
184 return (h->str);
187 const char *
188 hist_next(struct hist *hist)
190 struct hist_item *h;
191 int wrap = hist->flags & HIST_WRAP;
193 if (hist->cur == NULL && !wrap)
194 return (NULL);
196 if (hist->cur == NULL ||
197 (h = TAILQ_NEXT(hist->cur, entries)) == NULL) {
198 if (!wrap || (h = TAILQ_FIRST(&hist->head)) == NULL)
199 return (NULL);
200 hist->off = 0;
201 } else
202 hist->off++;
204 hist->cur = h;
205 return (h->str);
208 void
209 hist_seek_start(struct hist *hist)
211 hist->off = -1;
212 hist->cur = NULL;
215 int
216 hist_push(struct hist *hist, const char *str)
218 struct hist_item *h;
220 if ((h = calloc(1, sizeof(*h))) == NULL)
221 return (-1);
223 if ((h->str = strdup(str)) == NULL) {
224 free(h);
225 return (-1);
228 if (hist->cur != NULL)
229 hist_erase_from(hist, TAILQ_NEXT(hist->cur, entries));
230 hist->cur = h;
231 hist->off++;
232 hist->size++;
233 TAILQ_INSERT_TAIL(&hist->head, h, entries);
234 return (0);
237 int
238 hist_prepend(struct hist *hist, const char *str)
240 struct hist_item *h;
242 if (hist->cur == NULL)
243 return (-1);
245 if ((h = calloc(1, sizeof(*h))) == NULL)
246 return (-1);
248 if ((h->str = strdup(str)) == NULL) {
249 free(h);
250 return (-1);
253 hist->size++;
254 hist->off++;
255 TAILQ_INSERT_BEFORE(hist->cur, h, entries);
256 return (0);
259 int
260 hist_append(struct hist *hist, const char *str)
262 struct hist_item *h;
264 /*
265 * Not sure. The minibuffer needs to append even when there
266 * are no items.
267 */
268 if (hist->cur == NULL && !(hist->flags & HIST_WRAP))
269 return (-1);
271 if ((h = calloc(1, sizeof(*h))) == NULL)
272 return (-1);
274 if ((h->str = strdup(str)) == NULL) {
275 free(h);
276 return (-1);
279 hist->size++;
280 TAILQ_INSERT_TAIL(&hist->head, h, entries);
281 return (0);