]> git.wh0rd.org - fontconfig.git/blob - src/fccache.c
Dont cache directorys until theyve been scanned. Avoids losing subdir
[fontconfig.git] / src / fccache.c
1 /*
2 * $XFree86: xc/lib/fontconfig/src/fccache.c,v 1.12 2002/08/22 07:36:44 keithp Exp $
3 *
4 * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of Keith Packard not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission. Keith Packard makes no
13 * representations about the suitability of this software for any purpose. It
14 * is provided "as is" without express or implied warranty.
15 *
16 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 * PERFORMANCE OF THIS SOFTWARE.
23 */
24
25 #include "fcint.h"
26
27 /*
28 * POSIX has broken stdio so that getc must do thread-safe locking,
29 * this is a serious performance problem for applications doing large
30 * amounts of IO with getc (as is done here). If available, use
31 * the getc_unlocked varient instead.
32 */
33
34 #if defined(getc_unlocked) || defined(_IO_getc_unlocked)
35 #define GETC(f) getc_unlocked(f)
36 #define PUTC(c,f) putc_unlocked(c,f)
37 #else
38 #define GETC(f) getc(f)
39 #define PUTC(c,f) putc(c,f)
40 #endif
41
42 #define FC_DBG_CACHE_REF 1024
43
44 static FcChar8 *
45 FcCacheReadString (FILE *f, FcChar8 *dest, int len)
46 {
47 int c;
48 FcBool escape;
49 FcChar8 *d;
50 int size;
51 int i;
52
53 while ((c = GETC (f)) != EOF)
54 if (c == '"')
55 break;
56 if (c == EOF)
57 return FcFalse;
58 if (len == 0)
59 return FcFalse;
60
61 size = len;
62 i = 0;
63 d = dest;
64 escape = FcFalse;
65 while ((c = GETC (f)) != EOF)
66 {
67 if (!escape)
68 {
69 switch (c) {
70 case '"':
71 c = '\0';
72 break;
73 case '\\':
74 escape = FcTrue;
75 continue;
76 }
77 }
78 if (i == size)
79 {
80 FcChar8 *new = malloc (size * 2); /* freed in caller */
81 if (!new)
82 break;
83 memcpy (new, d, size);
84 size *= 2;
85 if (d != dest)
86 free (d);
87 d = new;
88 }
89 d[i++] = c;
90 if (c == '\0')
91 return d;
92 escape = FcFalse;
93 }
94 if (d != dest)
95 free (d);
96 return 0;
97 }
98
99 static FcBool
100 FcCacheReadUlong (FILE *f, unsigned long *dest)
101 {
102 unsigned long t;
103 int c;
104
105 while ((c = GETC (f)) != EOF)
106 {
107 if (!isspace (c))
108 break;
109 }
110 if (c == EOF)
111 return FcFalse;
112 t = 0;
113 for (;;)
114 {
115 if (c == EOF || isspace (c))
116 break;
117 if (!isdigit (c))
118 return FcFalse;
119 t = t * 10 + (c - '0');
120 c = GETC (f);
121 }
122 *dest = t;
123 return FcTrue;
124 }
125
126 static FcBool
127 FcCacheReadInt (FILE *f, int *dest)
128 {
129 unsigned long t;
130 FcBool ret;
131
132 ret = FcCacheReadUlong (f, &t);
133 if (ret)
134 *dest = (int) t;
135 return ret;
136 }
137
138 static FcBool
139 FcCacheReadTime (FILE *f, time_t *dest)
140 {
141 unsigned long t;
142 FcBool ret;
143
144 ret = FcCacheReadUlong (f, &t);
145 if (ret)
146 *dest = (time_t) t;
147 return ret;
148 }
149
150 static FcBool
151 FcCacheWriteChars (FILE *f, const FcChar8 *chars)
152 {
153 FcChar8 c;
154 while ((c = *chars++))
155 {
156 switch (c) {
157 case '"':
158 case '\\':
159 if (PUTC ('\\', f) == EOF)
160 return FcFalse;
161 /* fall through */
162 default:
163 if (PUTC (c, f) == EOF)
164 return FcFalse;
165 }
166 }
167 return FcTrue;
168 }
169
170 static FcBool
171 FcCacheWriteString (FILE *f, const FcChar8 *string)
172 {
173
174 if (PUTC ('"', f) == EOF)
175 return FcFalse;
176 if (!FcCacheWriteChars (f, string))
177 return FcFalse;
178 if (PUTC ('"', f) == EOF)
179 return FcFalse;
180 return FcTrue;
181 }
182
183 static FcBool
184 FcCacheWritePath (FILE *f, const FcChar8 *dir, const FcChar8 *file)
185 {
186 if (PUTC ('"', f) == EOF)
187 return FcFalse;
188 if (dir)
189 if (!FcCacheWriteChars (f, dir))
190 return FcFalse;
191 if (dir && dir[strlen((const char *) dir) - 1] != '/')
192 if (PUTC ('/', f) == EOF)
193 return FcFalse;
194 if (!FcCacheWriteChars (f, file))
195 return FcFalse;
196 if (PUTC ('"', f) == EOF)
197 return FcFalse;
198 return FcTrue;
199 }
200
201 static FcBool
202 FcCacheWriteUlong (FILE *f, unsigned long t)
203 {
204 int pow;
205 unsigned long temp, digit;
206
207 temp = t;
208 pow = 1;
209 while (temp >= 10)
210 {
211 temp /= 10;
212 pow *= 10;
213 }
214 temp = t;
215 while (pow)
216 {
217 digit = temp / pow;
218 if (PUTC ((char) digit + '0', f) == EOF)
219 return FcFalse;
220 temp = temp - pow * digit;
221 pow = pow / 10;
222 }
223 return FcTrue;
224 }
225
226 static FcBool
227 FcCacheWriteInt (FILE *f, int i)
228 {
229 return FcCacheWriteUlong (f, (unsigned long) i);
230 }
231
232 static FcBool
233 FcCacheWriteTime (FILE *f, time_t t)
234 {
235 return FcCacheWriteUlong (f, (unsigned long) t);
236 }
237
238 static FcBool
239 FcCacheFontSetAdd (FcFontSet *set,
240 FcStrSet *dirs,
241 const FcChar8 *dir,
242 int dir_len,
243 const FcChar8 *file,
244 const FcChar8 *name)
245 {
246 FcChar8 path_buf[8192], *path;
247 int len;
248 FcBool ret = FcFalse;
249 FcPattern *font;
250 FcPattern *frozen;
251
252 path = path_buf;
253 len = (dir_len + 1 + strlen ((const char *) file) + 1);
254 if (len > sizeof (path_buf))
255 {
256 path = malloc (len); /* freed down below */
257 if (!path)
258 return FcFalse;
259 }
260 strncpy ((char *) path, (const char *) dir, dir_len);
261 if (dir[dir_len - 1] != '/')
262 path[dir_len++] = '/';
263 strcpy ((char *) path + dir_len, (const char *) file);
264 if (!FcStrCmp (name, FC_FONT_FILE_DIR))
265 {
266 if (FcDebug () & FC_DBG_CACHEV)
267 printf (" dir cache dir \"%s\"\n", path);
268 ret = FcStrSetAdd (dirs, path);
269 }
270 else if (!FcStrCmp (name, FC_FONT_FILE_INVALID))
271 {
272 ret = FcTrue;
273 }
274 else
275 {
276 font = FcNameParse (name);
277 if (font)
278 {
279 if (FcDebug () & FC_DBG_CACHEV)
280 printf (" dir cache file \"%s\"\n", file);
281 ret = FcPatternAddString (font, FC_FILE, path);
282 if (ret)
283 {
284 frozen = FcPatternFreeze (font);
285 ret = (frozen != 0);
286 if (ret)
287 ret = FcFontSetAdd (set, frozen);
288 }
289 FcPatternDestroy (font);
290 }
291 }
292 if (path != path_buf) free (path);
293 return ret;
294
295 }
296
297 static unsigned int
298 FcCacheHash (const FcChar8 *string, int len)
299 {
300 unsigned int h = 0;
301 FcChar8 c;
302
303 while (len-- && (c = *string++))
304 h = (h << 1) ^ c;
305 return h;
306 }
307
308 /*
309 * Verify the saved timestamp for a file
310 */
311 FcBool
312 FcGlobalCacheCheckTime (FcGlobalCacheInfo *info)
313 {
314 struct stat statb;
315
316 if (stat ((char *) info->file, &statb) < 0)
317 {
318 if (FcDebug () & FC_DBG_CACHE)
319 printf (" file missing\n");
320 return FcFalse;
321 }
322 if (statb.st_mtime != info->time)
323 {
324 if (FcDebug () & FC_DBG_CACHE)
325 printf (" timestamp mismatch (was %d is %d)\n",
326 (int) info->time, (int) statb.st_mtime);
327 return FcFalse;
328 }
329 return FcTrue;
330 }
331
332 void
333 FcGlobalCacheReferenced (FcGlobalCache *cache,
334 FcGlobalCacheInfo *info)
335 {
336 if (!info->referenced)
337 {
338 info->referenced = FcTrue;
339 cache->referenced++;
340 if (FcDebug () & FC_DBG_CACHE_REF)
341 printf ("Reference %d %s\n", cache->referenced, info->file);
342 }
343 }
344
345 /*
346 * Break a path into dir/base elements and compute the base hash
347 * and the dir length. This is shared between the functions
348 * which walk the file caches
349 */
350
351 typedef struct _FcFilePathInfo {
352 const FcChar8 *dir;
353 int dir_len;
354 const FcChar8 *base;
355 unsigned int base_hash;
356 } FcFilePathInfo;
357
358 static FcFilePathInfo
359 FcFilePathInfoGet (const FcChar8 *path)
360 {
361 FcFilePathInfo i;
362 FcChar8 *slash;
363
364 slash = (FcChar8 *) strrchr ((const char *) path, '/');
365 if (slash)
366 {
367 i.dir = path;
368 i.dir_len = slash - path;
369 if (!i.dir_len)
370 i.dir_len = 1;
371 i.base = slash + 1;
372 }
373 else
374 {
375 i.dir = (const FcChar8 *) ".";
376 i.dir_len = 1;
377 i.base = path;
378 }
379 i.base_hash = FcCacheHash (i.base, -1);
380 return i;
381 }
382
383 FcGlobalCacheDir *
384 FcGlobalCacheDirGet (FcGlobalCache *cache,
385 const FcChar8 *dir,
386 int len,
387 FcBool create_missing)
388 {
389 unsigned int hash = FcCacheHash (dir, len);
390 FcGlobalCacheDir *d, **prev;
391
392 for (prev = &cache->ents[hash % FC_GLOBAL_CACHE_DIR_HASH_SIZE];
393 (d = *prev);
394 prev = &(*prev)->next)
395 {
396 if (d->info.hash == hash && d->len == len &&
397 !strncmp ((const char *) d->info.file,
398 (const char *) dir, len))
399 break;
400 }
401 if (!(d = *prev))
402 {
403 int i;
404 if (!create_missing)
405 return 0;
406 d = malloc (sizeof (FcGlobalCacheDir) + len + 1);
407 if (!d)
408 return 0;
409 FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCacheDir) + len + 1);
410 d->next = *prev;
411 *prev = d;
412 d->info.hash = hash;
413 d->info.file = (FcChar8 *) (d + 1);
414 strncpy ((char *) d->info.file, (const char *) dir, len);
415 d->info.file[len] = '\0';
416 d->info.time = 0;
417 d->info.referenced = FcFalse;
418 d->len = len;
419 for (i = 0; i < FC_GLOBAL_CACHE_FILE_HASH_SIZE; i++)
420 d->ents[i] = 0;
421 d->subdirs = 0;
422 }
423 return d;
424 }
425
426 static FcGlobalCacheInfo *
427 FcGlobalCacheDirAdd (FcGlobalCache *cache,
428 const FcChar8 *dir,
429 time_t time,
430 FcBool replace)
431 {
432 FcGlobalCacheDir *d;
433 FcFilePathInfo i;
434 FcGlobalCacheSubdir *subdir;
435 FcGlobalCacheDir *parent;
436
437 /*
438 * Add this directory to the cache
439 */
440 d = FcGlobalCacheDirGet (cache, dir, strlen ((const char *) dir), FcTrue);
441 if (!d)
442 return 0;
443 d->info.time = time;
444 i = FcFilePathInfoGet (dir);
445 /*
446 * Add this directory to the subdirectory list of the parent
447 */
448 parent = FcGlobalCacheDirGet (cache, i.dir, i.dir_len, FcTrue);
449 if (!parent)
450 return 0;
451 subdir = malloc (sizeof (FcGlobalCacheSubdir));
452 if (!subdir)
453 return 0;
454 FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCacheSubdir));
455 subdir->ent = d;
456 subdir->next = parent->subdirs;
457 parent->subdirs = subdir;
458 return &d->info;
459 }
460
461 static void
462 FcGlobalCacheDirDestroy (FcGlobalCacheDir *d)
463 {
464 FcGlobalCacheFile *f, *next;
465 int h;
466 FcGlobalCacheSubdir *s, *nexts;
467
468 for (h = 0; h < FC_GLOBAL_CACHE_FILE_HASH_SIZE; h++)
469 for (f = d->ents[h]; f; f = next)
470 {
471 next = f->next;
472 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheFile) +
473 strlen ((char *) f->info.file) + 1 +
474 strlen ((char *) f->name) + 1);
475 free (f);
476 }
477 for (s = d->subdirs; s; s = nexts)
478 {
479 nexts = s->next;
480 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheSubdir));
481 free (s);
482 }
483 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheDir) + d->len + 1);
484 free (d);
485 }
486
487 /*
488 * Check to see if the global cache contains valid data for 'dir'.
489 * If so, scan the global cache for files and directories in 'dir'.
490 * else, return False.
491 */
492 FcBool
493 FcGlobalCacheScanDir (FcFontSet *set,
494 FcStrSet *dirs,
495 FcGlobalCache *cache,
496 const FcChar8 *dir)
497 {
498 FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, dir,
499 strlen ((const char *) dir),
500 FcFalse);
501 FcGlobalCacheFile *f;
502 int h;
503 int dir_len;
504 FcGlobalCacheSubdir *subdir;
505 FcBool any_in_cache = FcFalse;
506
507 if (FcDebug() & FC_DBG_CACHE)
508 printf ("FcGlobalCacheScanDir %s\n", dir);
509
510 if (!d)
511 {
512 if (FcDebug () & FC_DBG_CACHE)
513 printf ("\tNo dir cache entry\n");
514 return FcFalse;
515 }
516
517 /*
518 * See if the timestamp recorded in the global cache
519 * matches the directory time, if not, return False
520 */
521 if (!FcGlobalCacheCheckTime (&d->info))
522 {
523 if (FcDebug () & FC_DBG_CACHE)
524 printf ("\tdir cache entry time mismatch\n");
525 return FcFalse;
526 }
527
528 /*
529 * Add files from 'dir' to the fontset
530 */
531 dir_len = strlen ((const char *) dir);
532 for (h = 0; h < FC_GLOBAL_CACHE_FILE_HASH_SIZE; h++)
533 for (f = d->ents[h]; f; f = f->next)
534 {
535 if (FcDebug() & FC_DBG_CACHEV)
536 printf ("FcGlobalCacheScanDir add file %s\n", f->info.file);
537 any_in_cache = FcTrue;
538 if (!FcCacheFontSetAdd (set, dirs, dir, dir_len,
539 f->info.file, f->name))
540 {
541 cache->broken = FcTrue;
542 return FcFalse;
543 }
544 FcGlobalCacheReferenced (cache, &f->info);
545 }
546 /*
547 * Add directories in 'dir' to 'dirs'
548 */
549 for (subdir = d->subdirs; subdir; subdir = subdir->next)
550 {
551 FcFilePathInfo info = FcFilePathInfoGet (subdir->ent->info.file);
552
553 any_in_cache = FcTrue;
554 if (!FcCacheFontSetAdd (set, dirs, dir, dir_len,
555 info.base, FC_FONT_FILE_DIR))
556 {
557 cache->broken = FcTrue;
558 return FcFalse;
559 }
560 FcGlobalCacheReferenced (cache, &subdir->ent->info);
561 }
562
563 FcGlobalCacheReferenced (cache, &d->info);
564
565 /*
566 * To recover from a bug in previous versions of fontconfig,
567 * return FcFalse if no entries in the cache were found
568 * for this directory. This will cause any empty directories
569 * to get rescanned every time fontconfig is initialized. This
570 * might get removed at some point when the older cache files are
571 * presumably fixed.
572 */
573 return any_in_cache;
574 }
575
576 /*
577 * Locate the cache entry for a particular file
578 */
579 FcGlobalCacheFile *
580 FcGlobalCacheFileGet (FcGlobalCache *cache,
581 const FcChar8 *file,
582 int id,
583 int *count)
584 {
585 FcFilePathInfo i = FcFilePathInfoGet (file);
586 FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, i.dir,
587 i.dir_len, FcFalse);
588 FcGlobalCacheFile *f, *match = 0;
589 int max = -1;
590
591 if (!d)
592 return 0;
593 for (f = d->ents[i.base_hash % FC_GLOBAL_CACHE_FILE_HASH_SIZE]; f; f = f->next)
594 {
595 if (f->info.hash == i.base_hash &&
596 !strcmp ((const char *) f->info.file, (const char *) i.base))
597 {
598 if (f->id == id)
599 match = f;
600 if (f->id > max)
601 max = f->id;
602 }
603 }
604 if (count)
605 *count = max;
606 return match;
607 }
608
609 /*
610 * Add a file entry to the cache
611 */
612 static FcGlobalCacheInfo *
613 FcGlobalCacheFileAdd (FcGlobalCache *cache,
614 const FcChar8 *path,
615 int id,
616 time_t time,
617 const FcChar8 *name,
618 FcBool replace)
619 {
620 FcFilePathInfo i = FcFilePathInfoGet (path);
621 FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, i.dir,
622 i.dir_len, FcTrue);
623 FcGlobalCacheFile *f, **prev;
624 int size;
625
626 if (!d)
627 return 0;
628 for (prev = &d->ents[i.base_hash % FC_GLOBAL_CACHE_FILE_HASH_SIZE];
629 (f = *prev);
630 prev = &(*prev)->next)
631 {
632 if (f->info.hash == i.base_hash &&
633 f->id == id &&
634 !strcmp ((const char *) f->info.file, (const char *) i.base))
635 {
636 break;
637 }
638 }
639 if (*prev)
640 {
641 if (!replace)
642 return 0;
643
644 f = *prev;
645 if (f->info.referenced)
646 cache->referenced--;
647 *prev = f->next;
648 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheFile) +
649 strlen ((char *) f->info.file) + 1 +
650 strlen ((char *) f->name) + 1);
651 free (f);
652 }
653 size = (sizeof (FcGlobalCacheFile) +
654 strlen ((char *) i.base) + 1 +
655 strlen ((char *) name) + 1);
656 f = malloc (size);
657 if (!f)
658 return 0;
659 FcMemAlloc (FC_MEM_CACHE, size);
660 f->next = *prev;
661 *prev = f;
662 f->info.hash = i.base_hash;
663 f->info.file = (FcChar8 *) (f + 1);
664 f->info.time = time;
665 f->info.referenced = FcFalse;
666 f->id = id;
667 f->name = f->info.file + strlen ((char *) i.base) + 1;
668 strcpy ((char *) f->info.file, (const char *) i.base);
669 strcpy ((char *) f->name, (const char *) name);
670 return &f->info;
671 }
672
673 FcGlobalCache *
674 FcGlobalCacheCreate (void)
675 {
676 FcGlobalCache *cache;
677 int h;
678
679 cache = malloc (sizeof (FcGlobalCache));
680 if (!cache)
681 return 0;
682 FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache));
683 for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
684 cache->ents[h] = 0;
685 cache->entries = 0;
686 cache->referenced = 0;
687 cache->updated = FcFalse;
688 cache->broken = FcFalse;
689 return cache;
690 }
691
692 void
693 FcGlobalCacheDestroy (FcGlobalCache *cache)
694 {
695 FcGlobalCacheDir *d, *next;
696 int h;
697
698 for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
699 {
700 for (d = cache->ents[h]; d; d = next)
701 {
702 next = d->next;
703 FcGlobalCacheDirDestroy (d);
704 }
705 }
706 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache));
707 free (cache);
708 }
709
710 /*
711 * Cache file syntax is quite simple:
712 *
713 * "file_name" id time "font_name" \n
714 */
715
716 void
717 FcGlobalCacheLoad (FcGlobalCache *cache,
718 const FcChar8 *cache_file)
719 {
720 FILE *f;
721 FcChar8 file_buf[8192], *file;
722 int id;
723 time_t time;
724 FcChar8 name_buf[8192], *name;
725 FcGlobalCacheInfo *info;
726
727 f = fopen ((char *) cache_file, "r");
728 if (!f)
729 return;
730
731 cache->updated = FcFalse;
732 file = 0;
733 name = 0;
734 while ((file = FcCacheReadString (f, file_buf, sizeof (file_buf))) &&
735 FcCacheReadInt (f, &id) &&
736 FcCacheReadTime (f, &time) &&
737 (name = FcCacheReadString (f, name_buf, sizeof (name_buf))))
738 {
739 if (FcDebug () & FC_DBG_CACHEV)
740 printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file, name);
741 if (!FcStrCmp (name, FC_FONT_FILE_DIR))
742 info = FcGlobalCacheDirAdd (cache, file, time, FcFalse);
743 else
744 info = FcGlobalCacheFileAdd (cache, file, id, time, name, FcFalse);
745 if (!info)
746 cache->broken = FcTrue;
747 else
748 cache->entries++;
749 if (FcDebug () & FC_DBG_CACHE_REF)
750 printf ("FcGlobalCacheLoad entry %d %s\n",
751 cache->entries, file);
752 if (file != file_buf)
753 free (file);
754 if (name != name_buf)
755 free (name);
756 file = 0;
757 name = 0;
758 }
759 if (file && file != file_buf)
760 free (file);
761 if (name && name != name_buf)
762 free (name);
763 fclose (f);
764 }
765
766 FcBool
767 FcGlobalCacheUpdate (FcGlobalCache *cache,
768 const FcChar8 *file,
769 int id,
770 const FcChar8 *name)
771 {
772 const FcChar8 *match;
773 struct stat statb;
774 FcGlobalCacheInfo *info;
775
776 match = file;
777
778 if (stat ((char *) file, &statb) < 0)
779 return FcFalse;
780 if (S_ISDIR (statb.st_mode))
781 info = FcGlobalCacheDirAdd (cache, file, statb.st_mtime,
782 FcTrue);
783 else
784 info = FcGlobalCacheFileAdd (cache, file, id, statb.st_mtime,
785 name, FcTrue);
786 if (info)
787 {
788 FcGlobalCacheReferenced (cache, info);
789 cache->updated = FcTrue;
790 }
791 else
792 cache->broken = FcTrue;
793 return info != 0;
794 }
795
796 FcBool
797 FcGlobalCacheSave (FcGlobalCache *cache,
798 const FcChar8 *cache_file)
799 {
800 FILE *f;
801 int dir_hash, file_hash;
802 FcGlobalCacheDir *dir;
803 FcGlobalCacheFile *file;
804 FcAtomic *atomic;
805
806 if (!cache->updated && cache->referenced == cache->entries)
807 return FcTrue;
808
809 if (cache->broken)
810 return FcFalse;
811
812 /* Set-UID programs can't safely update the cache */
813 if (getuid () != geteuid ())
814 return FcFalse;
815
816 atomic = FcAtomicCreate (cache_file);
817 if (!atomic)
818 goto bail0;
819 if (!FcAtomicLock (atomic))
820 goto bail1;
821 f = fopen ((char *) FcAtomicNewFile(atomic), "w");
822 if (!f)
823 goto bail2;
824
825 for (dir_hash = 0; dir_hash < FC_GLOBAL_CACHE_DIR_HASH_SIZE; dir_hash++)
826 {
827 for (dir = cache->ents[dir_hash]; dir; dir = dir->next)
828 {
829 if (!dir->info.referenced)
830 continue;
831 if (!FcCacheWriteString (f, dir->info.file))
832 goto bail4;
833 if (PUTC (' ', f) == EOF)
834 goto bail4;
835 if (!FcCacheWriteInt (f, 0))
836 goto bail4;
837 if (PUTC (' ', f) == EOF)
838 goto bail4;
839 if (!FcCacheWriteTime (f, dir->info.time))
840 goto bail4;
841 if (PUTC (' ', f) == EOF)
842 goto bail4;
843 if (!FcCacheWriteString (f, (FcChar8 *) FC_FONT_FILE_DIR))
844 goto bail4;
845 if (PUTC ('\n', f) == EOF)
846 goto bail4;
847
848 for (file_hash = 0; file_hash < FC_GLOBAL_CACHE_FILE_HASH_SIZE; file_hash++)
849 {
850 for (file = dir->ents[file_hash]; file; file = file->next)
851 {
852 if (!file->info.referenced)
853 continue;
854 if (!FcCacheWritePath (f, dir->info.file, file->info.file))
855 goto bail4;
856 if (PUTC (' ', f) == EOF)
857 goto bail4;
858 if (!FcCacheWriteInt (f, file->id < 0 ? 0 : file->id))
859 goto bail4;
860 if (PUTC (' ', f) == EOF)
861 goto bail4;
862 if (!FcCacheWriteTime (f, file->info.time))
863 goto bail4;
864 if (PUTC (' ', f) == EOF)
865 goto bail4;
866 if (!FcCacheWriteString (f, file->name))
867 goto bail4;
868 if (PUTC ('\n', f) == EOF)
869 goto bail4;
870 }
871 }
872 }
873 }
874
875 if (fclose (f) == EOF)
876 goto bail3;
877
878 if (!FcAtomicReplaceOrig (atomic))
879 goto bail3;
880
881 FcAtomicUnlock (atomic);
882 FcAtomicDestroy (atomic);
883
884 cache->updated = FcFalse;
885 return FcTrue;
886
887 bail4:
888 fclose (f);
889 bail3:
890 FcAtomicDeleteNew (atomic);
891 bail2:
892 FcAtomicUnlock (atomic);
893 bail1:
894 FcAtomicDestroy (atomic);
895 bail0:
896 return FcFalse;
897 }
898
899 FcBool
900 FcDirCacheValid (const FcChar8 *dir)
901 {
902 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
903 struct stat file_stat, dir_stat;
904
905 if (stat ((char *) dir, &dir_stat) < 0)
906 {
907 FcStrFree (cache_file);
908 return FcFalse;
909 }
910 if (stat ((char *) cache_file, &file_stat) < 0)
911 {
912 FcStrFree (cache_file);
913 return FcFalse;
914 }
915 FcStrFree (cache_file);
916 /*
917 * If the directory has been modified more recently than
918 * the cache file, the cache is not valid
919 */
920 if (dir_stat.st_mtime - file_stat.st_mtime > 0)
921 return FcFalse;
922 return FcTrue;
923 }
924
925 FcBool
926 FcDirCacheReadDir (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
927 {
928 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
929 FILE *f;
930 FcChar8 *base;
931 int id;
932 int dir_len;
933 FcChar8 file_buf[8192], *file;
934 FcChar8 name_buf[8192], *name;
935 FcBool ret = FcFalse;
936
937 if (!cache_file)
938 goto bail0;
939
940 if (FcDebug () & FC_DBG_CACHE)
941 printf ("FcDirCacheReadDir cache_file \"%s\"\n", cache_file);
942
943 f = fopen ((char *) cache_file, "r");
944 if (!f)
945 {
946 if (FcDebug () & FC_DBG_CACHE)
947 printf (" no cache file\n");
948 goto bail1;
949 }
950
951 if (!FcDirCacheValid (dir))
952 {
953 if (FcDebug () & FC_DBG_CACHE)
954 printf (" cache file older than directory\n");
955 goto bail2;
956 }
957
958 base = (FcChar8 *) strrchr ((char *) cache_file, '/');
959 if (!base)
960 goto bail2;
961 base++;
962 dir_len = base - cache_file;
963
964 file = 0;
965 name = 0;
966 while ((file = FcCacheReadString (f, file_buf, sizeof (file_buf))) &&
967 FcCacheReadInt (f, &id) &&
968 (name = FcCacheReadString (f, name_buf, sizeof (name_buf))))
969 {
970 if (!FcCacheFontSetAdd (set, dirs, cache_file, dir_len,
971 file, name))
972 goto bail3;
973 if (file != file_buf)
974 free (file);
975 if (name != name_buf)
976 free (name);
977 file = name = 0;
978 }
979 if (FcDebug () & FC_DBG_CACHE)
980 printf (" cache loaded\n");
981
982 ret = FcTrue;
983 bail3:
984 if (file && file != file_buf)
985 free (file);
986 if (name && name != name_buf)
987 free (name);
988 bail2:
989 fclose (f);
990 bail1:
991 FcStrFree (cache_file);
992 bail0:
993 return ret;
994 }
995
996 /*
997 * return the path from the directory containing 'cache' to 'file'
998 */
999
1000 static const FcChar8 *
1001 FcFileBaseName (const FcChar8 *cache, const FcChar8 *file)
1002 {
1003 const FcChar8 *cache_slash;
1004
1005 cache_slash = (const FcChar8 *) strrchr ((const char *) cache, '/');
1006 if (cache_slash && !strncmp ((const char *) cache, (const char *) file,
1007 (cache_slash + 1) - cache))
1008 return file + ((cache_slash + 1) - cache);
1009 return file;
1010 }
1011
1012 FcBool
1013 FcDirCacheWriteDir (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
1014 {
1015 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
1016 FcPattern *font;
1017 FILE *f;
1018 FcChar8 *name;
1019 const FcChar8 *file, *base;
1020 int n;
1021 int id;
1022 FcBool ret;
1023 FcStrList *list;
1024
1025 if (!cache_file)
1026 goto bail0;
1027 if (FcDebug () & FC_DBG_CACHE)
1028 printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file);
1029
1030 f = fopen ((char *) cache_file, "w");
1031 if (!f)
1032 {
1033 if (FcDebug () & FC_DBG_CACHE)
1034 printf (" can't create \"%s\"\n", cache_file);
1035 goto bail1;
1036 }
1037
1038 list = FcStrListCreate (dirs);
1039 if (!list)
1040 goto bail2;
1041
1042 while ((dir = FcStrListNext (list)))
1043 {
1044 base = FcFileBaseName (cache_file, dir);
1045 if (!FcCacheWriteString (f, base))
1046 goto bail3;
1047 if (PUTC (' ', f) == EOF)
1048 goto bail3;
1049 if (!FcCacheWriteInt (f, 0))
1050 goto bail3;
1051 if (PUTC (' ', f) == EOF)
1052 goto bail3;
1053 if (!FcCacheWriteString (f, FC_FONT_FILE_DIR))
1054 goto bail3;
1055 if (PUTC ('\n', f) == EOF)
1056 goto bail3;
1057 }
1058
1059 for (n = 0; n < set->nfont; n++)
1060 {
1061 font = set->fonts[n];
1062 if (FcPatternGetString (font, FC_FILE, 0, (FcChar8 **) &file) != FcResultMatch)
1063 goto bail3;
1064 base = FcFileBaseName (cache_file, file);
1065 if (FcPatternGetInteger (font, FC_INDEX, 0, &id) != FcResultMatch)
1066 goto bail3;
1067 if (FcDebug () & FC_DBG_CACHEV)
1068 printf (" write file \"%s\"\n", base);
1069 if (!FcCacheWriteString (f, base))
1070 goto bail3;
1071 if (PUTC (' ', f) == EOF)
1072 goto bail3;
1073 if (!FcCacheWriteInt (f, id))
1074 goto bail3;
1075 if (PUTC (' ', f) == EOF)
1076 goto bail3;
1077 name = FcNameUnparse (font);
1078 if (!name)
1079 goto bail3;
1080 ret = FcCacheWriteString (f, name);
1081 FcStrFree (name);
1082 if (!ret)
1083 goto bail3;
1084 if (PUTC ('\n', f) == EOF)
1085 goto bail3;
1086 }
1087
1088 FcStrListDone (list);
1089
1090 if (fclose (f) == EOF)
1091 goto bail1;
1092
1093 FcStrFree (cache_file);
1094
1095 if (FcDebug () & FC_DBG_CACHE)
1096 printf (" cache written\n");
1097 return FcTrue;
1098
1099 bail3:
1100 FcStrListDone (list);
1101 bail2:
1102 fclose (f);
1103 bail1:
1104 unlink ((char *) cache_file);
1105 FcStrFree (cache_file);
1106 bail0:
1107 return FcFalse;
1108 }