diff --git a/thumbs.c b/thumbs.c index 8e5e67e..e3c3f53 100644 --- a/thumbs.c +++ b/thumbs.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -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(×[0], &fstats.st_atim); + TIMESPEC_TO_TIMEVAL(×[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 #include +#include +#include #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 */