Commit Diff


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 <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
 #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;