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