Trim trailing white spaces on empty lines.
[geeqie.git] / src / cache.c
index 95e7793..a3320b0 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Geeqie
  * (C) 2004 John Ellis
+ * Copyright (C) 2008 - 2012 The Geeqie Team
  *
  * Author: John Ellis
  *
@@ -13,6 +14,7 @@
 #include "cache.h"
 
 #include "md5-util.h"
+#include "secure_save.h"
 #include "ui_fileops.h"
 
 #include <utime.h>
  *-------------------------------------------------------------------
  * Cache data file format:
  *-------------------------------------------------------------------
- * 
+ *
  * SIMcache
- * #coment
+ * #comment
  * Dimensions=[<width> x <height>]
  * Date=[<value in time_t format, or -1 if no embedded date>]
  * Checksum=[<value>]
  * MD5sum=[<32 character ascii text digest>]
  * SimilarityGrid[32 x 32]=<3072 bytes of data (1024 pixels in RGB format, 1 pixel is 24bits)>
- * 
- * 
+ *
+ *
  * The first line (9 bytes) indicates it is a SIMcache format file. (new line char must exist)
  * Comment lines starting with a # are ignored up to a new line.
  * All data lines should end with a new line char.
@@ -72,106 +74,107 @@ void cache_sim_data_free(CacheData *cd)
  *-------------------------------------------------------------------
  */
 
-static gint cache_sim_write_dimensions(FILE *f, CacheData *cd)
+static gboolean cache_sim_write_dimensions(SecureSaveInfo *ssi, CacheData *cd)
 {
-       if (!f || !cd || !cd->dimensions) return FALSE;
+       if (!cd || !cd->dimensions) return FALSE;
 
-       fprintf(f, "Dimensions=[%d x %d]\n", cd->width, cd->height);
+       secure_fprintf(ssi, "Dimensions=[%d x %d]\n", cd->width, cd->height);
 
        return TRUE;
 }
 
-static gint cache_sim_write_date(FILE *f, CacheData *cd)
+static gboolean cache_sim_write_date(SecureSaveInfo *ssi, CacheData *cd)
 {
-       if (!f || !cd || !cd->have_date) return FALSE;
+       if (!cd || !cd->have_date) return FALSE;
 
-       fprintf(f, "Date=[%ld]\n", cd->date);
+       secure_fprintf(ssi, "Date=[%ld]\n", cd->date);
 
        return TRUE;
 }
 
-static gint cache_sim_write_checksum(FILE *f, CacheData *cd)
+static gboolean cache_sim_write_checksum(SecureSaveInfo *ssi, CacheData *cd)
 {
-       if (!f || !cd || !cd->have_checksum) return FALSE;
+       if (!cd || !cd->have_checksum) return FALSE;
 
-       fprintf(f, "Checksum=[%ld]\n", cd->checksum);
+       secure_fprintf(ssi, "Checksum=[%ld]\n", cd->checksum);
 
        return TRUE;
 }
 
-static gint cache_sim_write_md5sum(FILE *f, CacheData *cd)
+static gboolean cache_sim_write_md5sum(SecureSaveInfo *ssi, CacheData *cd)
 {
        gchar *text;
 
-       if (!f || !cd || !cd->have_md5sum) return FALSE;
+       if (!cd || !cd->have_md5sum) return FALSE;
 
        text = md5_digest_to_text(cd->md5sum);
-       fprintf(f, "MD5sum=[%s]\n", text);
+       secure_fprintf(ssi, "MD5sum=[%s]\n", text);
        g_free(text);
 
        return TRUE;
 }
 
-static gint cache_sim_write_similarity(FILE *f, CacheData *cd)
+static gboolean cache_sim_write_similarity(SecureSaveInfo *ssi, CacheData *cd)
 {
-       gint success = FALSE;
+       guint x, y;
+       guint8 buf[3 * 32];
 
-       if (!f || !cd || !cd->similarity) return FALSE;
+       if (!cd || !cd->similarity || !cd->sim || !cd->sim->filled) return FALSE;
 
-       if (cd->sim && cd->sim->filled)
+       secure_fprintf(ssi, "SimilarityGrid[32 x 32]=");
+       for (y = 0; y < 32; y++)
                {
-               gint x, y;
-               guint8 buf[96];
+               guint s = y * 32;
+               guint8 *avg_r = &cd->sim->avg_r[s];
+               guint8 *avg_g = &cd->sim->avg_g[s];
+               guint8 *avg_b = &cd->sim->avg_b[s];
+               guint n = 0;
 
-               fprintf(f, "SimilarityGrid[32 x 32]=");
-               for (y = 0; y < 32; y++)
+               for (x = 0; x < 32; x++)
                        {
-                       gint s;
-                       guint8 *p;
-
-                       s = y * 32;
-                       p = buf;
-                       for(x = 0; x < 32; x++)
-                               {
-                               *p = cd->sim->avg_r[s + x]; p++;
-                               *p = cd->sim->avg_g[s + x]; p++;
-                               *p = cd->sim->avg_b[s + x]; p++;
-                               }
-                       fwrite(buf, sizeof(buf), 1, f);
+                       buf[n++] = avg_r[x];
+                       buf[n++] = avg_g[x];
+                       buf[n++] = avg_b[x];
                        }
 
-               fprintf(f, "\n");
-               success = TRUE;
+               secure_fwrite(buf, sizeof(buf), 1, ssi);
                }
 
-       return success;
+       secure_fputc(ssi, '\n');
+
+       return TRUE;
 }
 
-gint cache_sim_data_save(CacheData *cd)
+gboolean cache_sim_data_save(CacheData *cd)
 {
-       FILE *f;
+       SecureSaveInfo *ssi;
        gchar *pathl;
 
        if (!cd || !cd->path) return FALSE;
 
        pathl = path_from_utf8(cd->path);
-       f = fopen(pathl, "w");
+       ssi = secure_open(pathl);
        g_free(pathl);
 
-       if (!f)
+       if (!ssi)
                {
-               printf("Unable to save sim cache data: %s\n", cd->path);
+               log_printf("Unable to save sim cache data: %s\n", cd->path);
                return FALSE;
                }
 
-       fprintf(f, "SIMcache\n#%s %s\n", PACKAGE, VERSION);
-       cache_sim_write_dimensions(f, cd);
-       cache_sim_write_date(f, cd);
-       cache_sim_write_checksum(f, cd);
-       cache_sim_write_md5sum(f, cd);
-       cache_sim_write_similarity(f, cd);
+       secure_fprintf(ssi, "SIMcache\n#%s %s\n", PACKAGE, VERSION);
+       cache_sim_write_dimensions(ssi, cd);
+       cache_sim_write_date(ssi, cd);
+       cache_sim_write_checksum(ssi, cd);
+       cache_sim_write_md5sum(ssi, cd);
+       cache_sim_write_similarity(ssi, cd);
 
-       fclose (f);
+       if (secure_close(ssi))
+               {
+               log_printf(_("error saving sim cache data: %s\nerror: %s\n"), cd->path,
+                           secsave_strerror(secsave_errno));
+               return FALSE;
+               }
 
        return TRUE;
 }
@@ -182,13 +185,13 @@ gint cache_sim_data_save(CacheData *cd)
  *-------------------------------------------------------------------
  */
 
-static gint cache_sim_read_skipline(FILE *f, int s)
+static gboolean cache_sim_read_skipline(FILE *f, gint s)
 {
        if (!f) return FALSE;
 
        if (fseek(f, 0 - s, SEEK_CUR) == 0)
                {
-               char b;
+               gchar b;
                while (fread(&b, sizeof(b), 1, f) == 1)
                        {
                        if (b == '\n') return TRUE;
@@ -199,7 +202,7 @@ static gint cache_sim_read_skipline(FILE *f, int s)
        return FALSE;
 }
 
-static gint cache_sim_read_comment(FILE *f, char *buf, int s, CacheData *cd)
+static gboolean cache_sim_read_comment(FILE *f, gchar *buf, gint s, CacheData *cd)
 {
        if (!f || !buf || !cd) return FALSE;
 
@@ -208,7 +211,7 @@ static gint cache_sim_read_comment(FILE *f, char *buf, int s, CacheData *cd)
        return cache_sim_read_skipline(f, s - 1);
 }
 
-static gint cache_sim_read_dimensions(FILE *f, char *buf, int s, CacheData *cd)
+static gboolean cache_sim_read_dimensions(FILE *f, gchar *buf, gint s, CacheData *cd)
 {
        if (!f || !buf || !cd) return FALSE;
 
@@ -216,9 +219,9 @@ static gint cache_sim_read_dimensions(FILE *f, char *buf, int s, CacheData *cd)
 
        if (fseek(f, - s, SEEK_CUR) == 0)
                {
-               char b;
-               char buf[1024];
-               gint p = 0;
+               gchar b;
+               gchar buf[1024];
+               gsize p = 0;
                gint w, h;
 
                b = 'X';
@@ -226,7 +229,7 @@ static gint cache_sim_read_dimensions(FILE *f, char *buf, int s, CacheData *cd)
                        {
                        if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
                        }
-               while (b != ']' && p < 1023)
+               while (b != ']' && p < sizeof(buf) - 1)
                        {
                        if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
                        buf[p] = b;
@@ -251,7 +254,7 @@ static gint cache_sim_read_dimensions(FILE *f, char *buf, int s, CacheData *cd)
        return FALSE;
 }
 
-static gint cache_sim_read_date(FILE *f, char *buf, int s, CacheData *cd)
+static gboolean cache_sim_read_date(FILE *f, gchar *buf, gint s, CacheData *cd)
 {
        if (!f || !buf || !cd) return FALSE;
 
@@ -259,16 +262,16 @@ static gint cache_sim_read_date(FILE *f, char *buf, int s, CacheData *cd)
 
        if (fseek(f, - s, SEEK_CUR) == 0)
                {
-               char b;
-               char buf[1024];
-               gint p = 0;
+               gchar b;
+               gchar buf[1024];
+               gsize p = 0;
 
                b = 'X';
                while (b != '[')
                        {
                        if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
                        }
-               while (b != ']' && p < 1023)
+               while (b != ']' && p < sizeof(buf) - 1)
                        {
                        if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
                        buf[p] = b;
@@ -291,7 +294,7 @@ static gint cache_sim_read_date(FILE *f, char *buf, int s, CacheData *cd)
        return FALSE;
 }
 
-static gint cache_sim_read_checksum(FILE *f, char *buf, int s, CacheData *cd)
+static gboolean cache_sim_read_checksum(FILE *f, gchar *buf, gint s, CacheData *cd)
 {
        if (!f || !buf || !cd) return FALSE;
 
@@ -299,16 +302,16 @@ static gint cache_sim_read_checksum(FILE *f, char *buf, int s, CacheData *cd)
 
        if (fseek(f, - s, SEEK_CUR) == 0)
                {
-               char b;
-               char buf[1024];
-               gint p = 0;
+               gchar b;
+               gchar buf[1024];
+               gsize p = 0;
 
                b = 'X';
                while (b != '[')
                        {
                        if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
                        }
-               while (b != ']' && p < 1023)
+               while (b != ']' && p < sizeof(buf) - 1)
                        {
                        if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
                        buf[p] = b;
@@ -331,7 +334,7 @@ static gint cache_sim_read_checksum(FILE *f, char *buf, int s, CacheData *cd)
        return FALSE;
 }
 
-static gint cache_sim_read_md5sum(FILE *f, char *buf, int s, CacheData *cd)
+static gboolean cache_sim_read_md5sum(FILE *f, gchar *buf, gint s, CacheData *cd)
 {
        if (!f || !buf || !cd) return FALSE;
 
@@ -339,16 +342,16 @@ static gint cache_sim_read_md5sum(FILE *f, char *buf, int s, CacheData *cd)
 
        if (fseek(f, - s, SEEK_CUR) == 0)
                {
-               char b;
-               char buf[64];
-               gint p = 0;
+               gchar b;
+               gchar buf[64];
+               gsize p = 0;
 
                b = 'X';
                while (b != '[')
                        {
                        if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
                        }
-               while (b != ']' && p < 63)
+               while (b != ']' && p < sizeof(buf) - 1)
                        {
                        if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
                        buf[p] = b;
@@ -368,7 +371,7 @@ static gint cache_sim_read_md5sum(FILE *f, char *buf, int s, CacheData *cd)
        return FALSE;
 }
 
-static gint cache_sim_read_similarity(FILE *f, char *buf, int s, CacheData *cd)
+static gboolean cache_sim_read_similarity(FILE *f, gchar *buf, gint s, CacheData *cd)
 {
        if (!f || !buf || !cd) return FALSE;
 
@@ -378,7 +381,7 @@ static gint cache_sim_read_similarity(FILE *f, char *buf, int s, CacheData *cd)
 
        if (fseek(f, - s, SEEK_CUR) == 0)
                {
-               char b;
+               gchar b;
                guint8 pixel_buf[3];
                ImageSimilarityData *sd;
                gint x, y;
@@ -438,7 +441,7 @@ CacheData *cache_sim_data_load(const gchar *path)
 {
        FILE *f;
        CacheData *cd = NULL;
-       char buf[32];
+       gchar buf[32];
        gint success = CACHE_LOAD_LINE_NOISE;
        gchar *pathl;
 
@@ -453,17 +456,17 @@ CacheData *cache_sim_data_load(const gchar *path)
        cd = cache_sim_data_new();
        cd->path = g_strdup(path);
 
-       if (fread(&buf, sizeof(char), 9, f) != 9 ||
+       if (fread(&buf, sizeof(gchar), 9, f) != 9 ||
            strncmp(buf, "SIMcache", 8) != 0)
                {
-               if (debug) printf("%s is not a cache file\n", cd->path);
+               DEBUG_1("%s is not a cache file", cd->path);
                success = 0;
                }
 
        while (success > 0)
                {
-               int s;
-               s = fread(&buf, sizeof(char), sizeof(buf), f);
+               gint s;
+               s = fread(&buf, sizeof(gchar), sizeof(buf), f);
 
                if (s < 1)
                        {
@@ -532,7 +535,7 @@ void cache_sim_data_set_date(CacheData *cd, time_t date)
        cd->have_date = TRUE;
 }
 
-void cache_sim_data_set_checksum(CacheData *cd, long checksum)
+void cache_sim_data_set_checksum(CacheData *cd, glong checksum)
 {
        if (!cd) return;
 
@@ -567,7 +570,7 @@ void cache_sim_data_set_similarity(CacheData *cd, ImageSimilarityData *sd)
        cd->similarity = TRUE;
 }
 
-gint cache_sim_data_filled(ImageSimilarityData *sd)
+gboolean cache_sim_data_filled(ImageSimilarityData *sd)
 {
        if (!sd) return FALSE;
        return sd->filled;
@@ -579,40 +582,6 @@ gint cache_sim_data_filled(ImageSimilarityData *sd)
  *-------------------------------------------------------------------
  */
 
-/* warning: this func modifies path string contents!, on fail it is set to fail point */
-gint cache_ensure_dir_exists(gchar *path, mode_t mode)
-{
-       if (!path) return FALSE;
-
-       if (!isdir(path))
-               {
-               gchar *p = path;
-               while (p[0] != '\0')
-                       {
-                       p++;
-                       if (p[0] == '/' || p[0] == '\0')
-                               {
-                               gint end = TRUE;
-                               if (p[0] != '\0')
-                                       {
-                                       p[0] = '\0';
-                                       end = FALSE;
-                                       }
-                               if (!isdir(path))
-                                       {
-                                       if (debug) printf("creating sub dir:%s\n", path);
-                                       if (!mkdir_utf8(path, mode))
-                                               {
-                                               printf("create dir failed: %s\n", path);
-                                               return FALSE;
-                                               }
-                                       }
-                               if (!end) p[0] = '/';
-                               }
-                       }
-               }
-       return TRUE;
-}
 
 static void cache_path_parts(CacheType type,
                             const gchar **cache_rc, const gchar **cache_local, const gchar **cache_ext)
@@ -620,20 +589,25 @@ static void cache_path_parts(CacheType type,
        switch (type)
                {
                case CACHE_TYPE_THUMB:
-                       *cache_rc = GQ_CACHE_RC_THUMB;
+                       *cache_rc = get_thumbnails_cache_dir();
                        *cache_local = GQ_CACHE_LOCAL_THUMB;
                        *cache_ext = GQ_CACHE_EXT_THUMB;
                        break;
                case CACHE_TYPE_SIM:
-                       *cache_rc = GQ_CACHE_RC_THUMB;
+                       *cache_rc = get_thumbnails_cache_dir();
                        *cache_local = GQ_CACHE_LOCAL_THUMB;
                        *cache_ext = GQ_CACHE_EXT_SIM;
                        break;
                case CACHE_TYPE_METADATA:
-                       *cache_rc = GQ_CACHE_RC_METADATA;
+                       *cache_rc = get_metadata_cache_dir();
                        *cache_local = GQ_CACHE_LOCAL_METADATA;
                        *cache_ext = GQ_CACHE_EXT_METADATA;
                        break;
+               case CACHE_TYPE_XMP_METADATA:
+                       *cache_rc = get_metadata_cache_dir();
+                       *cache_local = GQ_CACHE_LOCAL_METADATA;
+                       *cache_ext = GQ_CACHE_EXT_XMP_METADATA;
+                       break;
                }
 }
 
@@ -653,24 +627,20 @@ gchar *cache_get_location(CacheType type, const gchar *source, gint include_name
        base = remove_level_from_path(source);
        if (include_name)
                {
-               name = g_strconcat("/", filename_from_path(source), NULL);
-               }
-       else
-               {
-               cache_ext = NULL;
+               name = g_strconcat(filename_from_path(source), cache_ext, NULL);
                }
 
-       if (((type != CACHE_TYPE_METADATA && enable_thumb_dirs) ||
-            (type == CACHE_TYPE_METADATA && enable_metadata_dirs)) &&
+       if (((type != CACHE_TYPE_METADATA && type != CACHE_TYPE_XMP_METADATA && options->thumbnails.cache_into_dirs) ||
+            ((type == CACHE_TYPE_METADATA || type == CACHE_TYPE_XMP_METADATA) && options->metadata.enable_metadata_dirs)) &&
            access_file(base, W_OK))
                {
-               path = g_strconcat(base, "/", cache_local, name, cache_ext, NULL);
+               path = g_build_filename(base, cache_local, name, NULL);
                if (mode) *mode = 0775;
                }
 
        if (!path)
                {
-               path = g_strconcat(homedir(), "/", cache_rc, base, name, cache_ext, NULL);
+               path = g_build_filename(cache_rc, base, name, NULL);
                if (mode) *mode = 0755;
                }
 
@@ -680,39 +650,56 @@ gchar *cache_get_location(CacheType type, const gchar *source, gint include_name
        return path;
 }
 
+static gchar *cache_build_path_local(const gchar *source, const gchar *cache_local, const gchar *cache_ext)
+{
+       gchar *path;
+       gchar *base = remove_level_from_path(source);
+       gchar *name = g_strconcat(filename_from_path(source), cache_ext, NULL);
+       path = g_build_filename(base, cache_local, name, NULL);
+       g_free(name);
+       g_free(base);
+
+       return path;
+}
+
+static gchar *cache_build_path_rc(const gchar *source, const gchar *cache_rc, const gchar *cache_ext)
+{
+       gchar *path;
+       gchar *name = g_strconcat(source, cache_ext, NULL);
+       path = g_build_filename(cache_rc, name, NULL);
+       g_free(name);
+
+       return path;
+}
+
 gchar *cache_find_location(CacheType type, const gchar *source)
 {
        gchar *path;
-       const gchar *name;
-       gchar *base;
        const gchar *cache_rc;
        const gchar *cache_local;
        const gchar *cache_ext;
-       gint prefer_local;
+       gboolean prefer_local;
 
        if (!source) return NULL;
 
        cache_path_parts(type, &cache_rc, &cache_local, &cache_ext);
 
-       name = filename_from_path(source);
-       base = remove_level_from_path(source);
-
-       if (type == CACHE_TYPE_METADATA)
+       if (type == CACHE_TYPE_METADATA || type == CACHE_TYPE_XMP_METADATA)
                {
-               prefer_local = enable_metadata_dirs;
+               prefer_local = options->metadata.enable_metadata_dirs;
                }
        else
                {
-               prefer_local = enable_thumb_dirs;
+               prefer_local = options->thumbnails.cache_into_dirs;
                }
 
        if (prefer_local)
                {
-               path = g_strconcat(base, "/", cache_local, "/", name, cache_ext, NULL);
+               path = cache_build_path_local(source, cache_local, cache_ext);
                }
        else
                {
-               path = g_strconcat(homedir(), "/", cache_rc, source, cache_ext, NULL);
+               path = cache_build_path_rc(source, cache_rc, cache_ext);
                }
 
        if (!isfile(path))
@@ -722,11 +709,11 @@ gchar *cache_find_location(CacheType type, const gchar *source)
                /* try the opposite method if not found */
                if (!prefer_local)
                        {
-                       path = g_strconcat(base, "/", cache_local, "/", name, cache_ext, NULL);
+                       path = cache_build_path_local(source, cache_local, cache_ext);
                        }
                else
                        {
-                       path = g_strconcat(homedir(), "/", cache_rc, source, cache_ext, NULL);
+                       path = cache_build_path_rc(source, cache_rc, cache_ext);
                        }
 
                if (!isfile(path))
@@ -736,18 +723,16 @@ gchar *cache_find_location(CacheType type, const gchar *source)
                        }
                }
 
-       g_free(base);
-
        return path;
 }
 
-gint cache_time_valid(const gchar *cache, const gchar *path)
+gboolean cache_time_valid(const gchar *cache, const gchar *path)
 {
        struct stat cache_st;
        struct stat path_st;
        gchar *cachel;
        gchar *pathl;
-       gint ret = FALSE;
+       gboolean ret = FALSE;
 
        if (!cache || !path) return FALSE;
 
@@ -769,7 +754,7 @@ gint cache_time_valid(const gchar *cache, const gchar *path)
                        if (utime(cachel, &ut) < 0 &&
                            errno == EPERM)
                                {
-                               if (debug) printf("cache permission workaround: %s\n", cachel);
+                               DEBUG_1("cache permission workaround: %s", cachel);
                                ret = TRUE;
                                }
                        }
@@ -781,3 +766,44 @@ gint cache_time_valid(const gchar *cache, const gchar *path)
        return ret;
 }
 
+const gchar *get_thumbnails_cache_dir(void)
+{
+       static gchar *thumbnails_cache_dir = NULL;
+
+       if (thumbnails_cache_dir) return thumbnails_cache_dir;
+
+       if (USE_XDG)
+               {
+               thumbnails_cache_dir = g_build_filename(xdg_cache_home_get(), GQ_APPNAME_LC, GQ_CACHE_THUMB, NULL);
+               }
+       else
+               {
+               thumbnails_cache_dir = g_build_filename(get_rc_dir(), GQ_CACHE_THUMB, NULL);
+               }
+
+       return thumbnails_cache_dir;
+}
+
+const gchar *get_metadata_cache_dir(void)
+{
+       static gchar *metadata_cache_dir = NULL;
+
+       if (metadata_cache_dir) return metadata_cache_dir;
+
+       if (USE_XDG)
+               {
+               /* Metadata go to $XDG_DATA_HOME.
+                * "Keywords and comments, among other things, are irreplaceable and cannot be auto-generated,
+                * so I don't think they'd be appropriate for the cache directory." -- Omari Stephens on geeqie-devel ml
+                */
+               metadata_cache_dir = g_build_filename(xdg_data_home_get(), GQ_APPNAME_LC, GQ_CACHE_METADATA, NULL);
+               }
+       else
+               {
+               metadata_cache_dir = g_build_filename(get_rc_dir(), GQ_CACHE_METADATA, NULL);
+               }
+
+       return metadata_cache_dir;
+}
+
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */