7 * This software is released under the GNU General Public License (GNU GPL).
8 * Please read the included file COPYING for more information.
9 * This software comes with no warranty of any kind, use at your own risk!
16 #include "ui_fileops.h"
23 *-------------------------------------------------------------------
24 * Cache data file format:
25 *-------------------------------------------------------------------
29 * Dimensions=[<width> x <height>]
31 * MD5sum=[<32 character ascii text digest>]
32 * SimilarityGrid[32 x 32]=<3072 bytes of data (1024 pixels in RGB format, 1 pixel is 24bits)>
35 * The first line (9 bytes) indicates it is a SIMcache format file. (new line char must exist)
36 * Comment lines starting with a # are ignored up to a new line.
37 * All data lines should end with a new line char.
38 * Format is very strict, data must begin with the char immediately following '='.
39 * Currently SimilarityGrid is always assumed to be 32 x 32 RGB.
44 *-------------------------------------------------------------------
46 *-------------------------------------------------------------------
49 CacheData *cache_sim_data_new(void)
53 cd = g_new0(CacheData, 1);
54 cd->dimensions = FALSE;
55 cd->similarity = FALSE;
60 void cache_sim_data_free(CacheData *cd)
65 image_sim_free(cd->sim);
70 *-------------------------------------------------------------------
72 *-------------------------------------------------------------------
75 static gint cache_sim_write_dimensions(FILE *f, CacheData *cd)
77 if (!f || !cd || !cd->dimensions) return FALSE;
79 fprintf(f, "Dimensions=[%d x %d]\n", cd->width, cd->height);
84 static gint cache_sim_write_checksum(FILE *f, CacheData *cd)
86 if (!f || !cd || !cd->have_checksum) return FALSE;
88 fprintf(f, "Checksum=[%ld]\n", cd->checksum);
93 static gint cache_sim_write_md5sum(FILE *f, CacheData *cd)
97 if (!f || !cd || !cd->have_md5sum) return FALSE;
99 text = md5_digest_to_text(cd->md5sum);
100 fprintf(f, "MD5sum=[%s]\n", text);
106 static gint cache_sim_write_similarity(FILE *f, CacheData *cd)
108 gint success = FALSE;
110 if (!f || !cd || !cd->similarity) return FALSE;
112 if (cd->sim && cd->sim->filled)
117 fprintf(f, "SimilarityGrid[32 x 32]=");
118 for (y = 0; y < 32; y++)
125 for(x = 0; x < 32; x++)
127 *p = cd->sim->avg_r[s + x]; p++;
128 *p = cd->sim->avg_g[s + x]; p++;
129 *p = cd->sim->avg_b[s + x]; p++;
131 fwrite(buf, sizeof(buf), 1, f);
141 gint cache_sim_data_save(CacheData *cd)
146 if (!cd || !cd->path) return FALSE;
148 pathl = path_from_utf8(cd->path);
149 f = fopen(pathl, "w");
154 printf("Unable to save sim cache data: %s\n", cd->path);
158 fprintf(f, "SIMcache\n#%s %s\n", PACKAGE, VERSION);
159 cache_sim_write_dimensions(f, cd);
160 cache_sim_write_checksum(f, cd);
161 cache_sim_write_md5sum(f, cd);
162 cache_sim_write_similarity(f, cd);
170 *-------------------------------------------------------------------
172 *-------------------------------------------------------------------
175 static gint cache_sim_read_comment(FILE *f, char *buf, int s, CacheData *cd)
177 if (!f || !buf || !cd) return FALSE;
179 if (buf[0] != '#') return FALSE;
181 if (fseek(f, 0 - (s - 1), SEEK_CUR) == 0)
184 while(fread(&b, sizeof(b), 1, f) == 1)
186 if (b == '\n') return TRUE;
194 static gint cache_sim_read_dimensions(FILE *f, char *buf, int s, CacheData *cd)
196 if (!f || !buf || !cd) return FALSE;
198 if (s < 10 || strncmp("Dimensions", buf, 10) != 0) return FALSE;
200 if (fseek(f, - s, SEEK_CUR) == 0)
210 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
212 while (b != ']' && p < 1023)
214 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
221 if (fread(&b, sizeof(b), 1, f) != 1) break;
225 if (sscanf(buf, "%d x %d", &w, &h) != 2) return FALSE;
229 cd->dimensions = TRUE;
237 static gint cache_sim_read_checksum(FILE *f, char *buf, int s, CacheData *cd)
239 if (!f || !buf || !cd) return FALSE;
241 if (s < 8 || strncmp("Checksum", buf, 8) != 0) return FALSE;
243 if (fseek(f, - s, SEEK_CUR) == 0)
252 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
254 while (b != ']' && p < 1023)
256 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
263 if (fread(&b, sizeof(b), 1, f) != 1) break;
267 cd->checksum = strtol(buf, NULL, 10);
269 cd->have_checksum = TRUE;
277 static gint cache_sim_read_md5sum(FILE *f, char *buf, int s, CacheData *cd)
279 if (!f || !buf || !cd) return FALSE;
281 if (s < 8 || strncmp("MD5sum", buf, 6) != 0) return FALSE;
283 if (fseek(f, - s, SEEK_CUR) == 0)
292 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
294 while (b != ']' && p < 63)
296 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
302 if (fread(&b, sizeof(b), 1, f) != 1) break;
306 cd->have_md5sum = md5_digest_from_text(buf, cd->md5sum);
314 static gint cache_sim_read_similarity(FILE *f, char *buf, int s, CacheData *cd)
316 if (!f || !buf || !cd) return FALSE;
318 if (s < 11 || strncmp("Similarity", buf, 10) != 0) return FALSE;
320 if (strncmp("Grid[32 x 32]", buf + 10, 13) != 0) return FALSE;
322 if (fseek(f, - s, SEEK_CUR) == 0)
326 ImageSimilarityData *sd;
332 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
337 /* use current sim that may already contain data we will not touch here */
340 cd->similarity = FALSE;
344 sd = image_sim_new();
347 for (y = 0; y < 32; y++)
350 for (x = 0; x < 32; x++)
352 if (fread(&pixel_buf, sizeof(pixel_buf), 1, f) != 1)
357 sd->avg_r[s + x] = pixel_buf[0];
358 sd->avg_g[s + x] = pixel_buf[1];
359 sd->avg_b[s + x] = pixel_buf[2];
363 if (fread(&b, sizeof(b), 1, f) == 1)
365 if (b != '\n') fseek(f, -1, SEEK_CUR);
369 cd->sim->filled = TRUE;
370 cd->similarity = TRUE;
378 CacheData *cache_sim_data_load(const gchar *path)
381 CacheData *cd = NULL;
386 if (!path) return NULL;
388 pathl = path_from_utf8(path);
389 f = fopen(pathl, "r");
394 cd = cache_sim_data_new();
395 cd->path = g_strdup(path);
397 if (fread(&buf, sizeof(char), 9, f) != 9 ||
398 strncmp(buf, "SIMcache", 8) != 0)
400 if (debug) printf("%s is not a cache file\n", cd->path);
407 s = fread(&buf, sizeof(char), sizeof(buf), f);
415 if (!cache_sim_read_comment(f, buf, s, cd) &&
416 !cache_sim_read_dimensions(f, buf, s, cd) &&
417 !cache_sim_read_checksum(f, buf, s, cd) &&
418 !cache_sim_read_md5sum(f, buf, s, cd) &&
419 !cache_sim_read_similarity(f, buf, s, cd))
428 if (!cd->dimensions && !cd->similarity)
430 cache_sim_data_free(cd);
438 *-------------------------------------------------------------------
440 *-------------------------------------------------------------------
443 void cache_sim_data_set_dimensions(CacheData *cd, gint w, gint h)
449 cd->dimensions = TRUE;
452 void cache_sim_data_set_checksum(CacheData *cd, long checksum)
456 cd->checksum = checksum;
457 cd->have_checksum = TRUE;
460 void cache_sim_data_set_md5sum(CacheData *cd, guchar digest[16])
466 for (i = 0; i < 16; i++)
468 cd->md5sum[i] = digest[i];
470 cd->have_md5sum = TRUE;
473 void cache_sim_data_set_similarity(CacheData *cd, ImageSimilarityData *sd)
475 if (!cd || !sd || !sd->filled) return;
477 if (!cd->sim) cd->sim = image_sim_new();
479 memcpy(cd->sim->avg_r, sd->avg_r, 1024);
480 memcpy(cd->sim->avg_g, sd->avg_g, 1024);
481 memcpy(cd->sim->avg_b, sd->avg_b, 1024);
482 cd->sim->filled = TRUE;
484 cd->similarity = TRUE;
487 gint cache_sim_data_filled(ImageSimilarityData *sd)
489 if (!sd) return FALSE;
494 *-------------------------------------------------------------------
495 * cache path location utils
496 *-------------------------------------------------------------------
499 /* warning: this func modifies path string contents!, on fail it is set to fail point */
500 gint cache_ensure_dir_exists(gchar *path, mode_t mode)
502 if (!path) return FALSE;
510 if (p[0] == '/' || p[0] == '\0')
520 if (debug) printf("creating sub dir:%s\n", path);
521 if (!mkdir_utf8(path, mode))
523 printf("create dir failed: %s\n", path);
527 if (!end) p[0] = '/';
534 static void cache_path_parts(CacheType type,
535 const gchar **cache_rc, const gchar **cache_local, const gchar **cache_ext)
539 case CACHE_TYPE_THUMB:
540 *cache_rc = GQVIEW_CACHE_RC_THUMB;
541 *cache_local = GQVIEW_CACHE_LOCAL_THUMB;
542 *cache_ext = GQVIEW_CACHE_EXT_THUMB;
545 *cache_rc = GQVIEW_CACHE_RC_THUMB;
546 *cache_local = GQVIEW_CACHE_LOCAL_THUMB;
547 *cache_ext = GQVIEW_CACHE_EXT_SIM;
549 case CACHE_TYPE_METADATA:
550 *cache_rc = GQVIEW_CACHE_RC_METADATA;
551 *cache_local = GQVIEW_CACHE_LOCAL_METADATA;
552 *cache_ext = GQVIEW_CACHE_EXT_METADATA;
557 gchar *cache_get_location(CacheType type, const gchar *source, gint include_name, mode_t *mode)
562 const gchar *cache_rc;
563 const gchar *cache_local;
564 const gchar *cache_ext;
566 if (!source) return NULL;
568 cache_path_parts(type, &cache_rc, &cache_local, &cache_ext);
570 base = remove_level_from_path(source);
573 name = g_strconcat("/", filename_from_path(source), NULL);
580 if (((type != CACHE_TYPE_METADATA && enable_thumb_dirs) ||
581 (type == CACHE_TYPE_METADATA && enable_metadata_dirs)) &&
582 access_file(base, W_OK))
584 path = g_strconcat(base, "/", cache_local, name, cache_ext, NULL);
585 if (mode) *mode = 0775;
590 path = g_strconcat(homedir(), "/", cache_rc, base, name, cache_ext, NULL);
591 if (mode) *mode = 0755;
595 if (name) g_free(name);
600 gchar *cache_find_location(CacheType type, const gchar *source)
605 const gchar *cache_rc;
606 const gchar *cache_local;
607 const gchar *cache_ext;
610 if (!source) return NULL;
612 cache_path_parts(type, &cache_rc, &cache_local, &cache_ext);
614 name = filename_from_path(source);
615 base = remove_level_from_path(source);
617 if (type == CACHE_TYPE_METADATA)
619 prefer_local = enable_metadata_dirs;
623 prefer_local = enable_thumb_dirs;
628 path = g_strconcat(base, "/", cache_local, "/", name, cache_ext, NULL);
632 path = g_strconcat(homedir(), "/", cache_rc, source, cache_ext, NULL);
639 /* try the opposite method if not found */
642 path = g_strconcat(base, "/", cache_local, "/", name, cache_ext, NULL);
646 path = g_strconcat(homedir(), "/", cache_rc, source, cache_ext, NULL);
661 gint cache_time_valid(const gchar *cache, const gchar *path)
663 struct stat cache_st;
669 if (!cache || !path) return FALSE;
671 cachel = path_from_utf8(cache);
672 pathl = path_from_utf8(path);
674 if (stat(cachel, &cache_st) == 0 &&
675 stat(pathl, &path_st) == 0)
677 if (cache_st.st_mtime == path_st.st_mtime)
681 else if (cache_st.st_mtime > path_st.st_mtime)
685 ut.actime = ut.modtime = cache_st.st_mtime;
686 if (utime(cachel, &ut) < 0 &&
689 if (debug) printf("cache permission workaround: %s\n", cachel);