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>]
30 * Date=[<value in time_t format, or -1 if no embedded date>]
32 * MD5sum=[<32 character ascii text digest>]
33 * SimilarityGrid[32 x 32]=<3072 bytes of data (1024 pixels in RGB format, 1 pixel is 24bits)>
36 * The first line (9 bytes) indicates it is a SIMcache format file. (new line char must exist)
37 * Comment lines starting with a # are ignored up to a new line.
38 * All data lines should end with a new line char.
39 * Format is very strict, data must begin with the char immediately following '='.
40 * Currently SimilarityGrid is always assumed to be 32 x 32 RGB.
45 *-------------------------------------------------------------------
47 *-------------------------------------------------------------------
50 CacheData *cache_sim_data_new(void)
54 cd = g_new0(CacheData, 1);
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_date(FILE *f, CacheData *cd)
86 if (!f || !cd || !cd->have_date) return FALSE;
88 fprintf(f, "Date=[%ld]\n", cd->date);
93 static gint cache_sim_write_checksum(FILE *f, CacheData *cd)
95 if (!f || !cd || !cd->have_checksum) return FALSE;
97 fprintf(f, "Checksum=[%ld]\n", cd->checksum);
102 static gint cache_sim_write_md5sum(FILE *f, CacheData *cd)
106 if (!f || !cd || !cd->have_md5sum) return FALSE;
108 text = md5_digest_to_text(cd->md5sum);
109 fprintf(f, "MD5sum=[%s]\n", text);
115 static gint cache_sim_write_similarity(FILE *f, CacheData *cd)
117 gint success = FALSE;
119 if (!f || !cd || !cd->similarity) return FALSE;
121 if (cd->sim && cd->sim->filled)
126 fprintf(f, "SimilarityGrid[32 x 32]=");
127 for (y = 0; y < 32; y++)
134 for(x = 0; x < 32; x++)
136 *p = cd->sim->avg_r[s + x]; p++;
137 *p = cd->sim->avg_g[s + x]; p++;
138 *p = cd->sim->avg_b[s + x]; p++;
140 fwrite(buf, sizeof(buf), 1, f);
150 gint cache_sim_data_save(CacheData *cd)
155 if (!cd || !cd->path) return FALSE;
157 pathl = path_from_utf8(cd->path);
158 f = fopen(pathl, "w");
163 printf("Unable to save sim cache data: %s\n", cd->path);
167 fprintf(f, "SIMcache\n#%s %s\n", PACKAGE, VERSION);
168 cache_sim_write_dimensions(f, cd);
169 cache_sim_write_date(f, cd);
170 cache_sim_write_checksum(f, cd);
171 cache_sim_write_md5sum(f, cd);
172 cache_sim_write_similarity(f, cd);
180 *-------------------------------------------------------------------
182 *-------------------------------------------------------------------
185 static gint cache_sim_read_comment(FILE *f, char *buf, int s, CacheData *cd)
187 if (!f || !buf || !cd) return FALSE;
189 if (buf[0] != '#') return FALSE;
191 if (fseek(f, 0 - (s - 1), SEEK_CUR) == 0)
194 while(fread(&b, sizeof(b), 1, f) == 1)
196 if (b == '\n') return TRUE;
204 static gint cache_sim_read_dimensions(FILE *f, char *buf, int s, CacheData *cd)
206 if (!f || !buf || !cd) return FALSE;
208 if (s < 10 || strncmp("Dimensions", buf, 10) != 0) return FALSE;
210 if (fseek(f, - s, SEEK_CUR) == 0)
220 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
222 while (b != ']' && p < 1023)
224 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
231 if (fread(&b, sizeof(b), 1, f) != 1) break;
235 if (sscanf(buf, "%d x %d", &w, &h) != 2) return FALSE;
239 cd->dimensions = TRUE;
247 static gint cache_sim_read_date(FILE *f, char *buf, int s, CacheData *cd)
249 if (!f || !buf || !cd) return FALSE;
251 if (s < 4 || strncmp("Date", buf, 4) != 0) return FALSE;
253 if (fseek(f, - s, SEEK_CUR) == 0)
262 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
264 while (b != ']' && p < 1023)
266 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
273 if (fread(&b, sizeof(b), 1, f) != 1) break;
277 cd->date = strtol(buf, NULL, 10);
279 cd->have_date = TRUE;
287 static gint cache_sim_read_checksum(FILE *f, char *buf, int s, CacheData *cd)
289 if (!f || !buf || !cd) return FALSE;
291 if (s < 8 || strncmp("Checksum", buf, 8) != 0) return FALSE;
293 if (fseek(f, - s, SEEK_CUR) == 0)
302 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
304 while (b != ']' && p < 1023)
306 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
313 if (fread(&b, sizeof(b), 1, f) != 1) break;
317 cd->checksum = strtol(buf, NULL, 10);
319 cd->have_checksum = TRUE;
327 static gint cache_sim_read_md5sum(FILE *f, char *buf, int s, CacheData *cd)
329 if (!f || !buf || !cd) return FALSE;
331 if (s < 8 || strncmp("MD5sum", buf, 6) != 0) return FALSE;
333 if (fseek(f, - s, SEEK_CUR) == 0)
342 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
344 while (b != ']' && p < 63)
346 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
352 if (fread(&b, sizeof(b), 1, f) != 1) break;
356 cd->have_md5sum = md5_digest_from_text(buf, cd->md5sum);
364 static gint cache_sim_read_similarity(FILE *f, char *buf, int s, CacheData *cd)
366 if (!f || !buf || !cd) return FALSE;
368 if (s < 11 || strncmp("Similarity", buf, 10) != 0) return FALSE;
370 if (strncmp("Grid[32 x 32]", buf + 10, 13) != 0) return FALSE;
372 if (fseek(f, - s, SEEK_CUR) == 0)
376 ImageSimilarityData *sd;
382 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
387 /* use current sim that may already contain data we will not touch here */
390 cd->similarity = FALSE;
394 sd = image_sim_new();
397 for (y = 0; y < 32; y++)
400 for (x = 0; x < 32; x++)
402 if (fread(&pixel_buf, sizeof(pixel_buf), 1, f) != 1)
407 sd->avg_r[s + x] = pixel_buf[0];
408 sd->avg_g[s + x] = pixel_buf[1];
409 sd->avg_b[s + x] = pixel_buf[2];
413 if (fread(&b, sizeof(b), 1, f) == 1)
415 if (b != '\n') fseek(f, -1, SEEK_CUR);
419 cd->sim->filled = TRUE;
420 cd->similarity = TRUE;
428 CacheData *cache_sim_data_load(const gchar *path)
431 CacheData *cd = NULL;
436 if (!path) return NULL;
438 pathl = path_from_utf8(path);
439 f = fopen(pathl, "r");
444 cd = cache_sim_data_new();
445 cd->path = g_strdup(path);
447 if (fread(&buf, sizeof(char), 9, f) != 9 ||
448 strncmp(buf, "SIMcache", 8) != 0)
450 if (debug) printf("%s is not a cache file\n", cd->path);
457 s = fread(&buf, sizeof(char), sizeof(buf), f);
465 if (!cache_sim_read_comment(f, buf, s, cd) &&
466 !cache_sim_read_dimensions(f, buf, s, cd) &&
467 !cache_sim_read_date(f, buf, s, cd) &&
468 !cache_sim_read_checksum(f, buf, s, cd) &&
469 !cache_sim_read_md5sum(f, buf, s, cd) &&
470 !cache_sim_read_similarity(f, buf, s, cd))
479 if (!cd->dimensions && !cd->similarity)
481 cache_sim_data_free(cd);
489 *-------------------------------------------------------------------
491 *-------------------------------------------------------------------
494 void cache_sim_data_set_dimensions(CacheData *cd, gint w, gint h)
500 cd->dimensions = TRUE;
503 void cache_sim_data_set_date(CacheData *cd, time_t date)
508 cd->have_date = TRUE;
511 void cache_sim_data_set_checksum(CacheData *cd, long checksum)
515 cd->checksum = checksum;
516 cd->have_checksum = TRUE;
519 void cache_sim_data_set_md5sum(CacheData *cd, guchar digest[16])
525 for (i = 0; i < 16; i++)
527 cd->md5sum[i] = digest[i];
529 cd->have_md5sum = TRUE;
532 void cache_sim_data_set_similarity(CacheData *cd, ImageSimilarityData *sd)
534 if (!cd || !sd || !sd->filled) return;
536 if (!cd->sim) cd->sim = image_sim_new();
538 memcpy(cd->sim->avg_r, sd->avg_r, 1024);
539 memcpy(cd->sim->avg_g, sd->avg_g, 1024);
540 memcpy(cd->sim->avg_b, sd->avg_b, 1024);
541 cd->sim->filled = TRUE;
543 cd->similarity = TRUE;
546 gint cache_sim_data_filled(ImageSimilarityData *sd)
548 if (!sd) return FALSE;
553 *-------------------------------------------------------------------
554 * cache path location utils
555 *-------------------------------------------------------------------
558 /* warning: this func modifies path string contents!, on fail it is set to fail point */
559 gint cache_ensure_dir_exists(gchar *path, mode_t mode)
561 if (!path) return FALSE;
569 if (p[0] == '/' || p[0] == '\0')
579 if (debug) printf("creating sub dir:%s\n", path);
580 if (!mkdir_utf8(path, mode))
582 printf("create dir failed: %s\n", path);
586 if (!end) p[0] = '/';
593 static void cache_path_parts(CacheType type,
594 const gchar **cache_rc, const gchar **cache_local, const gchar **cache_ext)
598 case CACHE_TYPE_THUMB:
599 *cache_rc = GQVIEW_CACHE_RC_THUMB;
600 *cache_local = GQVIEW_CACHE_LOCAL_THUMB;
601 *cache_ext = GQVIEW_CACHE_EXT_THUMB;
604 *cache_rc = GQVIEW_CACHE_RC_THUMB;
605 *cache_local = GQVIEW_CACHE_LOCAL_THUMB;
606 *cache_ext = GQVIEW_CACHE_EXT_SIM;
608 case CACHE_TYPE_METADATA:
609 *cache_rc = GQVIEW_CACHE_RC_METADATA;
610 *cache_local = GQVIEW_CACHE_LOCAL_METADATA;
611 *cache_ext = GQVIEW_CACHE_EXT_METADATA;
616 gchar *cache_get_location(CacheType type, const gchar *source, gint include_name, mode_t *mode)
621 const gchar *cache_rc;
622 const gchar *cache_local;
623 const gchar *cache_ext;
625 if (!source) return NULL;
627 cache_path_parts(type, &cache_rc, &cache_local, &cache_ext);
629 base = remove_level_from_path(source);
632 name = g_strconcat("/", filename_from_path(source), NULL);
639 if (((type != CACHE_TYPE_METADATA && enable_thumb_dirs) ||
640 (type == CACHE_TYPE_METADATA && enable_metadata_dirs)) &&
641 access_file(base, W_OK))
643 path = g_strconcat(base, "/", cache_local, name, cache_ext, NULL);
644 if (mode) *mode = 0775;
649 path = g_strconcat(homedir(), "/", cache_rc, base, name, cache_ext, NULL);
650 if (mode) *mode = 0755;
654 if (name) g_free(name);
659 gchar *cache_find_location(CacheType type, const gchar *source)
664 const gchar *cache_rc;
665 const gchar *cache_local;
666 const gchar *cache_ext;
669 if (!source) return NULL;
671 cache_path_parts(type, &cache_rc, &cache_local, &cache_ext);
673 name = filename_from_path(source);
674 base = remove_level_from_path(source);
676 if (type == CACHE_TYPE_METADATA)
678 prefer_local = enable_metadata_dirs;
682 prefer_local = enable_thumb_dirs;
687 path = g_strconcat(base, "/", cache_local, "/", name, cache_ext, NULL);
691 path = g_strconcat(homedir(), "/", cache_rc, source, cache_ext, NULL);
698 /* try the opposite method if not found */
701 path = g_strconcat(base, "/", cache_local, "/", name, cache_ext, NULL);
705 path = g_strconcat(homedir(), "/", cache_rc, source, cache_ext, NULL);
720 gint cache_time_valid(const gchar *cache, const gchar *path)
722 struct stat cache_st;
728 if (!cache || !path) return FALSE;
730 cachel = path_from_utf8(cache);
731 pathl = path_from_utf8(path);
733 if (stat(cachel, &cache_st) == 0 &&
734 stat(pathl, &path_st) == 0)
736 if (cache_st.st_mtime == path_st.st_mtime)
740 else if (cache_st.st_mtime > path_st.st_mtime)
744 ut.actime = ut.modtime = cache_st.st_mtime;
745 if (utime(cachel, &ut) < 0 &&
748 if (debug) printf("cache permission workaround: %s\n", cachel);