💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Vgmi › files › b6ddd3ada928e0733579eb8de2062… captured on 2024-02-05 at 09:57:28. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
0 /*
1 * ISC License
2 * Copyright (c) 2023 RMF <rawmonk@firemail.cc>
3 */
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <string.h>
8 #include <wchar.h>
9 #include <pthread.h>
10 #include "macro.h"
11 #include "strlcpy.h"
12 #include "error.h"
13 #include "config.h"
14 #include "utf8.h"
15 #include "url.h"
16 #include "storage.h"
17 #define HISTORY_INTERNAL
18 #include "history.h"
19 #define PARSER_INTERNAL
20 #include "parser.h"
21
22 #define HISTORY_DISABLED (!config.enableHistory || !config.maximumHistorySize)
23 #define HISTORY "history.txt"
24 #define HISTORY_RELOAD 400 /* reload history after [x] requests */
25 pthread_mutex_t history_mutex = PTHREAD_MUTEX_INITIALIZER;
26
27 struct history_entry *history = NULL;
28 int history_length = 0;
29
30 int history_load(const char *path) {
31
32 struct history_entry entry = {0};
33 struct history_entry *last = NULL;
34 FILE *f;
35 unsigned int i, part, count;
36
37 if (!(f = storage_fopen(path, "r"))) return ERROR_STORAGE_ACCESS;
38
39 count = i = part = 0;
40 for (;;) {
41 uint32_t ch;
42 char *ptr;
43 if (utf8_fgetc(f, &ch)) break;
44 if (!renderable(ch)) continue;
45 if (ch == '\n') {
46 struct history_entry *new;
47 i = 0;
48 part = 0;
49 new = malloc(sizeof(*new));
50 if (!new) {
51 fclose(f);
52 return ERROR_MEMORY_FAILURE;
53 }
54 *new = entry;
55 if (last) {
56 last->next = new;
57 } else {
58 new->next = NULL;
59 history = new;
60 }
61 last = new;
62 memset(&entry, 0, sizeof(entry));
63 count++;
64 if (count >= (unsigned)config.maximumHistorySize)
65 break;
66 continue;
67 }
68 if (!part && ch <= ' ') {
69 part = 1;
70 i = 0;
71 continue;
72 }
73 if (part == 1) {
74 if (ch <= ' ') continue;
75 part = 2;
76 }
77 if (i >= (part ? sizeof(entry.title) : sizeof(entry.url))) {
78 continue;
79 }
80 ptr = part ? entry.title : entry.url;
81 i += utf8_unicode_to_char(&ptr[i], ch);
82 }
83 fclose(f);
84 history_length = count;
85 return 0;
86 }
87
88 void history_init(void) {
89 if (HISTORY_DISABLED) return;
90 history_load(HISTORY);
91 }
92
93 int history_write(const char *path) {
94
95 FILE *f;
96 struct history_entry *entry;
97 int count = 0;
98
99 if (HISTORY_DISABLED) return 0;
100 f = storage_fopen(path, "w");
101 if (!f) return -1;
102 for (entry = history; entry; entry = entry->next) {
103 if (count++ >= config.maximumHistorySize) break;
104 fprintf(f, "%s ", entry->url);
105 utf8_fprintf(f, V(entry->title));
106 fprintf(f, "\n");
107 }
108 fclose(f);
109
110 return 0;
111 }
112
113 int history_clear(void) {
114 history_free();
115 history = NULL;
116 history_length = 0;
117 return history_save();
118 }
119
120 int history_save(void) {
121 return history_write(HISTORY);
122 }
123
124 int history_add(const char *url, const char *title) {
125
126 struct history_entry *entry;
127 int limit;
128
129 if (HISTORY_DISABLED) return 0;
130 limit = config.maximumHistorySize + config.maximumHistoryCache;
131 if (history_length > limit) {
132 /* reload history to prevents overloading memory */
133 history_save();
134 history_free();
135 history_load(HISTORY);
136 }
137 history_length++;
138 entry = malloc(sizeof(*entry));
139 if (!entry) return ERROR_MEMORY_FAILURE;
140 url_hide_query(title, V(entry->title));
141 if (strstr(url, "gemini://") == url) {
142 url_hide_query(url, V(entry->url));
143 } else {
144 UTF8CPY(entry->title, title);
145 }
146 pthread_mutex_lock(&history_mutex);
147 entry->next = history;
148 history = entry;
149 pthread_mutex_unlock(&history_mutex);
150 return 0;
151 }
152
153 void history_free(void) {
154 struct history_entry *next;
155 while (history) {
156 next = history->next;
157 free(history);
158 history = next;
159 }
160 }
161