grafthistory: support curl
[elinks/elinks-j605.git] / src / globhist / globhist.c
blob39127c83702577e111eced0a672f8d9cf7ed965c
1 /* Global history */
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE /* XXX: we _WANT_ strcasestr() ! */
5 #endif
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
11 #include "elinks.h"
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #ifdef HAVE_TIME_H
17 #include <time.h>
18 #endif
20 #include "bfu/dialog.h"
21 #include "config/home.h"
22 #include "config/options.h"
23 #include "globhist/dialogs.h"
24 #include "globhist/globhist.h"
25 #include "intl/gettext/libintl.h"
26 #include "main/module.h"
27 #include "main/object.h"
28 #include "main/select.h"
29 #include "util/conv.h"
30 #include "util/file.h"
31 #include "util/hash.h"
32 #include "util/memory.h"
33 #include "util/secsave.h"
34 #include "util/string.h"
35 #include "util/lists.h"
36 #include "util/time.h"
38 #define GLOBAL_HISTORY_FILENAME "globhist"
41 INIT_INPUT_HISTORY(global_history);
44 /* GUI stuff. Declared here because done_global_history() frees it. */
45 unsigned char *gh_last_searched_title = NULL;
46 unsigned char *gh_last_searched_url = NULL;
48 enum global_history_options {
49 GLOBHIST_TREE,
51 GLOBHIST_ENABLE,
52 GLOBHIST_MAX_ITEMS,
53 GLOBHIST_DISPLAY_TYPE,
55 GLOBHIST_OPTIONS,
58 static struct option_info global_history_options[] = {
59 INIT_OPT_TREE("document.history", N_("Global history"),
60 "global", 0,
61 N_("Global history options.")),
63 INIT_OPT_BOOL("document.history.global", N_("Enable"),
64 "enable", 0, 1,
65 N_("Enable global history (\"history of all pages visited\").")),
67 INIT_OPT_INT("document.history.global", N_("Maximum number of entries"),
68 "max_items", 0, 1, INT_MAX, 1024,
69 N_("Maximum number of entries in the global history.")),
71 INIT_OPT_INT("document.history.global", N_("Display style"),
72 "display_type", 0, 0, 1, 0,
73 N_("What to display in global history dialog:\n"
74 "0 is URLs\n"
75 "1 is page titles")),
77 /* Compatibility alias: added by jonas at 2004-07-16, 0.9.CVS. */
78 INIT_OPT_ALIAS("document.history.global", "write_interval", 0,
79 "infofiles.save_interval"),
81 NULL_OPTION_INFO,
84 #define get_opt_globhist(which) global_history_options[(which)].option.value
85 #define get_globhist_enable() get_opt_globhist(GLOBHIST_ENABLE).number
86 #define get_globhist_max_items() get_opt_globhist(GLOBHIST_MAX_ITEMS).number
87 #define get_globhist_display_type() get_opt_globhist(GLOBHIST_DISPLAY_TYPE).number
89 static struct hash *globhist_cache = NULL;
90 static int globhist_cache_entries = 0;
93 static void
94 remove_item_from_global_history(struct global_history_item *history_item)
96 del_from_history_list(&global_history, history_item);
98 if (globhist_cache) {
99 struct hash_item *item;
101 item = get_hash_item(globhist_cache, history_item->url, strlen(history_item->url));
102 if (item) {
103 del_hash_item(globhist_cache, item);
104 globhist_cache_entries--;
109 static void
110 done_global_history_item(struct global_history_item *history_item)
112 done_listbox_item(&globhist_browser, history_item->box_item);
113 mem_free(history_item->title);
114 mem_free(history_item->url);
115 mem_free(history_item);
118 void
119 delete_global_history_item(struct global_history_item *history_item)
121 remove_item_from_global_history(history_item);
123 done_global_history_item(history_item);
126 /* Search global history for item matching url. */
127 struct global_history_item *
128 get_global_history_item(unsigned char *url)
130 struct hash_item *item;
132 if (!url || !globhist_cache) return NULL;
134 /* Search for cached entry. */
136 item = get_hash_item(globhist_cache, url, strlen(url));
138 return item ? (struct global_history_item *) item->value : NULL;
141 #if 0
142 /* Search global history for certain item. There must be full match with the
143 * parameter or the parameter must be NULL/zero. */
144 struct global_history_item *
145 multiget_global_history_item(unsigned char *url, unsigned char *title, time_t time)
147 struct global_history_item *history_item;
149 /* Code duplication vs performance, since this function is called most
150 * of time for url matching only... Execution time is divided by 2. */
151 if (url && !title && !time) {
152 return get_global_history_item(url);
153 } else {
154 foreach (history_item, global_history.items) {
155 if ((!url || !strcmp(history_item->url, url)) &&
156 (!title || !strcmp(history_item->title, title)) &&
157 (!time || history_item->last_visit == time)) {
158 return history_item;
163 return NULL;
165 #endif
167 static struct global_history_item *
168 init_global_history_item(unsigned char *url, unsigned char *title, time_t vtime)
170 struct global_history_item *history_item;
172 history_item = mem_calloc(1, sizeof(*history_item));
173 if (!history_item)
174 return NULL;
176 history_item->last_visit = vtime;
177 history_item->title = stracpy(empty_string_or_(title));
178 if (!history_item->title) {
179 mem_free(history_item);
180 return NULL;
182 sanitize_title(history_item->title);
184 history_item->url = stracpy(url);
185 if (!history_item->url || !sanitize_url(history_item->url)) {
186 mem_free_if(history_item->url);
187 mem_free(history_item->title);
188 mem_free(history_item);
189 return NULL;
192 history_item->box_item = add_listbox_leaf(&globhist_browser, NULL,
193 history_item);
194 if (!history_item->box_item) {
195 mem_free(history_item->url);
196 mem_free(history_item->title);
197 mem_free(history_item);
198 return NULL;
201 object_nolock(history_item, "globhist");
203 return history_item;
206 static int
207 cap_global_history(int max_globhist_items)
209 while (global_history.size >= max_globhist_items) {
210 struct global_history_item *history_item;
212 history_item = global_history.entries.prev;
214 if ((void *) history_item == &global_history.entries) {
215 INTERNAL("global history is empty");
216 global_history.size = 0;
217 return 0;
220 delete_global_history_item(history_item);
223 return 1;
226 static void
227 add_item_to_global_history(struct global_history_item *history_item,
228 int max_globhist_items)
230 add_to_history_list(&global_history, history_item);
232 /* Hash creation if needed. */
233 if (!globhist_cache)
234 globhist_cache = init_hash(8, &strhash);
236 if (globhist_cache && globhist_cache_entries < max_globhist_items) {
237 int urllen = strlen(history_item->url);
239 /* Create a new entry. */
240 if (add_hash_item(globhist_cache, history_item->url, urllen, history_item)) {
241 globhist_cache_entries++;
246 /* Add a new entry in history list, take care of duplicate, respect history
247 * size limit, and update any open history dialogs. */
248 void
249 add_global_history_item(unsigned char *url, unsigned char *title, time_t vtime)
251 struct global_history_item *history_item;
252 int max_globhist_items;
254 if (!url || !get_globhist_enable()) return;
256 max_globhist_items = get_globhist_max_items();
258 history_item = get_global_history_item(url);
259 if (history_item) delete_global_history_item(history_item);
261 if (!cap_global_history(max_globhist_items)) return;
263 history_item = init_global_history_item(url, title, vtime);
264 if (!history_item) return;
266 add_item_to_global_history(history_item, max_globhist_items);
271 globhist_simple_search(unsigned char *search_url, unsigned char *search_title)
273 struct global_history_item *history_item;
275 if (!search_title || !search_url)
276 return 0;
278 /* Memorize last searched title */
279 mem_free_set(&gh_last_searched_title, stracpy(search_title));
280 if (!gh_last_searched_title) return 0;
282 /* Memorize last searched url */
283 mem_free_set(&gh_last_searched_url, stracpy(search_url));
284 if (!gh_last_searched_url) {
285 mem_free(gh_last_searched_title);
286 return 0;
289 if (!*search_title && !*search_url) {
290 /* No search terms, make all entries visible. */
291 foreach (history_item, global_history.entries) {
292 history_item->box_item->visible = 1;
294 return 1;
297 foreach (history_item, global_history.entries) {
298 /* Make matching entries visible, hide others. */
299 if ((*search_title
300 && strcasestr(history_item->title, search_title))
301 || (*search_url
302 && strcasestr(history_item->url, search_url))) {
303 history_item->box_item->visible = 1;
304 } else {
305 history_item->box_item->visible = 0;
308 return 1;
312 static void
313 read_global_history(void)
315 unsigned char in_buffer[MAX_STR_LEN * 3];
316 unsigned char *file_name = GLOBAL_HISTORY_FILENAME;
317 unsigned char *title;
318 FILE *f;
320 if (!get_globhist_enable()
321 || get_cmd_opt_bool("anonymous"))
322 return;
324 if (elinks_home) {
325 file_name = straconcat(elinks_home, file_name, NULL);
326 if (!file_name) return;
328 f = fopen(file_name, "rb");
329 if (elinks_home) mem_free(file_name);
330 if (!f) return;
332 title = in_buffer;
333 global_history.nosave = 1;
335 while (fgets(in_buffer, sizeof(in_buffer), f)) {
336 unsigned char *url, *last_visit, *eol;
338 url = strchr(title, '\t');
339 if (!url) continue;
340 *url++ = '\0'; /* Now url points to the character after \t. */
342 last_visit = strchr(url, '\t');
343 if (!last_visit) continue;
344 *last_visit++ = '\0';
346 eol = strchr(last_visit, '\n');
347 if (!eol) continue;
348 *eol = '\0'; /* Drop ending '\n'. */
350 add_global_history_item(url, title, str_to_time_t(last_visit));
353 global_history.nosave = 0;
354 fclose(f);
357 static void
358 write_global_history(void)
360 struct global_history_item *history_item;
361 unsigned char *file_name;
362 struct secure_save_info *ssi;
364 if (!global_history.dirty || !elinks_home
365 || !get_globhist_enable()
366 || get_cmd_opt_bool("anonymous"))
367 return;
369 file_name = straconcat(elinks_home, GLOBAL_HISTORY_FILENAME, NULL);
370 if (!file_name) return;
372 ssi = secure_open(file_name);
373 mem_free(file_name);
374 if (!ssi) return;
376 foreachback (history_item, global_history.entries) {
377 if (secure_fprintf(ssi, "%s\t%s\t%ld\n",
378 history_item->title,
379 history_item->url,
380 history_item->last_visit) < 0) break;
383 if (!secure_close(ssi)) global_history.dirty = 0;
386 static void
387 free_global_history(void)
389 if (globhist_cache) {
390 free_hash(globhist_cache);
391 globhist_cache = NULL;
392 globhist_cache_entries = 0;
395 while (!list_empty(global_history.entries))
396 delete_global_history_item(global_history.entries.next);
399 static enum evhook_status
400 global_history_write_hook(va_list ap, void *data)
402 write_global_history();
403 return EVENT_HOOK_STATUS_NEXT;
406 struct event_hook_info global_history_hooks[] = {
407 { "periodic-saving", 0, global_history_write_hook, NULL },
409 NULL_EVENT_HOOK_INFO,
412 static void
413 init_global_history(struct module *module)
415 read_global_history();
418 static void
419 done_global_history(struct module *module)
421 write_global_history();
422 free_global_history();
423 mem_free_if(gh_last_searched_title);
424 mem_free_if(gh_last_searched_url);
427 struct module global_history_module = struct_module(
428 /* name: */ N_("Global History"),
429 /* options: */ global_history_options,
430 /* events: */ global_history_hooks,
431 /* submodules: */ NULL,
432 /* data: */ NULL,
433 /* init: */ init_global_history,
434 /* done: */ done_global_history