4 * Copyright (C) 2008 The Geeqie Team
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
18 #include "secure_save.h"
19 #include "ui_fileops.h"
26 *-------------------------------------------------------------------
27 * Cache data file format:
28 *-------------------------------------------------------------------
32 * Dimensions=[<width> x <height>]
33 * Date=[<value in time_t format, or -1 if no embedded date>]
35 * MD5sum=[<32 character ascii text digest>]
36 * SimilarityGrid[32 x 32]=<3072 bytes of data (1024 pixels in RGB format, 1 pixel is 24bits)>
39 * The first line (9 bytes) indicates it is a SIMcache format file. (new line char must exist)
40 * Comment lines starting with a # are ignored up to a new line.
41 * All data lines should end with a new line char.
42 * Format is very strict, data must begin with the char immediately following '='.
43 * Currently SimilarityGrid is always assumed to be 32 x 32 RGB.
48 *-------------------------------------------------------------------
50 *-------------------------------------------------------------------
53 CacheData *cache_sim_data_new(void)
57 cd = g_new0(CacheData, 1);
63 void cache_sim_data_free(CacheData *cd)
68 image_sim_free(cd->sim);
73 *-------------------------------------------------------------------
75 *-------------------------------------------------------------------
78 static gint cache_sim_write_dimensions(SecureSaveInfo *ssi, CacheData *cd)
80 if (!cd || !cd->dimensions) return FALSE;
82 secure_fprintf(ssi, "Dimensions=[%d x %d]\n", cd->width, cd->height);
87 static gint cache_sim_write_date(SecureSaveInfo *ssi, CacheData *cd)
89 if (!cd || !cd->have_date) return FALSE;
91 secure_fprintf(ssi, "Date=[%ld]\n", cd->date);
96 static gint cache_sim_write_checksum(SecureSaveInfo *ssi, CacheData *cd)
98 if (!cd || !cd->have_checksum) return FALSE;
100 secure_fprintf(ssi, "Checksum=[%ld]\n", cd->checksum);
105 static gint cache_sim_write_md5sum(SecureSaveInfo *ssi, CacheData *cd)
109 if (!cd || !cd->have_md5sum) return FALSE;
111 text = md5_digest_to_text(cd->md5sum);
112 secure_fprintf(ssi, "MD5sum=[%s]\n", text);
118 static gint cache_sim_write_similarity(SecureSaveInfo *ssi, CacheData *cd)
123 if (!cd || !cd->similarity || !cd->sim || !cd->sim->filled) return FALSE;
125 secure_fprintf(ssi, "SimilarityGrid[32 x 32]=");
126 for (y = 0; y < 32; y++)
129 guint8 *avg_r = &cd->sim->avg_r[s];
130 guint8 *avg_g = &cd->sim->avg_g[s];
131 guint8 *avg_b = &cd->sim->avg_b[s];
134 for (x = 0; x < 32; x++)
141 secure_fwrite(buf, sizeof(buf), 1, ssi);
144 secure_fputc(ssi, '\n');
149 gint cache_sim_data_save(CacheData *cd)
154 if (!cd || !cd->path) return FALSE;
156 pathl = path_from_utf8(cd->path);
157 ssi = secure_open(pathl);
162 printf("Unable to save sim cache data: %s\n", cd->path);
166 secure_fprintf(ssi, "SIMcache\n#%s %s\n", PACKAGE, VERSION);
167 cache_sim_write_dimensions(ssi, cd);
168 cache_sim_write_date(ssi, cd);
169 cache_sim_write_checksum(ssi, cd);
170 cache_sim_write_md5sum(ssi, cd);
171 cache_sim_write_similarity(ssi, cd);
173 if (secure_close(ssi))
175 printf_term(_("error saving sim cache data: %s\nerror: %s\n"), cd->path,
176 secsave_strerror(secsave_errno));
184 *-------------------------------------------------------------------
186 *-------------------------------------------------------------------
189 static gint cache_sim_read_skipline(FILE *f, int s)
191 if (!f) return FALSE;
193 if (fseek(f, 0 - s, SEEK_CUR) == 0)
196 while (fread(&b, sizeof(b), 1, f) == 1)
198 if (b == '\n') return TRUE;
206 static gint cache_sim_read_comment(FILE *f, char *buf, int s, CacheData *cd)
208 if (!f || !buf || !cd) return FALSE;
210 if (s < 1 || buf[0] != '#') return FALSE;
212 return cache_sim_read_skipline(f, s - 1);
215 static gint cache_sim_read_dimensions(FILE *f, char *buf, int s, CacheData *cd)
217 if (!f || !buf || !cd) return FALSE;
219 if (s < 10 || strncmp("Dimensions", buf, 10) != 0) return FALSE;
221 if (fseek(f, - s, SEEK_CUR) == 0)
231 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
233 while (b != ']' && p < 1023)
235 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
242 if (fread(&b, sizeof(b), 1, f) != 1) break;
246 if (sscanf(buf, "%d x %d", &w, &h) != 2) return FALSE;
250 cd->dimensions = TRUE;
258 static gint cache_sim_read_date(FILE *f, char *buf, int s, CacheData *cd)
260 if (!f || !buf || !cd) return FALSE;
262 if (s < 4 || strncmp("Date", buf, 4) != 0) return FALSE;
264 if (fseek(f, - s, SEEK_CUR) == 0)
273 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
275 while (b != ']' && p < 1023)
277 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
284 if (fread(&b, sizeof(b), 1, f) != 1) break;
288 cd->date = strtol(buf, NULL, 10);
290 cd->have_date = TRUE;
298 static gint cache_sim_read_checksum(FILE *f, char *buf, int s, CacheData *cd)
300 if (!f || !buf || !cd) return FALSE;
302 if (s < 8 || strncmp("Checksum", buf, 8) != 0) return FALSE;
304 if (fseek(f, - s, SEEK_CUR) == 0)
313 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
315 while (b != ']' && p < 1023)
317 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
324 if (fread(&b, sizeof(b), 1, f) != 1) break;
328 cd->checksum = strtol(buf, NULL, 10);
330 cd->have_checksum = TRUE;
338 static gint cache_sim_read_md5sum(FILE *f, char *buf, int s, CacheData *cd)
340 if (!f || !buf || !cd) return FALSE;
342 if (s < 8 || strncmp("MD5sum", buf, 6) != 0) return FALSE;
344 if (fseek(f, - s, SEEK_CUR) == 0)
353 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
355 while (b != ']' && p < 63)
357 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
363 if (fread(&b, sizeof(b), 1, f) != 1) break;
367 cd->have_md5sum = md5_digest_from_text(buf, cd->md5sum);
375 static gint cache_sim_read_similarity(FILE *f, char *buf, int s, CacheData *cd)
377 if (!f || !buf || !cd) return FALSE;
379 if (s < 11 || strncmp("Similarity", buf, 10) != 0) return FALSE;
381 if (strncmp("Grid[32 x 32]", buf + 10, 13) != 0) return FALSE;
383 if (fseek(f, - s, SEEK_CUR) == 0)
387 ImageSimilarityData *sd;
393 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
398 /* use current sim that may already contain data we will not touch here */
401 cd->similarity = FALSE;
405 sd = image_sim_new();
408 for (y = 0; y < 32; y++)
411 for (x = 0; x < 32; x++)
413 if (fread(&pixel_buf, sizeof(pixel_buf), 1, f) != 1)
418 sd->avg_r[s + x] = pixel_buf[0];
419 sd->avg_g[s + x] = pixel_buf[1];
420 sd->avg_b[s + x] = pixel_buf[2];
424 if (fread(&b, sizeof(b), 1, f) == 1)
426 if (b != '\n') fseek(f, -1, SEEK_CUR);
430 cd->sim->filled = TRUE;
431 cd->similarity = TRUE;
439 #define CACHE_LOAD_LINE_NOISE 8
441 CacheData *cache_sim_data_load(const gchar *path)
444 CacheData *cd = NULL;
446 gint success = CACHE_LOAD_LINE_NOISE;
449 if (!path) return NULL;
451 pathl = path_from_utf8(path);
452 f = fopen(pathl, "r");
457 cd = cache_sim_data_new();
458 cd->path = g_strdup(path);
460 if (fread(&buf, sizeof(char), 9, f) != 9 ||
461 strncmp(buf, "SIMcache", 8) != 0)
463 DEBUG_1("%s is not a cache file", cd->path);
470 s = fread(&buf, sizeof(char), sizeof(buf), f);
478 if (!cache_sim_read_comment(f, buf, s, cd) &&
479 !cache_sim_read_dimensions(f, buf, s, cd) &&
480 !cache_sim_read_date(f, buf, s, cd) &&
481 !cache_sim_read_checksum(f, buf, s, cd) &&
482 !cache_sim_read_md5sum(f, buf, s, cd) &&
483 !cache_sim_read_similarity(f, buf, s, cd))
485 if (!cache_sim_read_skipline(f, s))
496 success = CACHE_LOAD_LINE_NOISE;
503 if (!cd->dimensions &&
505 !cd->have_checksum &&
509 cache_sim_data_free(cd);
517 *-------------------------------------------------------------------
519 *-------------------------------------------------------------------
522 void cache_sim_data_set_dimensions(CacheData *cd, gint w, gint h)
528 cd->dimensions = TRUE;
531 void cache_sim_data_set_date(CacheData *cd, time_t date)
536 cd->have_date = TRUE;
539 void cache_sim_data_set_checksum(CacheData *cd, long checksum)
543 cd->checksum = checksum;
544 cd->have_checksum = TRUE;
547 void cache_sim_data_set_md5sum(CacheData *cd, guchar digest[16])
553 for (i = 0; i < 16; i++)
555 cd->md5sum[i] = digest[i];
557 cd->have_md5sum = TRUE;
560 void cache_sim_data_set_similarity(CacheData *cd, ImageSimilarityData *sd)
562 if (!cd || !sd || !sd->filled) return;
564 if (!cd->sim) cd->sim = image_sim_new();
566 memcpy(cd->sim->avg_r, sd->avg_r, 1024);
567 memcpy(cd->sim->avg_g, sd->avg_g, 1024);
568 memcpy(cd->sim->avg_b, sd->avg_b, 1024);
569 cd->sim->filled = TRUE;
571 cd->similarity = TRUE;
574 gint cache_sim_data_filled(ImageSimilarityData *sd)
576 if (!sd) return FALSE;
581 *-------------------------------------------------------------------
582 * cache path location utils
583 *-------------------------------------------------------------------
586 /* warning: this func modifies path string contents!, on fail it is set to fail point */
587 gint cache_ensure_dir_exists(gchar *path, mode_t mode)
589 if (!path) return FALSE;
597 if (p[0] == '/' || p[0] == '\0')
607 DEBUG_1("creating sub dir:%s", path);
608 if (!mkdir_utf8(path, mode))
610 printf("create dir failed: %s\n", path);
614 if (!end) p[0] = '/';
621 static void cache_path_parts(CacheType type,
622 const gchar **cache_rc, const gchar **cache_local, const gchar **cache_ext)
626 case CACHE_TYPE_THUMB:
627 *cache_rc = GQ_CACHE_RC_THUMB;
628 *cache_local = GQ_CACHE_LOCAL_THUMB;
629 *cache_ext = GQ_CACHE_EXT_THUMB;
632 *cache_rc = GQ_CACHE_RC_THUMB;
633 *cache_local = GQ_CACHE_LOCAL_THUMB;
634 *cache_ext = GQ_CACHE_EXT_SIM;
636 case CACHE_TYPE_METADATA:
637 *cache_rc = GQ_CACHE_RC_METADATA;
638 *cache_local = GQ_CACHE_LOCAL_METADATA;
639 *cache_ext = GQ_CACHE_EXT_METADATA;
644 gchar *cache_get_location(CacheType type, const gchar *source, gint include_name, mode_t *mode)
649 const gchar *cache_rc;
650 const gchar *cache_local;
651 const gchar *cache_ext;
653 if (!source) return NULL;
655 cache_path_parts(type, &cache_rc, &cache_local, &cache_ext);
657 base = remove_level_from_path(source);
660 name = g_strconcat("/", filename_from_path(source), NULL);
667 if (((type != CACHE_TYPE_METADATA && options->thumbnails.cache_into_dirs) ||
668 (type == CACHE_TYPE_METADATA && options->enable_metadata_dirs)) &&
669 access_file(base, W_OK))
671 path = g_strconcat(base, "/", cache_local, name, cache_ext, NULL);
672 if (mode) *mode = 0775;
677 path = g_strconcat(homedir(), "/", cache_rc, base, name, cache_ext, NULL);
678 if (mode) *mode = 0755;
682 if (name) g_free(name);
687 gchar *cache_find_location(CacheType type, const gchar *source)
692 const gchar *cache_rc;
693 const gchar *cache_local;
694 const gchar *cache_ext;
697 if (!source) return NULL;
699 cache_path_parts(type, &cache_rc, &cache_local, &cache_ext);
701 name = filename_from_path(source);
702 base = remove_level_from_path(source);
704 if (type == CACHE_TYPE_METADATA)
706 prefer_local = options->enable_metadata_dirs;
710 prefer_local = options->thumbnails.cache_into_dirs;
715 path = g_strconcat(base, "/", cache_local, "/", name, cache_ext, NULL);
719 path = g_strconcat(homedir(), "/", cache_rc, source, cache_ext, NULL);
726 /* try the opposite method if not found */
729 path = g_strconcat(base, "/", cache_local, "/", name, cache_ext, NULL);
733 path = g_strconcat(homedir(), "/", cache_rc, source, cache_ext, NULL);
748 gint cache_time_valid(const gchar *cache, const gchar *path)
750 struct stat cache_st;
756 if (!cache || !path) return FALSE;
758 cachel = path_from_utf8(cache);
759 pathl = path_from_utf8(path);
761 if (stat(cachel, &cache_st) == 0 &&
762 stat(pathl, &path_st) == 0)
764 if (cache_st.st_mtime == path_st.st_mtime)
768 else if (cache_st.st_mtime > path_st.st_mtime)
772 ut.actime = ut.modtime = cache_st.st_mtime;
773 if (utime(cachel, &ut) < 0 &&
776 DEBUG_1("cache permission workaround: %s", cachel);