Blob


1 /*
2 * Copyright (c) 2022 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 <stdint.h>
21 #include <string.h>
23 #include "telescope.h"
24 #include "mcache.h"
25 #include "parser.h"
26 #include "utils.h"
28 const char *gemtext_prefixes[] = {
29 [LINE_TEXT] = "",
30 [LINE_TITLE_1] = "# ",
31 [LINE_TITLE_2] = "## ",
32 [LINE_TITLE_3] = "### ",
33 [LINE_ITEM] = "* ",
34 [LINE_QUOTE] = "> ",
35 [LINE_PRE_START] = "``` ",
36 [LINE_PRE_CONTENT] = "",
37 [LINE_PRE_END] = "```",
38 };
40 static struct timeval tv = { 60, 0 };
41 static struct event timerev;
43 struct mcache {
44 struct ohash h;
45 size_t npages;
46 size_t tot;
47 } mcache;
49 struct mcache_entry {
50 time_t ts;
51 const char *parser_name;
52 int trust;
53 struct evbuffer *evb;
54 char url[];
55 };
57 static void
58 mcache_free_entry(const char *url)
59 {
60 struct mcache_entry *e;
61 unsigned int slot;
63 slot = ohash_qlookup(&mcache.h, url);
64 if ((e = ohash_remove(&mcache.h, slot)) == NULL)
65 return;
67 mcache.npages--;
68 mcache.tot -= EVBUFFER_LENGTH(e->evb);
70 evbuffer_free(e->evb);
71 free(e);
72 }
74 static void
75 clean_old_entries(int fd, short ev, void *data)
76 {
77 struct mcache_entry *e;
78 unsigned int i;
79 time_t treshold;
81 treshold = time(NULL) - 15 * 60;
83 for (e = ohash_first(&mcache.h, &i); e != NULL; e = ohash_next(&mcache.h, &i))
84 if (e->ts < treshold)
85 mcache_free_entry(e->url);
87 evtimer_add(&timerev, &tv);
88 }
90 void
91 mcache_init(void)
92 {
93 struct ohash_info info = {
94 .key_offset = offsetof(struct mcache_entry, url),
95 .calloc = hash_calloc,
96 .free = hash_free,
97 .alloc = hash_alloc,
98 };
100 ohash_init(&mcache.h, 5, &info);
102 evtimer_set(&timerev, clean_old_entries, NULL);
105 int
106 mcache_tab(struct tab *tab)
108 struct mcache_entry *e;
109 struct line *line;
110 unsigned int slot;
111 size_t l, len;
112 const char *url;
114 url = tab->hist_cur->h;
115 l = strlen(url);
116 len = sizeof(*e) + l + 1;
118 if ((e = calloc(1, len)) == NULL)
119 return -1;
120 e->ts = time(NULL);
121 e->parser_name = tab->buffer.page.name;
122 e->trust = tab->trust;
123 memcpy(e->url, url, l);
125 if ((e->evb = evbuffer_new()) == NULL)
126 goto err;
128 TAILQ_FOREACH(line, &tab->buffer.page.head, lines) {
129 const char *text, *alt;
130 int r;
132 if ((text = line->line) == NULL)
133 text = "";
135 if ((alt = line->alt) == NULL)
136 alt = "";
138 switch (line->type) {
139 case LINE_TEXT:
140 case LINE_TITLE_1:
141 case LINE_TITLE_2:
142 case LINE_TITLE_3:
143 case LINE_ITEM:
144 case LINE_QUOTE:
145 case LINE_PRE_START:
146 case LINE_PRE_CONTENT:
147 case LINE_PRE_END:
148 r = evbuffer_add_printf(e->evb, "%s%s\n",
149 gemtext_prefixes[line->type], text);
150 break;
152 case LINE_LINK:
153 r = evbuffer_add_printf(e->evb, "=> %s %s\n",
154 alt, text);
155 break;
157 case LINE_PATCH:
158 case LINE_PATCH_HDR:
159 case LINE_PATCH_HUNK_HDR:
160 case LINE_PATCH_ADD:
161 case LINE_PATCH_DEL:
162 /* TODO */
163 r = -1;
164 break;
166 case LINE_COMPL:
167 case LINE_COMPL_CURRENT:
168 case LINE_HELP:
169 case LINE_DOWNLOAD:
170 case LINE_DOWNLOAD_DONE:
171 case LINE_DOWNLOAD_INFO:
172 case LINE_FRINGE:
173 /* not reached */
174 abort();
177 if (r == -1)
178 goto err;
181 /* free any previously cached copies of this page */
182 mcache_free_entry(url);
184 slot = ohash_qlookup(&mcache.h, url);
185 ohash_insert(&mcache.h, slot, e);
187 mcache.npages++;
188 mcache.tot += EVBUFFER_LENGTH(e->evb);
190 if (!evtimer_pending(&timerev, NULL))
191 evtimer_add(&timerev, &tv);
193 return 0;
195 err:
196 if (e->evb != NULL)
197 evbuffer_free(e->evb);
198 free(e);
199 return -1;
202 int
203 mcache_lookup(const char *url, struct tab *tab)
205 struct mcache_entry *e;
206 unsigned int slot;
208 slot = ohash_qlookup(&mcache.h, url);
209 if ((e = ohash_find(&mcache.h, slot)) == NULL)
210 return 0;
212 parser_init(tab, gemtext_initparser);
213 if (!parser_parse(tab, EVBUFFER_DATA(e->evb), EVBUFFER_LENGTH(e->evb)))
214 goto err;
215 if (!parser_free(tab))
216 goto err;
218 tab->buffer.page.name = e->parser_name;
219 tab->trust = e->trust;
220 return 1;
222 err:
223 parser_free(tab);
224 erase_buffer(&tab->buffer);
225 return 0;
228 void
229 mcache_info(size_t *npages, size_t *tot)
231 *npages = mcache.npages;
232 *tot = mcache.tot;