]> git.wh0rd.org - fontconfig.git/blob - src/fccache.c
3bf0de9c3fcfb4c0066058c602d99c6eecb482e9
[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)
299 {
300 unsigned int h = 0;
301 FcChar8 c;
302
303 while ((c = *string++))
304 h = (h << 1) ^ c;
305 return 0;
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);
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);
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 strlen ((const char *) i.base) + 1);
453 if (!subdir)
454 return 0;
455 FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCacheSubdir) +
456 strlen ((const char *) i.base) + 1);
457 subdir->file = (FcChar8 *) (subdir + 1);
458 strcpy ((char *) subdir->file, (const char *) i.base);
459 subdir->next = parent->subdirs;
460 parent->subdirs = subdir;
461 return &d->info;
462 }
463
464 static void
465 FcGlobalCacheDirDestroy (FcGlobalCacheDir *d)
466 {
467 FcGlobalCacheFile *f, *next;
468 int h;
469 FcGlobalCacheSubdir *s, *nexts;
470
471 for (h = 0; h < FC_GLOBAL_CACHE_FILE_HASH_SIZE; h++)
472 for (f = d->ents[h]; f; f = next)
473 {
474 next = f->next;
475 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheFile) +
476 strlen ((char *) f->info.file) + 1 +
477 strlen ((char *) f->name) + 1);
478 free (f);
479 }
480 for (s = d->subdirs; s; s = nexts)
481 {
482 nexts = s->next;
483 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheSubdir) +
484 strlen ((char *) s->file) + 1);
485 free (s);
486 }
487 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheDir) + d->len + 1);
488 free (d);
489 }
490
491 FcBool
492 FcGlobalCacheScanDir (FcFontSet *set,
493 FcStrSet *dirs,
494 FcGlobalCache *cache,
495 const FcChar8 *dir)
496 {
497 FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, dir,
498 strlen ((const char *) dir),
499 FcFalse);
500 FcGlobalCacheFile *f;
501 int h;
502 int dir_len;
503 FcGlobalCacheSubdir *subdir;
504
505 if (FcDebug() & FC_DBG_CACHE)
506 printf ("FcGlobalCacheScanDir %s\n", dir);
507
508 if (!d)
509 {
510 if (FcDebug () & FC_DBG_CACHE)
511 printf ("\tNo dir cache entry\n");
512 return FcFalse;
513 }
514
515 if (!FcGlobalCacheCheckTime (&d->info))
516 {
517 if (FcDebug () & FC_DBG_CACHE)
518 printf ("\tdir cache entry time mismatch\n");
519 return FcFalse;
520 }
521
522 dir_len = strlen ((const char *) dir);
523 for (h = 0; h < FC_GLOBAL_CACHE_FILE_HASH_SIZE; h++)
524 for (f = d->ents[h]; f; f = f->next)
525 {
526 if (FcDebug() & FC_DBG_CACHEV)
527 printf ("FcGlobalCacheScanDir add file %s\n", f->info.file);
528 if (!FcCacheFontSetAdd (set, dirs, dir, dir_len,
529 f->info.file, f->name))
530 {
531 cache->broken = FcTrue;
532 return FcFalse;
533 }
534 FcGlobalCacheReferenced (cache, &f->info);
535 }
536 for (subdir = d->subdirs; subdir; subdir = subdir->next)
537 {
538 if (!FcCacheFontSetAdd (set, dirs, dir, dir_len,
539 subdir->file, FC_FONT_FILE_DIR))
540 {
541 cache->broken = FcTrue;
542 return FcFalse;
543 }
544 }
545
546 FcGlobalCacheReferenced (cache, &d->info);
547
548 return FcTrue;
549 }
550
551 /*
552 * Locate the cache entry for a particular file
553 */
554 FcGlobalCacheFile *
555 FcGlobalCacheFileGet (FcGlobalCache *cache,
556 const FcChar8 *file,
557 int id,
558 int *count)
559 {
560 FcFilePathInfo i = FcFilePathInfoGet (file);
561 FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, i.dir,
562 i.dir_len, FcFalse);
563 FcGlobalCacheFile *f, *match = 0;
564 int max = -1;
565
566 if (!d)
567 return 0;
568 for (f = d->ents[i.base_hash % FC_GLOBAL_CACHE_FILE_HASH_SIZE]; f; f = f->next)
569 {
570 if (f->info.hash == i.base_hash &&
571 !strcmp ((const char *) f->info.file, (const char *) i.base))
572 {
573 if (f->id == id)
574 match = f;
575 if (f->id > max)
576 max = f->id;
577 }
578 }
579 if (count)
580 *count = max;
581 return match;
582 }
583
584 /*
585 * Add a file entry to the cache
586 */
587 static FcGlobalCacheInfo *
588 FcGlobalCacheFileAdd (FcGlobalCache *cache,
589 const FcChar8 *path,
590 int id,
591 time_t time,
592 const FcChar8 *name,
593 FcBool replace)
594 {
595 FcFilePathInfo i = FcFilePathInfoGet (path);
596 FcGlobalCacheDir *d = FcGlobalCacheDirGet (cache, i.dir,
597 i.dir_len, FcTrue);
598 FcGlobalCacheFile *f, **prev;
599 int size;
600
601 if (!d)
602 return 0;
603 for (prev = &d->ents[i.base_hash % FC_GLOBAL_CACHE_FILE_HASH_SIZE];
604 (f = *prev);
605 prev = &(*prev)->next)
606 {
607 if (f->info.hash == i.base_hash &&
608 f->id == id &&
609 !strcmp ((const char *) f->info.file, (const char *) i.base))
610 {
611 break;
612 }
613 }
614 if (*prev)
615 {
616 if (!replace)
617 return 0;
618
619 f = *prev;
620 if (f->info.referenced)
621 cache->referenced--;
622 *prev = f->next;
623 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheFile) +
624 strlen ((char *) f->info.file) + 1 +
625 strlen ((char *) f->name) + 1);
626 free (f);
627 }
628 size = (sizeof (FcGlobalCacheFile) +
629 strlen ((char *) i.base) + 1 +
630 strlen ((char *) name) + 1);
631 f = malloc (size);
632 if (!f)
633 return 0;
634 FcMemAlloc (FC_MEM_CACHE, size);
635 f->next = *prev;
636 *prev = f;
637 f->info.hash = i.base_hash;
638 f->info.file = (FcChar8 *) (f + 1);
639 f->info.time = time;
640 f->info.referenced = FcFalse;
641 f->id = id;
642 f->name = f->info.file + strlen ((char *) i.base) + 1;
643 strcpy ((char *) f->info.file, (const char *) i.base);
644 strcpy ((char *) f->name, (const char *) name);
645 return &f->info;
646 }
647
648 FcGlobalCache *
649 FcGlobalCacheCreate (void)
650 {
651 FcGlobalCache *cache;
652 int h;
653
654 cache = malloc (sizeof (FcGlobalCache));
655 if (!cache)
656 return 0;
657 FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache));
658 for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
659 cache->ents[h] = 0;
660 cache->entries = 0;
661 cache->referenced = 0;
662 cache->updated = FcFalse;
663 cache->broken = FcFalse;
664 return cache;
665 }
666
667 void
668 FcGlobalCacheDestroy (FcGlobalCache *cache)
669 {
670 FcGlobalCacheDir *d, *next;
671 int h;
672
673 for (h = 0; h < FC_GLOBAL_CACHE_DIR_HASH_SIZE; h++)
674 {
675 for (d = cache->ents[h]; d; d = next)
676 {
677 next = d->next;
678 FcGlobalCacheDirDestroy (d);
679 }
680 }
681 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache));
682 free (cache);
683 }
684
685 /*
686 * Cache file syntax is quite simple:
687 *
688 * "file_name" id time "font_name" \n
689 */
690
691 void
692 FcGlobalCacheLoad (FcGlobalCache *cache,
693 const FcChar8 *cache_file)
694 {
695 FILE *f;
696 FcChar8 file_buf[8192], *file;
697 int id;
698 time_t time;
699 FcChar8 name_buf[8192], *name;
700 FcGlobalCacheInfo *info;
701
702 f = fopen ((char *) cache_file, "r");
703 if (!f)
704 return;
705
706 cache->updated = FcFalse;
707 file = 0;
708 name = 0;
709 while ((file = FcCacheReadString (f, file_buf, sizeof (file_buf))) &&
710 FcCacheReadInt (f, &id) &&
711 FcCacheReadTime (f, &time) &&
712 (name = FcCacheReadString (f, name_buf, sizeof (name_buf))))
713 {
714 if (FcDebug () & FC_DBG_CACHEV)
715 printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file, name);
716 if (!FcStrCmp (name, FC_FONT_FILE_DIR))
717 info = FcGlobalCacheDirAdd (cache, file, time, FcFalse);
718 else
719 info = FcGlobalCacheFileAdd (cache, file, id, time, name, FcFalse);
720 if (!info)
721 cache->broken = FcTrue;
722 else
723 cache->entries++;
724 if (FcDebug () & FC_DBG_CACHE_REF)
725 printf ("FcGlobalCacheLoad entry %d %s\n",
726 cache->entries, file);
727 if (file != file_buf)
728 free (file);
729 if (name != name_buf)
730 free (name);
731 file = 0;
732 name = 0;
733 }
734 if (file && file != file_buf)
735 free (file);
736 if (name && name != name_buf)
737 free (name);
738 fclose (f);
739 }
740
741 FcBool
742 FcGlobalCacheUpdate (FcGlobalCache *cache,
743 const FcChar8 *file,
744 int id,
745 const FcChar8 *name)
746 {
747 const FcChar8 *match;
748 struct stat statb;
749 FcGlobalCacheInfo *info;
750
751 match = file;
752
753 if (stat ((char *) file, &statb) < 0)
754 return FcFalse;
755 if (S_ISDIR (statb.st_mode))
756 info = FcGlobalCacheDirAdd (cache, file, statb.st_mtime,
757 FcTrue);
758 else
759 info = FcGlobalCacheFileAdd (cache, file, id, statb.st_mtime,
760 name, FcTrue);
761 if (info)
762 {
763 FcGlobalCacheReferenced (cache, info);
764 cache->updated = FcTrue;
765 }
766 else
767 cache->broken = FcTrue;
768 return info != 0;
769 }
770
771 FcBool
772 FcGlobalCacheSave (FcGlobalCache *cache,
773 const FcChar8 *cache_file)
774 {
775 FILE *f;
776 int dir_hash, file_hash;
777 FcGlobalCacheDir *dir;
778 FcGlobalCacheFile *file;
779 FcAtomic *atomic;
780
781 if (!cache->updated && cache->referenced == cache->entries)
782 return FcTrue;
783
784 if (cache->broken)
785 return FcFalse;
786
787 /* Set-UID programs can't safely update the cache */
788 if (getuid () != geteuid ())
789 return FcFalse;
790
791 atomic = FcAtomicCreate (cache_file);
792 if (!atomic)
793 goto bail0;
794 if (!FcAtomicLock (atomic))
795 goto bail1;
796 f = fopen ((char *) FcAtomicNewFile(atomic), "w");
797 if (!f)
798 goto bail2;
799
800 for (dir_hash = 0; dir_hash < FC_GLOBAL_CACHE_DIR_HASH_SIZE; dir_hash++)
801 {
802 for (dir = cache->ents[dir_hash]; dir; dir = dir->next)
803 {
804 if (!dir->info.referenced)
805 continue;
806 if (!FcCacheWriteString (f, dir->info.file))
807 goto bail4;
808 if (PUTC (' ', f) == EOF)
809 goto bail4;
810 if (!FcCacheWriteInt (f, 0))
811 goto bail4;
812 if (PUTC (' ', f) == EOF)
813 goto bail4;
814 if (!FcCacheWriteTime (f, dir->info.time))
815 goto bail4;
816 if (PUTC (' ', f) == EOF)
817 goto bail4;
818 if (!FcCacheWriteString (f, (FcChar8 *) FC_FONT_FILE_DIR))
819 goto bail4;
820 if (PUTC ('\n', f) == EOF)
821 goto bail4;
822
823 for (file_hash = 0; file_hash < FC_GLOBAL_CACHE_FILE_HASH_SIZE; file_hash++)
824 {
825 for (file = dir->ents[file_hash]; file; file = file->next)
826 {
827 if (!file->info.referenced)
828 continue;
829 if (!FcCacheWritePath (f, dir->info.file, file->info.file))
830 goto bail4;
831 if (PUTC (' ', f) == EOF)
832 goto bail4;
833 if (!FcCacheWriteInt (f, file->id < 0 ? 0 : file->id))
834 goto bail4;
835 if (PUTC (' ', f) == EOF)
836 goto bail4;
837 if (!FcCacheWriteTime (f, file->info.time))
838 goto bail4;
839 if (PUTC (' ', f) == EOF)
840 goto bail4;
841 if (!FcCacheWriteString (f, file->name))
842 goto bail4;
843 if (PUTC ('\n', f) == EOF)
844 goto bail4;
845 }
846 }
847 }
848 }
849
850 if (fclose (f) == EOF)
851 goto bail3;
852
853 if (!FcAtomicReplaceOrig (atomic))
854 goto bail3;
855
856 FcAtomicUnlock (atomic);
857 FcAtomicDestroy (atomic);
858
859 cache->updated = FcFalse;
860 return FcTrue;
861
862 bail4:
863 fclose (f);
864 bail3:
865 FcAtomicDeleteNew (atomic);
866 bail2:
867 FcAtomicUnlock (atomic);
868 bail1:
869 FcAtomicDestroy (atomic);
870 bail0:
871 return FcFalse;
872 }
873
874 FcBool
875 FcDirCacheValid (const FcChar8 *dir)
876 {
877 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
878 struct stat file_stat, dir_stat;
879
880 if (stat ((char *) dir, &dir_stat) < 0)
881 {
882 FcStrFree (cache_file);
883 return FcFalse;
884 }
885 if (stat ((char *) cache_file, &file_stat) < 0)
886 {
887 FcStrFree (cache_file);
888 return FcFalse;
889 }
890 FcStrFree (cache_file);
891 /*
892 * If the directory has been modified more recently than
893 * the cache file, the cache is not valid
894 */
895 if (dir_stat.st_mtime - file_stat.st_mtime > 0)
896 return FcFalse;
897 return FcTrue;
898 }
899
900 FcBool
901 FcDirCacheReadDir (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
902 {
903 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
904 FILE *f;
905 FcChar8 *base;
906 int id;
907 int dir_len;
908 FcChar8 file_buf[8192], *file;
909 FcChar8 name_buf[8192], *name;
910 FcBool ret = FcFalse;
911
912 if (!cache_file)
913 goto bail0;
914
915 if (FcDebug () & FC_DBG_CACHE)
916 printf ("FcDirCacheReadDir cache_file \"%s\"\n", cache_file);
917
918 f = fopen ((char *) cache_file, "r");
919 if (!f)
920 {
921 if (FcDebug () & FC_DBG_CACHE)
922 printf (" no cache file\n");
923 goto bail1;
924 }
925
926 if (!FcDirCacheValid (dir))
927 {
928 if (FcDebug () & FC_DBG_CACHE)
929 printf (" cache file older than directory\n");
930 goto bail2;
931 }
932
933 base = (FcChar8 *) strrchr ((char *) cache_file, '/');
934 if (!base)
935 goto bail2;
936 base++;
937 dir_len = base - cache_file;
938
939 file = 0;
940 name = 0;
941 while ((file = FcCacheReadString (f, file_buf, sizeof (file_buf))) &&
942 FcCacheReadInt (f, &id) &&
943 (name = FcCacheReadString (f, name_buf, sizeof (name_buf))))
944 {
945 if (!FcCacheFontSetAdd (set, dirs, cache_file, dir_len,
946 file, name))
947 goto bail3;
948 if (file != file_buf)
949 free (file);
950 if (name != name_buf)
951 free (name);
952 file = name = 0;
953 }
954 if (FcDebug () & FC_DBG_CACHE)
955 printf (" cache loaded\n");
956
957 ret = FcTrue;
958 bail3:
959 if (file && file != file_buf)
960 free (file);
961 if (name && name != name_buf)
962 free (name);
963 bail2:
964 fclose (f);
965 bail1:
966 FcStrFree (cache_file);
967 bail0:
968 return ret;
969 }
970
971 /*
972 * return the path from the directory containing 'cache' to 'file'
973 */
974
975 static const FcChar8 *
976 FcFileBaseName (const FcChar8 *cache, const FcChar8 *file)
977 {
978 const FcChar8 *cache_slash;
979
980 cache_slash = (const FcChar8 *) strrchr ((const char *) cache, '/');
981 if (cache_slash && !strncmp ((const char *) cache, (const char *) file,
982 (cache_slash + 1) - cache))
983 return file + ((cache_slash + 1) - cache);
984 return file;
985 }
986
987 FcBool
988 FcDirCacheWriteDir (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
989 {
990 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
991 FcPattern *font;
992 FILE *f;
993 FcChar8 *name;
994 const FcChar8 *file, *base;
995 int n;
996 int id;
997 FcBool ret;
998 FcStrList *list;
999
1000 if (!cache_file)
1001 goto bail0;
1002 if (FcDebug () & FC_DBG_CACHE)
1003 printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file);
1004
1005 f = fopen ((char *) cache_file, "w");
1006 if (!f)
1007 {
1008 if (FcDebug () & FC_DBG_CACHE)
1009 printf (" can't create \"%s\"\n", cache_file);
1010 goto bail1;
1011 }
1012
1013 list = FcStrListCreate (dirs);
1014 if (!list)
1015 goto bail2;
1016
1017 while ((dir = FcStrListNext (list)))
1018 {
1019 base = FcFileBaseName (cache_file, dir);
1020 if (!FcCacheWriteString (f, base))
1021 goto bail3;
1022 if (PUTC (' ', f) == EOF)
1023 goto bail3;
1024 if (!FcCacheWriteInt (f, 0))
1025 goto bail3;
1026 if (PUTC (' ', f) == EOF)
1027 goto bail3;
1028 if (!FcCacheWriteString (f, FC_FONT_FILE_DIR))
1029 goto bail3;
1030 if (PUTC ('\n', f) == EOF)
1031 goto bail3;
1032 }
1033
1034 for (n = 0; n < set->nfont; n++)
1035 {
1036 font = set->fonts[n];
1037 if (FcPatternGetString (font, FC_FILE, 0, (FcChar8 **) &file) != FcResultMatch)
1038 goto bail3;
1039 base = FcFileBaseName (cache_file, file);
1040 if (FcPatternGetInteger (font, FC_INDEX, 0, &id) != FcResultMatch)
1041 goto bail3;
1042 if (FcDebug () & FC_DBG_CACHEV)
1043 printf (" write file \"%s\"\n", base);
1044 if (!FcCacheWriteString (f, base))
1045 goto bail3;
1046 if (PUTC (' ', f) == EOF)
1047 goto bail3;
1048 if (!FcCacheWriteInt (f, id))
1049 goto bail3;
1050 if (PUTC (' ', f) == EOF)
1051 goto bail3;
1052 name = FcNameUnparse (font);
1053 if (!name)
1054 goto bail3;
1055 ret = FcCacheWriteString (f, name);
1056 FcStrFree (name);
1057 if (!ret)
1058 goto bail3;
1059 if (PUTC ('\n', f) == EOF)
1060 goto bail3;
1061 }
1062
1063 FcStrListDone (list);
1064
1065 if (fclose (f) == EOF)
1066 goto bail1;
1067
1068 FcStrFree (cache_file);
1069
1070 if (FcDebug () & FC_DBG_CACHE)
1071 printf (" cache written\n");
1072 return FcTrue;
1073
1074 bail3:
1075 FcStrListDone (list);
1076 bail2:
1077 fclose (f);
1078 bail1:
1079 unlink ((char *) cache_file);
1080 FcStrFree (cache_file);
1081 bail0:
1082 return FcFalse;
1083 }