9
|
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
|