Mercurial > geeqie
comparison src/cache.c @ 9:d907d608745f
Sync to GQview 1.5.9 release.
########
DO NOT BASE ENHANCEMENTS OR TRANSLATION UPDATES ON CODE IN THIS CVS!
This CVS is never up to date with current development and is provided
solely for reference purposes, please use the latest official release
package when making any changes or translation updates.
########
author | gqview |
---|---|
date | Sat, 26 Feb 2005 00:13:35 +0000 |
parents | |
children | 67ba4381497e |
comparison
equal
deleted
inserted
replaced
8:e0d0593d519e | 9:d907d608745f |
---|---|
1 /* | |
2 * GQview | |
3 * (C) 2004 John Ellis | |
4 * | |
5 * Author: John Ellis | |
6 * | |
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! | |
10 */ | |
11 | |
12 #include "gqview.h" | |
13 #include "cache.h" | |
14 | |
15 #include "md5-util.h" | |
16 #include "ui_fileops.h" | |
17 | |
18 #include <utime.h> | |
19 #include <errno.h> | |
20 | |
21 | |
22 /* | |
23 *------------------------------------------------------------------- | |
24 * Cache data file format: | |
25 *------------------------------------------------------------------- | |
26 * | |
27 * SIMcache | |
28 * #coment | |
29 * Dimensions=[<width> x <height>] | |
30 * Checksum=[<value>] | |
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)> | |
33 * | |
34 * | |
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. | |
40 */ | |
41 | |
42 | |
43 /* | |
44 *------------------------------------------------------------------- | |
45 * sim cache data | |
46 *------------------------------------------------------------------- | |
47 */ | |
48 | |
49 CacheData *cache_sim_data_new(void) | |
50 { | |
51 CacheData *cd; | |
52 | |
53 cd = g_new0(CacheData, 1); | |
54 cd->dimensions = FALSE; | |
55 cd->similarity = FALSE; | |
56 | |
57 return cd; | |
58 } | |
59 | |
60 void cache_sim_data_free(CacheData *cd) | |
61 { | |
62 if (!cd) return; | |
63 | |
64 g_free(cd->path); | |
65 image_sim_free(cd->sim); | |
66 g_free(cd); | |
67 } | |
68 | |
69 /* | |
70 *------------------------------------------------------------------- | |
71 * sim cache write | |
72 *------------------------------------------------------------------- | |
73 */ | |
74 | |
75 static gint cache_sim_write_dimensions(FILE *f, CacheData *cd) | |
76 { | |
77 if (!f || !cd || !cd->dimensions) return FALSE; | |
78 | |
79 fprintf(f, "Dimensions=[%d x %d]\n", cd->width, cd->height); | |
80 | |
81 return TRUE; | |
82 } | |
83 | |
84 static gint cache_sim_write_checksum(FILE *f, CacheData *cd) | |
85 { | |
86 if (!f || !cd || !cd->have_checksum) return FALSE; | |
87 | |
88 fprintf(f, "Checksum=[%ld]\n", cd->checksum); | |
89 | |
90 return TRUE; | |
91 } | |
92 | |
93 static gint cache_sim_write_md5sum(FILE *f, CacheData *cd) | |
94 { | |
95 gchar *text; | |
96 | |
97 if (!f || !cd || !cd->have_md5sum) return FALSE; | |
98 | |
99 text = md5_digest_to_text(cd->md5sum); | |
100 fprintf(f, "MD5sum=[%s]\n", text); | |
101 g_free(text); | |
102 | |
103 return TRUE; | |
104 } | |
105 | |
106 static gint cache_sim_write_similarity(FILE *f, CacheData *cd) | |
107 { | |
108 gint success = FALSE; | |
109 | |
110 if (!f || !cd || !cd->similarity) return FALSE; | |
111 | |
112 if (cd->sim && cd->sim->filled) | |
113 { | |
114 gint x, y; | |
115 guint8 buf[96]; | |
116 | |
117 fprintf(f, "SimilarityGrid[32 x 32]="); | |
118 for (y = 0; y < 32; y++) | |
119 { | |
120 gint s; | |
121 guint8 *p; | |
122 | |
123 s = y * 32; | |
124 p = buf; | |
125 for(x = 0; x < 32; x++) | |
126 { | |
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++; | |
130 } | |
131 fwrite(buf, sizeof(buf), 1, f); | |
132 } | |
133 | |
134 fprintf(f, "\n"); | |
135 success = TRUE; | |
136 } | |
137 | |
138 return success; | |
139 } | |
140 | |
141 gint cache_sim_data_save(CacheData *cd) | |
142 { | |
143 FILE *f; | |
144 gchar *pathl; | |
145 | |
146 if (!cd || !cd->path) return FALSE; | |
147 | |
148 pathl = path_from_utf8(cd->path); | |
149 f = fopen(pathl, "w"); | |
150 g_free(pathl); | |
151 | |
152 if (!f) | |
153 { | |
154 printf("Unable to save sim cache data: %s\n", cd->path); | |
155 return FALSE; | |
156 } | |
157 | |
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); | |
163 | |
164 fclose (f); | |
165 | |
166 return TRUE; | |
167 } | |
168 | |
169 /* | |
170 *------------------------------------------------------------------- | |
171 * sim cache read | |
172 *------------------------------------------------------------------- | |
173 */ | |
174 | |
175 static gint cache_sim_read_comment(FILE *f, char *buf, int s, CacheData *cd) | |
176 { | |
177 if (!f || !buf || !cd) return FALSE; | |
178 | |
179 if (buf[0] != '#') return FALSE; | |
180 | |
181 if (fseek(f, 0 - (s - 1), SEEK_CUR) == 0) | |
182 { | |
183 char b; | |
184 while(fread(&b, sizeof(b), 1, f) == 1) | |
185 { | |
186 if (b == '\n') return TRUE; | |
187 } | |
188 return TRUE; | |
189 } | |
190 | |
191 return FALSE; | |
192 } | |
193 | |
194 static gint cache_sim_read_dimensions(FILE *f, char *buf, int s, CacheData *cd) | |
195 { | |
196 if (!f || !buf || !cd) return FALSE; | |
197 | |
198 if (s < 10 || strncmp("Dimensions", buf, 10) != 0) return FALSE; | |
199 | |
200 if (fseek(f, - s, SEEK_CUR) == 0) | |
201 { | |
202 char b; | |
203 char buf[1024]; | |
204 gint p = 0; | |
205 gint w, h; | |
206 | |
207 b = 'X'; | |
208 while (b != '[') | |
209 { | |
210 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE; | |
211 } | |
212 while (b != ']' && p < 1023) | |
213 { | |
214 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE; | |
215 buf[p] = b; | |
216 p++; | |
217 } | |
218 | |
219 while (b != '\n') | |
220 { | |
221 if (fread(&b, sizeof(b), 1, f) != 1) break; | |
222 } | |
223 | |
224 buf[p] = '\0'; | |
225 if (sscanf(buf, "%d x %d", &w, &h) != 2) return FALSE; | |
226 | |
227 cd->width = w; | |
228 cd->height = h; | |
229 cd->dimensions = TRUE; | |
230 | |
231 return TRUE; | |
232 } | |
233 | |
234 return FALSE; | |
235 } | |
236 | |
237 static gint cache_sim_read_checksum(FILE *f, char *buf, int s, CacheData *cd) | |
238 { | |
239 if (!f || !buf || !cd) return FALSE; | |
240 | |
241 if (s < 8 || strncmp("Checksum", buf, 8) != 0) return FALSE; | |
242 | |
243 if (fseek(f, - s, SEEK_CUR) == 0) | |
244 { | |
245 char b; | |
246 char buf[1024]; | |
247 gint p = 0; | |
248 | |
249 b = 'X'; | |
250 while (b != '[') | |
251 { | |
252 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE; | |
253 } | |
254 while (b != ']' && p < 1023) | |
255 { | |
256 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE; | |
257 buf[p] = b; | |
258 p++; | |
259 } | |
260 | |
261 while (b != '\n') | |
262 { | |
263 if (fread(&b, sizeof(b), 1, f) != 1) break; | |
264 } | |
265 | |
266 buf[p] = '\0'; | |
267 cd->checksum = strtol(buf, NULL, 10); | |
268 | |
269 cd->have_checksum = TRUE; | |
270 | |
271 return TRUE; | |
272 } | |
273 | |
274 return FALSE; | |
275 } | |
276 | |
277 static gint cache_sim_read_md5sum(FILE *f, char *buf, int s, CacheData *cd) | |
278 { | |
279 if (!f || !buf || !cd) return FALSE; | |
280 | |
281 if (s < 8 || strncmp("MD5sum", buf, 6) != 0) return FALSE; | |
282 | |
283 if (fseek(f, - s, SEEK_CUR) == 0) | |
284 { | |
285 char b; | |
286 char buf[64]; | |
287 gint p = 0; | |
288 | |
289 b = 'X'; | |
290 while (b != '[') | |
291 { | |
292 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE; | |
293 } | |
294 while (b != ']' && p < 63) | |
295 { | |
296 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE; | |
297 buf[p] = b; | |
298 p++; | |
299 } | |
300 while (b != '\n') | |
301 { | |
302 if (fread(&b, sizeof(b), 1, f) != 1) break; | |
303 } | |
304 | |
305 buf[p] = '\0'; | |
306 cd->have_md5sum = md5_digest_from_text(buf, cd->md5sum); | |
307 | |
308 return TRUE; | |
309 } | |
310 | |
311 return FALSE; | |
312 } | |
313 | |
314 static gint cache_sim_read_similarity(FILE *f, char *buf, int s, CacheData *cd) | |
315 { | |
316 if (!f || !buf || !cd) return FALSE; | |
317 | |
318 if (s < 11 || strncmp("Similarity", buf, 10) != 0) return FALSE; | |
319 | |
320 if (strncmp("Grid[32 x 32]", buf + 10, 13) != 0) return FALSE; | |
321 | |
322 if (fseek(f, - s, SEEK_CUR) == 0) | |
323 { | |
324 char b; | |
325 guint8 pixel_buf[3]; | |
326 ImageSimilarityData *sd; | |
327 gint x, y; | |
328 | |
329 b = 'X'; | |
330 while (b != '=') | |
331 { | |
332 if (fread(&b, sizeof(b), 1, f) != 1) return FALSE; | |
333 } | |
334 | |
335 if (cd->sim) | |
336 { | |
337 /* use current sim that may already contain data we will not touch here */ | |
338 sd = cd->sim; | |
339 cd->sim = NULL; | |
340 cd->similarity = FALSE; | |
341 } | |
342 else | |
343 { | |
344 sd = image_sim_new(); | |
345 } | |
346 | |
347 for (y = 0; y < 32; y++) | |
348 { | |
349 gint s = y * 32; | |
350 for (x = 0; x < 32; x++) | |
351 { | |
352 if (fread(&pixel_buf, sizeof(pixel_buf), 1, f) != 1) | |
353 { | |
354 image_sim_free(sd); | |
355 return FALSE; | |
356 } | |
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]; | |
360 } | |
361 } | |
362 | |
363 if (fread(&b, sizeof(b), 1, f) == 1) | |
364 { | |
365 if (b != '\n') fseek(f, -1, SEEK_CUR); | |
366 } | |
367 | |
368 cd->sim = sd; | |
369 cd->sim->filled = TRUE; | |
370 cd->similarity = TRUE; | |
371 | |
372 return TRUE; | |
373 } | |
374 | |
375 return FALSE; | |
376 } | |
377 | |
378 CacheData *cache_sim_data_load(const gchar *path) | |
379 { | |
380 FILE *f; | |
381 CacheData *cd = NULL; | |
382 char buf[32]; | |
383 gint success = TRUE; | |
384 gchar *pathl; | |
385 | |
386 if (!path) return NULL; | |
387 | |
388 pathl = path_from_utf8(path); | |
389 f = fopen(pathl, "r"); | |
390 g_free(pathl); | |
391 | |
392 if (!f) return NULL; | |
393 | |
394 cd = cache_sim_data_new(); | |
395 cd->path = g_strdup(path); | |
396 | |
397 if (fread(&buf, sizeof(char), 9, f) != 9 || | |
398 strncmp(buf, "SIMcache", 8) != 0) | |
399 { | |
400 if (debug) printf("%s is not a cache file\n", cd->path); | |
401 success = FALSE; | |
402 } | |
403 | |
404 while (success) | |
405 { | |
406 int s; | |
407 s = fread(&buf, sizeof(char), sizeof(buf), f); | |
408 | |
409 if (s < 1) | |
410 { | |
411 success = FALSE; | |
412 } | |
413 else | |
414 { | |
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)) | |
420 { | |
421 success = FALSE; | |
422 } | |
423 } | |
424 } | |
425 | |
426 fclose(f); | |
427 | |
428 if (!cd->dimensions && !cd->similarity) | |
429 { | |
430 cache_sim_data_free(cd); | |
431 cd = NULL; | |
432 } | |
433 | |
434 return cd; | |
435 } | |
436 | |
437 /* | |
438 *------------------------------------------------------------------- | |
439 * sim cache setting | |
440 *------------------------------------------------------------------- | |
441 */ | |
442 | |
443 void cache_sim_data_set_dimensions(CacheData *cd, gint w, gint h) | |
444 { | |
445 if (!cd) return; | |
446 | |
447 cd->width = w; | |
448 cd->height = h; | |
449 cd->dimensions = TRUE; | |
450 } | |
451 | |
452 void cache_sim_data_set_checksum(CacheData *cd, long checksum) | |
453 { | |
454 if (!cd) return; | |
455 | |
456 cd->checksum = checksum; | |
457 cd->have_checksum = TRUE; | |
458 } | |
459 | |
460 void cache_sim_data_set_md5sum(CacheData *cd, guchar digest[16]) | |
461 { | |
462 gint i; | |
463 | |
464 if (!cd) return; | |
465 | |
466 for (i = 0; i < 16; i++) | |
467 { | |
468 cd->md5sum[i] = digest[i]; | |
469 } | |
470 cd->have_md5sum = TRUE; | |
471 } | |
472 | |
473 void cache_sim_data_set_similarity(CacheData *cd, ImageSimilarityData *sd) | |
474 { | |
475 if (!cd || !sd || !sd->filled) return; | |
476 | |
477 if (!cd->sim) cd->sim = image_sim_new(); | |
478 | |
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; | |
483 | |
484 cd->similarity = TRUE; | |
485 } | |
486 | |
487 gint cache_sim_data_filled(ImageSimilarityData *sd) | |
488 { | |
489 if (!sd) return FALSE; | |
490 return sd->filled; | |
491 } | |
492 | |
493 /* | |
494 *------------------------------------------------------------------- | |
495 * cache path location utils | |
496 *------------------------------------------------------------------- | |
497 */ | |
498 | |
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) | |
501 { | |
502 if (!path) return FALSE; | |
503 | |
504 if (!isdir(path)) | |
505 { | |
506 gchar *p = path; | |
507 while (p[0] != '\0') | |
508 { | |
509 p++; | |
510 if (p[0] == '/' || p[0] == '\0') | |
511 { | |
512 gint end = TRUE; | |
513 if (p[0] != '\0') | |
514 { | |
515 p[0] = '\0'; | |
516 end = FALSE; | |
517 } | |
518 if (!isdir(path)) | |
519 { | |
520 if (debug) printf("creating sub dir:%s\n", path); | |
521 if (!mkdir_utf8(path, mode)) | |
522 { | |
523 printf("create dir failed: %s\n", path); | |
524 return FALSE; | |
525 } | |
526 } | |
527 if (!end) p[0] = '/'; | |
528 } | |
529 } | |
530 } | |
531 return TRUE; | |
532 } | |
533 | |
534 static void cache_path_parts(CacheType type, | |
535 const gchar **cache_rc, const gchar **cache_local, const gchar **cache_ext) | |
536 { | |
537 switch (type) | |
538 { | |
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; | |
543 break; | |
544 case CACHE_TYPE_SIM: | |
545 *cache_rc = GQVIEW_CACHE_RC_THUMB; | |
546 *cache_local = GQVIEW_CACHE_LOCAL_THUMB; | |
547 *cache_ext = GQVIEW_CACHE_EXT_SIM; | |
548 break; | |
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; | |
553 break; | |
554 } | |
555 } | |
556 | |
557 gchar *cache_get_location(CacheType type, const gchar *source, gint include_name, mode_t *mode) | |
558 { | |
559 gchar *path = NULL; | |
560 gchar *base; | |
561 gchar *name = NULL; | |
562 const gchar *cache_rc; | |
563 const gchar *cache_local; | |
564 const gchar *cache_ext; | |
565 | |
566 if (!source) return NULL; | |
567 | |
568 cache_path_parts(type, &cache_rc, &cache_local, &cache_ext); | |
569 | |
570 base = remove_level_from_path(source); | |
571 if (include_name) | |
572 { | |
573 name = g_strconcat("/", filename_from_path(source), NULL); | |
574 } | |
575 else | |
576 { | |
577 cache_ext = NULL; | |
578 } | |
579 | |
580 if (((type != CACHE_TYPE_METADATA && enable_thumb_dirs) || | |
581 (type == CACHE_TYPE_METADATA && enable_metadata_dirs)) && | |
582 access_file(base, W_OK)) | |
583 { | |
584 path = g_strconcat(base, "/", cache_local, name, cache_ext, NULL); | |
585 if (mode) *mode = 0775; | |
586 } | |
587 | |
588 if (!path) | |
589 { | |
590 path = g_strconcat(homedir(), "/", cache_rc, base, name, cache_ext, NULL); | |
591 if (mode) *mode = 0755; | |
592 } | |
593 | |
594 g_free(base); | |
595 if (name) g_free(name); | |
596 | |
597 return path; | |
598 } | |
599 | |
600 gchar *cache_find_location(CacheType type, const gchar *source) | |
601 { | |
602 gchar *path; | |
603 const gchar *name; | |
604 gchar *base; | |
605 const gchar *cache_rc; | |
606 const gchar *cache_local; | |
607 const gchar *cache_ext; | |
608 gint prefer_local; | |
609 | |
610 if (!source) return NULL; | |
611 | |
612 cache_path_parts(type, &cache_rc, &cache_local, &cache_ext); | |
613 | |
614 name = filename_from_path(source); | |
615 base = remove_level_from_path(source); | |
616 | |
617 if (type == CACHE_TYPE_METADATA) | |
618 { | |
619 prefer_local = enable_metadata_dirs; | |
620 } | |
621 else | |
622 { | |
623 prefer_local = enable_thumb_dirs; | |
624 } | |
625 | |
626 if (prefer_local) | |
627 { | |
628 path = g_strconcat(base, "/", cache_local, "/", name, cache_ext, NULL); | |
629 } | |
630 else | |
631 { | |
632 path = g_strconcat(homedir(), "/", cache_rc, source, cache_ext, NULL); | |
633 } | |
634 | |
635 if (!isfile(path)) | |
636 { | |
637 g_free(path); | |
638 | |
639 /* try the opposite method if not found */ | |
640 if (!prefer_local) | |
641 { | |
642 path = g_strconcat(base, "/", cache_local, "/", name, cache_ext, NULL); | |
643 } | |
644 else | |
645 { | |
646 path = g_strconcat(homedir(), "/", cache_rc, source, cache_ext, NULL); | |
647 } | |
648 | |
649 if (!isfile(path)) | |
650 { | |
651 g_free(path); | |
652 path = NULL; | |
653 } | |
654 } | |
655 | |
656 g_free(base); | |
657 | |
658 return path; | |
659 } | |
660 | |
661 gint cache_time_valid(const gchar *cache, const gchar *path) | |
662 { | |
663 struct stat cache_st; | |
664 struct stat path_st; | |
665 gchar *cachel; | |
666 gchar *pathl; | |
667 gint ret = FALSE; | |
668 | |
669 if (!cache || !path) return FALSE; | |
670 | |
671 cachel = path_from_utf8(cache); | |
672 pathl = path_from_utf8(path); | |
673 | |
674 if (stat(cachel, &cache_st) == 0 && | |
675 stat(pathl, &path_st) == 0) | |
676 { | |
677 if (cache_st.st_mtime == path_st.st_mtime) | |
678 { | |
679 ret = TRUE; | |
680 } | |
681 else if (cache_st.st_mtime > path_st.st_mtime) | |
682 { | |
683 struct utimbuf ut; | |
684 | |
685 ut.actime = ut.modtime = cache_st.st_mtime; | |
686 if (utime(cachel, &ut) < 0 && | |
687 errno == EPERM) | |
688 { | |
689 if (debug) printf("cache permission workaround: %s\n", cachel); | |
690 ret = TRUE; | |
691 } | |
692 } | |
693 } | |
694 | |
695 g_free(pathl); | |
696 g_free(cachel); | |
697 | |
698 return ret; | |
699 } | |
700 |