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!
17 #include "ui_fileops.h"
24 *-------------------------------------------------------------------
25 * Cache data file format:
26 *-------------------------------------------------------------------
30 * Dimensions=[<width> x <height>]
31 * Date=[<value in time_t format, or -1 if no embedded date>]
33 * MD5sum=[<32 character ascii text digest>]
34 * SimilarityGrid[32 x 32]=<3072 bytes of data (1024 pixels in RGB format, 1 pixel is 24bits)>
37 * The first line (9 bytes) indicates it is a SIMcache format file. (new line char must exist)
38 * Comment lines starting with a # are ignored up to a new line.
39 * All data lines should end with a new line char.
40 * Format is very strict, data must begin with the char immediately following '='.
41 * Currently SimilarityGrid is always assumed to be 32 x 32 RGB.
46 *-------------------------------------------------------------------
48 *-------------------------------------------------------------------
51 CacheData *cache_sim_data_new(void)
55 cd = g_new0(CacheData, 1);
61 void cache_sim_data_free(CacheData *cd)
66 image_sim_free(cd->sim);
71 *-------------------------------------------------------------------
73 *-------------------------------------------------------------------
76 static gint cache_sim_write_dimensions(FILE *f, CacheData *cd)
78 if (!f || !cd || !cd->dimensions) return FALSE;
80 fprintf(f, "Dimensions=[%d x %d]\n", cd->width, cd->height);
85 static gint cache_sim_write_date(FILE *f, CacheData *cd)
87 if (!f || !cd || !cd->have_date) return FALSE;
89 fprintf(f, "Date=[%ld]\n", cd->date);
94 static gint cache_sim_write_checksum(FILE *f, CacheData *cd)
96 if (!f || !cd || !cd->have_checksum) return FALSE;
98 fprintf(f, "Checksum=[%ld]\n", cd->checksum);
103 static gint cache_sim_write_md5sum(FILE *f, CacheData *cd)
107 if (!f || !cd || !cd->have_md5sum) return FALSE;
109 text = md5_digest_to_text(cd->md5sum);
110 fprintf(f, "MD5sum=[%s]\n", text);
116 static gint cache_sim_write_similarity(FILE *f, CacheData *cd)
118 gint success = FALSE;
120 if (!f || !cd || !cd->similarity) return FALSE;
122 if (cd->sim && cd->sim->filled)
127 fprintf(f, "SimilarityGrid[32 x 32]=");
128 for (y = 0; y < 32; y++)
135 for(x = 0; x < 32; x++)
137 *p = cd->sim->avg_r[s + x]; p++;
138 *p = cd->sim->avg_g[s + x]; p++;
139 *p = cd->sim->avg_b[s + x]; p++;
141 fwrite(buf, sizeof(buf), 1, f);
151 gint cache_sim_data_save(CacheData *cd)
156 if (!cd || !cd->path) return FALSE;
158 pathl = path_from_utf8(cd->path);
159 f = fopen(pathl, "w");
164 printf("Unable to save sim cache data: %s\n", cd->path);
168 fprintf(f, "SIMcache\n#%s %s\n", PACKAGE, VERSION);
169 cache_sim_write_dimensions(f, cd);
170 cache_sim_write_date(f, cd);
171 cache_sim_write_checksum(f, cd);
172 cache_sim_write_md5sum(f, cd);
173 cache_sim_write_similarity(f, cd);
181 *-------------------------------------------------------------------
183 *-------------------------------------------------------------------
186 static gint cache_sim_read_skipline(FILE *f, int s)
188 if (!f) return FALSE;
190 if (fseek(f, 0 - s, SEEK_CUR) == 0)
193 while (fread(&b, sizeof(b), 1, f) == 1)
195 if (b == '\n') return TRUE;
203 static gint cache_sim_read_comment(FILE *f, char *buf, int s, CacheData *cd)
205 if (!f || !buf || !cd) return FALSE;
207 if (s < 1 || buf[0] != '#') return FALSE;
209 return cache_sim_read_skipline(f, s - 1);
212 static gint cache_sim_read_dimensions(FILE *f, char *buf, int s, CacheData *cd)
214 if (!f || !buf || !cd) return FALSE;
216 if (s < 10 || strncmp("Dimensions", buf, 10) != 0) return FALSE;
218 if (fseek(f, - s, SEEK_CUR) == 0)
228 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
230 while (b != ']' && p < 1023)
232 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
239 if (fread(&b, sizeof(b), 1, f) != 1) break;
243 if (sscanf(buf, "%d x %d", &w, &h) != 2) return FALSE;
247 cd->dimensions = TRUE;
255 static gint cache_sim_read_date(FILE *f, char *buf, int s, CacheData *cd)
257 if (!f || !buf || !cd) return FALSE;
259 if (s < 4 || strncmp("Date", buf, 4) != 0) return FALSE;
261 if (fseek(f, - s, SEEK_CUR) == 0)
270 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
272 while (b != ']' && p < 1023)
274 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
281 if (fread(&b, sizeof(b), 1, f) != 1) break;
285 cd->date = strtol(buf, NULL, 10);
287 cd->have_date = TRUE;
295 static gint cache_sim_read_checksum(FILE *f, char *buf, int s, CacheData *cd)
297 if (!f || !buf || !cd) return FALSE;
299 if (s < 8 || strncmp("Checksum", buf, 8) != 0) return FALSE;
301 if (fseek(f, - s, SEEK_CUR) == 0)
310 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
312 while (b != ']' && p < 1023)
314 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
321 if (fread(&b, sizeof(b), 1, f) != 1) break;
325 cd->checksum = strtol(buf, NULL, 10);
327 cd->have_checksum = TRUE;
335 static gint cache_sim_read_md5sum(FILE *f, char *buf, int s, CacheData *cd)
337 if (!f || !buf || !cd) return FALSE;
339 if (s < 8 || strncmp("MD5sum", buf, 6) != 0) return FALSE;
341 if (fseek(f, - s, SEEK_CUR) == 0)
350 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
352 while (b != ']' && p < 63)
354 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
360 if (fread(&b, sizeof(b), 1, f) != 1) break;
364 cd->have_md5sum = md5_digest_from_text(buf, cd->md5sum);
372 static gint cache_sim_read_similarity(FILE *f, char *buf, int s, CacheData *cd)
374 if (!f || !buf || !cd) return FALSE;
376 if (s < 11 || strncmp("Similarity", buf, 10) != 0) return FALSE;
378 if (strncmp("Grid[32 x 32]", buf + 10, 13) != 0) return FALSE;
380 if (fseek(f, - s, SEEK_CUR) == 0)
384 ImageSimilarityData *sd;
390 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
395 /* use current sim that may already contain data we will not touch here */
398 cd->similarity = FALSE;
402 sd = image_sim_new();
405 for (y = 0; y < 32; y++)
408 for (x = 0; x < 32; x++)
410 if (fread(&pixel_buf, sizeof(pixel_buf), 1, f) != 1)
415 sd->avg_r[s + x] = pixel_buf[0];
416 sd->avg_g[s + x] = pixel_buf[1];
417 sd->avg_b[s + x] = pixel_buf[2];
421 if (fread(&b, sizeof(b), 1, f) == 1)
423 if (b != '\n') fseek(f, -1, SEEK_CUR);
427 cd->sim->filled = TRUE;
428 cd->similarity = TRUE;
436 #define CACHE_LOAD_LINE_NOISE 8
438 CacheData *cache_sim_data_load(const gchar *path)
441 CacheData *cd = NULL;
443 gint success = CACHE_LOAD_LINE_NOISE;
446 if (!path) return NULL;
448 pathl = path_from_utf8(path);
449 f = fopen(pathl, "r");
454 cd = cache_sim_data_new();
455 cd->path = g_strdup(path);
457 if (fread(&buf, sizeof(char), 9, f) != 9 ||
458 strncmp(buf, "SIMcache", 8) != 0)
460 DEBUG_1("%s is not a cache file\n", cd->path);
467 s = fread(&buf, sizeof(char), sizeof(buf), f);
475 if (!cache_sim_read_comment(f, buf, s, cd) &&
476 !cache_sim_read_dimensions(f, buf, s, cd) &&
477 !cache_sim_read_date(f, buf, s, cd) &&
478 !cache_sim_read_checksum(f, buf, s, cd) &&
479 !cache_sim_read_md5sum(f, buf, s, cd) &&
480 !cache_sim_read_similarity(f, buf, s, cd))
482 if (!cache_sim_read_skipline(f, s))
493 success = CACHE_LOAD_LINE_NOISE;
500 if (!cd->dimensions &&
502 !cd->have_checksum &&
506 cache_sim_data_free(cd);
514 *-------------------------------------------------------------------
516 *-------------------------------------------------------------------
519 void cache_sim_data_set_dimensions(CacheData *cd, gint w, gint h)
525 cd->dimensions = TRUE;
528 void cache_sim_data_set_date(CacheData *cd, time_t date)
533 cd->have_date = TRUE;
536 void cache_sim_data_set_checksum(CacheData *cd, long checksum)
540 cd->checksum = checksum;
541 cd->have_checksum = TRUE;
544 void cache_sim_data_set_md5sum(CacheData *cd, guchar digest[16])
550 for (i = 0; i < 16; i++)
552 cd->md5sum[i] = digest[i];
554 cd->have_md5sum = TRUE;
557 void cache_sim_data_set_similarity(CacheData *cd, ImageSimilarityData *sd)
559 if (!cd || !sd || !sd->filled) return;
561 if (!cd->sim) cd->sim = image_sim_new();
563 memcpy(cd->sim->avg_r, sd->avg_r, 1024);
564 memcpy(cd->sim->avg_g, sd->avg_g, 1024);
565 memcpy(cd->sim->avg_b, sd->avg_b, 1024);
566 cd->sim->filled = TRUE;
568 cd->similarity = TRUE;
571 gint cache_sim_data_filled(ImageSimilarityData *sd)
573 if (!sd) return FALSE;
578 *-------------------------------------------------------------------
579 * cache path location utils
580 *-------------------------------------------------------------------
583 /* warning: this func modifies path string contents!, on fail it is set to fail point */
584 gint cache_ensure_dir_exists(gchar *path, mode_t mode)
586 if (!path) return FALSE;
594 if (p[0] == '/' || p[0] == '\0')
604 DEBUG_1("creating sub dir:%s\n", path);
605 if (!mkdir_utf8(path, mode))
607 printf("create dir failed: %s\n", path);
611 if (!end) p[0] = '/';
618 static void cache_path_parts(CacheType type,
619 const gchar **cache_rc, const gchar **cache_local, const gchar **cache_ext)
623 case CACHE_TYPE_THUMB:
624 *cache_rc = GQ_CACHE_RC_THUMB;
625 *cache_local = GQ_CACHE_LOCAL_THUMB;
626 *cache_ext = GQ_CACHE_EXT_THUMB;
629 *cache_rc = GQ_CACHE_RC_THUMB;
630 *cache_local = GQ_CACHE_LOCAL_THUMB;
631 *cache_ext = GQ_CACHE_EXT_SIM;
633 case CACHE_TYPE_METADATA:
634 *cache_rc = GQ_CACHE_RC_METADATA;
635 *cache_local = GQ_CACHE_LOCAL_METADATA;
636 *cache_ext = GQ_CACHE_EXT_METADATA;
641 gchar *cache_get_location(CacheType type, const gchar *source, gint include_name, mode_t *mode)
646 const gchar *cache_rc;
647 const gchar *cache_local;
648 const gchar *cache_ext;
650 if (!source) return NULL;
652 cache_path_parts(type, &cache_rc, &cache_local, &cache_ext);
654 base = remove_level_from_path(source);
657 name = g_strconcat("/", filename_from_path(source), NULL);
664 if (((type != CACHE_TYPE_METADATA && options->thumbnails.cache_into_dirs) ||
665 (type == CACHE_TYPE_METADATA && options->enable_metadata_dirs)) &&
666 access_file(base, W_OK))
668 path = g_strconcat(base, "/", cache_local, name, cache_ext, NULL);
669 if (mode) *mode = 0775;
674 path = g_strconcat(homedir(), "/", cache_rc, base, name, cache_ext, NULL);
675 if (mode) *mode = 0755;
679 if (name) g_free(name);
684 gchar *cache_find_location(CacheType type, const gchar *source)
689 const gchar *cache_rc;
690 const gchar *cache_local;
691 const gchar *cache_ext;
694 if (!source) return NULL;
696 cache_path_parts(type, &cache_rc, &cache_local, &cache_ext);
698 name = filename_from_path(source);
699 base = remove_level_from_path(source);
701 if (type == CACHE_TYPE_METADATA)
703 prefer_local = options->enable_metadata_dirs;
707 prefer_local = options->thumbnails.cache_into_dirs;
712 path = g_strconcat(base, "/", cache_local, "/", name, cache_ext, NULL);
716 path = g_strconcat(homedir(), "/", cache_rc, source, cache_ext, NULL);
723 /* try the opposite method if not found */
726 path = g_strconcat(base, "/", cache_local, "/", name, cache_ext, NULL);
730 path = g_strconcat(homedir(), "/", cache_rc, source, cache_ext, NULL);
745 gint cache_time_valid(const gchar *cache, const gchar *path)
747 struct stat cache_st;
753 if (!cache || !path) return FALSE;
755 cachel = path_from_utf8(cache);
756 pathl = path_from_utf8(path);
758 if (stat(cachel, &cache_st) == 0 &&
759 stat(pathl, &path_st) == 0)
761 if (cache_st.st_mtime == path_st.st_mtime)
765 else if (cache_st.st_mtime > path_st.st_mtime)
769 ut.actime = ut.modtime = cache_st.st_mtime;
770 if (utime(cachel, &ut) < 0 &&
773 DEBUG_1("cache permission workaround: %s\n", cachel);