commit 9e97090d644b51c0cb6e049e8c777139b18457fa from: Omar Polo date: Sat Feb 26 17:45:38 2022 UTC persist list of visited URLs to the disk Persist a generalized history of visited URL in history_file and try to keep it in sync during telescope usage and shutdown. commit - 9f74492aafebdbfa02f5abd1aa25e283211fe297 commit + 9e97090d644b51c0cb6e049e8c777139b18457fa blob - f7631998b7a0f5abf1fc2d827c8610be026c3ff0 blob + 3d7a767702543a6d9cd2ab23a78ee3b5f600f7a1 --- fs.c +++ fs.c @@ -54,6 +54,7 @@ static void handle_session_start(struct imsg*, size_ static void handle_session_tab(struct imsg*, size_t); static void handle_session_tab_hist(struct imsg*, size_t); static void handle_session_end(struct imsg*, size_t); +static void handle_hist(struct imsg *, size_t); static void handle_dispatch_imsg(int, short, void*); static int fs_send_ui(int, uint32_t, int, const void *, uint16_t); static size_t join_path(char*, const char*, const char*, size_t); @@ -61,6 +62,7 @@ static void getenv_default(char*, const char*, const static void mkdirs(const char*, mode_t); static void init_paths(void); static void load_last_session(void); +static void load_hist(void); static int last_time_crashed(void); static void load_certs(void); @@ -82,6 +84,7 @@ char bookmark_file[PATH_MAX]; char known_hosts_file[PATH_MAX], known_hosts_tmp[PATH_MAX]; char crashed_file[PATH_MAX]; char session_file[PATH_MAX]; +static char history_file[PATH_MAX]; static imsg_handlerfn *handlers[] = { [IMSG_GET] = handle_get, @@ -96,6 +99,8 @@ static imsg_handlerfn *handlers[] = { [IMSG_SESSION_TAB] = handle_session_tab, [IMSG_SESSION_TAB_HIST] = handle_session_tab_hist, [IMSG_SESSION_END] = handle_session_end, + [IMSG_HIST_ITEM] = handle_hist, + [IMSG_HIST_END] = handle_hist, }; static void __attribute__((__noreturn__)) @@ -337,6 +342,7 @@ handle_misc(struct imsg *imsg, size_t datalen) switch (imsg->hdr.type) { case IMSG_INIT: load_certs(); + load_hist(); load_last_session(); break; @@ -542,6 +548,36 @@ handle_session_end(struct imsg *imsg, size_t datalen) die(); fclose(session); session = NULL; +} + +static void +handle_hist(struct imsg *imsg, size_t datalen) +{ + static FILE *hist; + struct histitem hi; + + switch (imsg->hdr.type) { + case IMSG_HIST_ITEM: + if (hist == NULL) { + if ((hist = fopen(history_file, "a")) == NULL) + return; + } + if (datalen != sizeof(hi)) + abort(); + memcpy(&hi, imsg->data, sizeof(hi)); + fprintf(hist, "%lld %s\n", (long long)hi.ts, hi.uri); + break; + + case IMSG_HIST_END: + if (hist == NULL) + return; + fclose(hist); + hist = NULL; + break; + + default: + abort(); + } } static void @@ -688,6 +724,8 @@ fs_init(void) "/known_hosts.tmp.XXXXXXXXXX", sizeof(known_hosts_tmp)); join_path(session_file, cache_path_base, "/session", sizeof(session_file)); + join_path(history_file, cache_path_base, "/history", + sizeof(history_file)); join_path(crashed_file, cache_path_base, "/crashed", sizeof(crashed_file)); @@ -801,6 +839,43 @@ end: fs_send_ui(IMSG_SESSION_END, 0, -1, &first_time, sizeof(first_time)); } +static void +load_hist(void) +{ + FILE *hist; + size_t linesize = 0; + ssize_t linelen; + char *nl, *spc, *line = NULL; + const char *errstr; + struct histitem hi; + + if ((hist = fopen(history_file, "r")) == NULL) + goto end; + + while ((linelen = getline(&line, &linesize, hist)) != -1) { + if ((nl = strchr(line, '\n')) != NULL) + *nl = '\0'; + if ((spc = strchr(line, ' ')) == NULL) + continue; + *spc = '\0'; + spc++; + + memset(&hi, 0, sizeof(hi)); + hi.ts = strtonum(line, INT64_MIN, INT64_MAX, &errstr); + if (errstr != NULL) + continue; + if (strlcpy(hi.uri, spc, sizeof(hi.uri)) >= sizeof(hi.uri)) + continue; + + fs_send_ui(IMSG_HIST_ITEM, 0, -1, &hi, sizeof(hi)); + } + + fclose(hist); + free(line); +end: + fs_send_ui(IMSG_HIST_END, 0, -1, NULL, 0); +} + int fs_main(void) { blob - 43a69b8fbf331f85f67f3c290166e110e243b5c4 blob + fcb02e901a04e7942b801206c5e170627e7cc97b --- include/session.h +++ include/session.h @@ -32,6 +32,25 @@ struct session_tab_hist { int future; }; +struct histitem { + time_t ts; + char uri[GEMINI_URL_LEN]; +}; + +struct history_item { + time_t ts; + char *uri; + int dirty; +}; + +#define HISTORY_CAP 1000 +struct history { + struct history_item items[HISTORY_CAP]; + size_t len; + size_t dirty; +}; +extern struct history history; + void switch_to_tab(struct tab *); unsigned int tab_new_id(void); struct tab *new_tab(const char *, const char *base, struct tab *); @@ -42,6 +61,10 @@ void stop_tab(struct tab*); void save_session(void); +void history_push(struct histitem *); +void history_sort(void); +void history_add(const char *); + void autosave_init(void); void autosave_timer(int, short, void *); void autosave_hook(void); blob - 66bad0bf2f576e29b0f3724503635a7823945e35 blob + 35d62cdf65eb32eb35a51fc8c3c7316463038e83 --- include/telescope.h +++ include/telescope.h @@ -76,6 +76,9 @@ enum imsg_type { IMSG_SESSION_TAB_HIST, IMSG_SESSION_END, + IMSG_HIST_ITEM, /* struct histitem */ + IMSG_HIST_END, /* empty */ + IMSG_CTL_OPEN_URL, }; blob - 11f78f41d23c9d31050427b6be0874cd8fe71180 blob + d5cdb78eff0b21b41f3eaaab0448c30f63689e41 --- session.c +++ session.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "defaults.h" @@ -28,6 +29,8 @@ #include "session.h" #include "ui.h" +struct history history; + static struct event autosaveev; void @@ -197,6 +200,8 @@ void save_session(void) { struct tab *tab; + struct histitem hi; + size_t i; if (safe_mode) return; @@ -209,9 +214,126 @@ save_session(void) sendtab(tab, 1); ui_send_fs(IMSG_SESSION_END, 0, NULL, 0); + + if (history.dirty) { + for (i = 0; i < history.len && history.dirty > 0; ++i) { + if (!history.items[i].dirty) + continue; + history.dirty--; + history.items[i].dirty = 0; + + memset(&hi, 0, sizeof(hi)); + hi.ts = history.items[i].ts; + strlcpy(hi.uri, history.items[i].uri, sizeof(hi.uri)); + ui_send_fs(IMSG_HIST_ITEM, 0, &hi, sizeof(hi)); + } + ui_send_fs(IMSG_HIST_END, 0, NULL, 0); + history.dirty = 0; + } +} + +void +history_push(struct histitem *hi) +{ + size_t i, oldest = 0; + char *uri; + + for (i = 0; i < history.len; ++i) { + if (history.items[i].ts < history.items[oldest].ts) + oldest = i; + + /* remove duplicates */ + if (!strcmp(history.items[i].uri, hi->uri)) + return; + } + + if ((uri = strdup(hi->uri)) == NULL) + abort(); + + /* don't grow too much; replace the oldest */ + if (history.len == HISTORY_CAP) { + history.items[oldest].ts = hi->ts; + free(history.items[oldest].uri); + history.items[oldest].uri = uri; + return; + } + + history.items[history.len].ts = hi->ts; + history.items[history.len].uri = uri; + history.len++; +} + +static int +history_cmp(const void *a, const void *b) +{ + const struct history_item *i = a, *j = b; + return strcmp(i->uri, j->uri); +} + +void +history_sort(void) +{ + qsort(history.items, history.len, sizeof(history.items[0]), + history_cmp); } void +history_add(const char *uri) +{ + size_t i, j, insert = 0, oldest = 0; + char *u; + int c; + + for (i = 0; i < history.len; ++i) { + if (history.items[i].ts < history.items[oldest].ts) + oldest = i; + + if (insert != 0 && insert < i) + continue; + + c = strcmp(uri, history.items[i].uri); + if (c == 0) { + history.items[i].ts = time(NULL); + history.items[i].dirty = 1; + history.dirty++; + autosave_hook(); + return; + } + + if (c > 0) + insert = i; + } + + if ((u = strdup(uri)) == NULL) + return; + + /* if history is full, replace the oldest one */ + if (history.len == HISTORY_CAP) { + free(history.items[oldest].uri); + history.items[oldest].uri = u; + history.items[oldest].ts = time(NULL); + history.items[oldest].dirty = 1; + history.dirty++; + history_sort(); + autosave_hook(); + return; + } + + /* otherwise just insert in the right spot */ + + for (j = history.len; j > insert; --j) + memcpy(&history.items[j], &history.items[j-1], + sizeof(history.items[j])); + + history.items[insert].ts = time(NULL); + history.items[insert].uri = u; + history.items[insert].dirty = 1; + history.dirty++; + history.len++; + autosave_hook(); +} + +void autosave_init(void) { evtimer_set(&autosaveev, autosave_timer, NULL); blob - 57eae92163eb02e3d31328a530e52eb16e554360 blob + 06cfcc999fdf902c64b2202a599a30d781459224 --- telescope.c +++ telescope.c @@ -125,6 +125,7 @@ static void handle_imsg_bookmark_ok(struct imsg *, s static void handle_imsg_save_cert_ok(struct imsg *, size_t); static void handle_imsg_update_cert_ok(struct imsg *, size_t); static void handle_imsg_session(struct imsg *, size_t); +static void handle_imsg_history(struct imsg *, size_t); static void handle_dispatch_imsg(int, short, void *); static int load_about_url(struct tab *, const char *); static int load_file_url(struct tab *, const char *); @@ -168,6 +169,8 @@ static imsg_handlerfn *handlers[] = { [IMSG_SESSION_TAB] = handle_imsg_session, [IMSG_SESSION_TAB_HIST] = handle_imsg_session, [IMSG_SESSION_END] = handle_imsg_session, + [IMSG_HIST_ITEM] = handle_imsg_history, + [IMSG_HIST_END] = handle_imsg_history, }; static struct ohash certs; @@ -411,6 +414,7 @@ handle_imsg_got_meta(struct imsg *imsg, size_t datalen load_page_from_str(tab, err_pages[tab->code]); ui_require_input(tab, tab->code == 11, ir_select_gemini); } else if (tab->code == 20) { + history_add(tab->hist_cur->h); if (setup_parser_for(tab)) { ui_send_net(IMSG_PROCEED, tab->id, NULL, 0); } else if (safe_mode) { @@ -584,6 +588,37 @@ handle_imsg_session(struct imsg *imsg, size_t datalen) } static void +handle_imsg_history(struct imsg *imsg, size_t datalen) +{ + struct histitem hi; + + /* + * The fs process tried to send history item after it + * has announced that it's done. Something fishy is + * going on, better die + */ + if (operating) + die(); + + switch (imsg->hdr.type) { + case IMSG_HIST_ITEM: + if (datalen != sizeof(hi)) + die(); + + memcpy(&hi, imsg->data, sizeof(hi)); + history_push(&hi); + break; + + case IMSG_HIST_END: + history_sort(); + break; + + default: + die(); + } +} + +static void handle_imsg_buf(struct imsg *imsg, size_t datalen) { struct tab *tab = NULL;