From c21a3e3f28a5c45497d09ab27d71538b983ca535 Mon Sep 17 00:00:00 2001
From: Bert <ber.t@gmx.com>
Date: Thu, 7 Apr 2011 14:33:57 +0200
Subject: [PATCH] Write thumbnail cache files on exit

---
 thumbs.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++-
 util.c   | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 util.h   |  7 +++++
 3 files changed, 156 insertions(+), 2 deletions(-)

diff --git a/thumbs.c b/thumbs.c
index 8e5e67e..e3c3f53 100644
--- a/thumbs.c
+++ b/thumbs.c
@@ -18,6 +18,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <sys/time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -292,6 +293,9 @@ int tns_translate(tns_t *tns, int x, int y) {
 	return -1;
 }
 
+
+/* thumbnail caching */
+
 int tns_cache_enabled() {
 	int len, ret = 0;
 	char *cpath, *homedir;
@@ -309,5 +313,70 @@ int tns_cache_enabled() {
 	return ret;
 }
 
-void tns_cache_write(thumb_t *t, Bool force) {
+char* tns_cache_filename(const char *filename) {
+	size_t len;
+	int i;
+	char *cfile, *abspath, *homedir;
+
+	if (!filename)
+		return NULL;
+	if (!(homedir = getenv("HOME")))
+		return NULL;
+	
+	if (*filename != '/') {
+		if (!(abspath = absolute_path(filename)))
+			return NULL;
+	} else {
+		abspath = (char*) s_malloc(strlen(filename) + 1);
+		strcpy(abspath, filename);
+	}
+
+	len = strlen(abspath);
+	for (i = 1; i < len; ++i) {
+		if (abspath[i] == '/')
+			abspath[i] = '%';
+	}
+
+	len += strlen(homedir) + 15;
+	cfile = (char*) s_malloc(len);
+	snprintf(cfile, len, "%s/.sxiv/%s.png", homedir, abspath + 1);
+	
+	free(abspath);
+
+	return cfile;
+}
+
+void tns_cache_write(thumb_t *t, Bool force) {
+	char *cfile;
+	struct stat cstats, fstats;
+	struct timeval times[2];
+	Imlib_Load_Error err;
+
+	if (!t || !t->im || !t->filename)
+		return;
+
+	if ((cfile = tns_cache_filename(t->filename))) {
+		if (stat(t->filename, &fstats))
+			goto end;
+
+		if (force || stat(cfile, &cstats) ||
+		    cstats.st_mtim.tv_sec != fstats.st_mtim.tv_sec ||
+		    cstats.st_mtim.tv_nsec != fstats.st_mtim.tv_nsec)
+		{
+			imlib_context_set_image(t->im);
+			imlib_image_set_format("png");
+			imlib_save_image_with_error_return(cfile, &err);
+
+			if (err) {
+				warn("could not cache thumbnail:", t->filename);
+			} else {
+				TIMESPEC_TO_TIMEVAL(&times[0], &fstats.st_atim);
+				TIMESPEC_TO_TIMEVAL(&times[1], &fstats.st_mtim);
+				utimes(cfile, times);
+			}
+		}
+	}
+
+end:
+	free(cfile);
 }
diff --git a/util.c b/util.c
index 5c5737b..f2302fd 100644
--- a/util.c
+++ b/util.c
@@ -18,11 +18,13 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+#include <errno.h>
 
 #include "options.h"
 #include "util.h"
 
-#define FNAME_LEN 512
+#define FNAME_LEN 1024
 
 void cleanup();
 
@@ -78,6 +80,82 @@ void size_readable(float *size, const char **unit) {
 	*unit = units[MIN(i, LEN(units) - 1)];
 }
 
+char* absolute_path(const char *filename) {
+	size_t len;
+	char *path = NULL;
+	const char *basename;
+	char *dirname = NULL;
+	char *cwd = NULL;
+	char *twd = NULL;
+	char *dir;
+	char *s;
+
+	if (!filename || *filename == '\0' || *filename == '/')
+		return NULL;
+
+	len = FNAME_LEN;
+	cwd = (char*) s_malloc(len);
+	while (!(s = getcwd(cwd, len)) && errno == ERANGE) {
+		len *= 2;
+		cwd = (char*) s_realloc(cwd, len);
+	}
+	if (!s)
+		goto error;
+
+	s = strrchr(filename, '/');
+	if (s) {
+		len = s - filename;
+		dirname = (char*) s_malloc(len + 1);
+		strncpy(dirname, filename, len);
+		dirname[len] = '\0';
+		basename = s + 1;
+
+		if (chdir(cwd))
+			/* we're not able to come back afterwards */
+			goto error;
+		if (chdir(dirname))
+			goto error;
+
+		len = FNAME_LEN;
+		twd = (char*) s_malloc(len);
+		while (!(s = getcwd(twd, len)) && errno == ERANGE) {
+			len *= 2;
+			twd = (char*) s_realloc(twd, len);
+		}
+		if (chdir(cwd))
+			die("could not revert to working directory");
+		if (!s)
+			goto error;
+		dir = twd;
+	} else {
+		/* only a single filename given */
+		basename = filename;
+		dir = cwd;
+	}
+
+	len = strlen(dir) + strlen(basename) + 2;
+	path = (char*) s_malloc(len);
+	snprintf(path, len, "%s/%s", dir, basename);
+
+goto end;
+
+error:
+	if (path) {
+		free(path);
+		path = NULL;
+	}
+
+end:
+	if (dirname)
+		free(dirname);
+	if (cwd)
+		free(cwd);
+	if (twd)
+		free(twd);
+
+	return path;
+}
+
 char* readline(FILE *stream) {
 	size_t len;
 	char *buf, *s, *end;
diff --git a/util.h b/util.h
index f1db6b8..e3cdef9 100644
--- a/util.h
+++ b/util.h
@@ -30,6 +30,11 @@
 #define TV_TO_DOUBLE(x) ((double) ((x).tv_sec) + 0.000001 * \
                          (double) ((x).tv_usec))
 
+#define TIMESPEC_TO_TIMEVAL(tv, ts) {      \
+		(tv)->tv_sec = (ts)->tv_sec;           \
+		(tv)->tv_usec = (ts)->tv_nsec / 1000;  \
+}
+
 void* s_malloc(size_t);
 void* s_realloc(void*, size_t);
 
@@ -38,6 +43,8 @@ void die(const char*, ...);
 
 void size_readable(float*, const char**);
 
+char* absolute_path(const char*);
+
 char* readline(FILE*);
 
 #endif /* UTIL_H */