2 * $XFree86: xc/lib/fontconfig/src/fccache.c,v 1.10 2002/08/06 19:00:43 keithp Exp $
4 * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
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.
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.
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.
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)
38 #define GETC(f) getc(f)
39 #define PUTC(c,f) putc(c,f)
42 #define FC_DBG_CACHE_REF 1024
45 FcCacheReadString (FILE *f
, FcChar8
*dest
, int len
)
53 while ((c
= GETC (f
)) != EOF
)
65 while ((c
= GETC (f
)) != EOF
)
80 FcChar8
*new = malloc (size
* 2);
83 memcpy (new, d
, size
);
100 FcCacheReadUlong (FILE *f
, unsigned long *dest
)
105 while ((c
= GETC (f
)) != EOF
)
115 if (c
== EOF
|| isspace (c
))
119 t
= t
* 10 + (c
- '0');
127 FcCacheReadInt (FILE *f
, int *dest
)
132 ret
= FcCacheReadUlong (f
, &t
);
139 FcCacheReadTime (FILE *f
, time_t *dest
)
144 ret
= FcCacheReadUlong (f
, &t
);
151 FcCacheWriteChars (FILE *f
, const FcChar8
*chars
)
154 while ((c
= *chars
++))
159 if (PUTC ('\\', f
) == EOF
)
163 if (PUTC (c
, f
) == EOF
)
171 FcCacheWriteString (FILE *f
, const FcChar8
*string
)
174 if (PUTC ('"', f
) == EOF
)
176 if (!FcCacheWriteChars (f
, string
))
178 if (PUTC ('"', f
) == EOF
)
184 FcCacheWritePath (FILE *f
, const FcChar8
*dir
, const FcChar8
*file
)
186 if (PUTC ('"', f
) == EOF
)
189 if (!FcCacheWriteChars (f
, dir
))
191 if (dir
&& dir
[strlen((const char *) dir
) - 1] != '/')
192 if (PUTC ('/', f
) == EOF
)
194 if (!FcCacheWriteChars (f
, file
))
196 if (PUTC ('"', f
) == EOF
)
202 FcCacheWriteUlong (FILE *f
, unsigned long t
)
205 unsigned long temp
, digit
;
218 if (PUTC ((char) digit
+ '0', f
) == EOF
)
220 temp
= temp
- pow
* digit
;
227 FcCacheWriteInt (FILE *f
, int i
)
229 return FcCacheWriteUlong (f
, (unsigned long) i
);
233 FcCacheWriteTime (FILE *f
, time_t t
)
235 return FcCacheWriteUlong (f
, (unsigned long) t
);
239 FcCacheFontSetAdd (FcFontSet
*set
,
246 FcChar8 path_buf
[8192], *path
;
248 FcBool ret
= FcFalse
;
252 len
= (dir_len
+ 1 + strlen ((const char *) file
) + 1);
253 if (len
> sizeof (path_buf
))
259 strncpy ((char *) path
, (const char *) dir
, dir_len
);
260 if (dir
[dir_len
- 1] != '/')
261 path
[dir_len
++] = '/';
262 strcpy ((char *) path
+ dir_len
, (const char *) file
);
263 if (!FcStrCmp (name
, FC_FONT_FILE_DIR
))
265 if (FcDebug () & FC_DBG_CACHEV
)
266 printf (" dir cache dir \"%s\"\n", path
);
267 ret
= FcStrSetAdd (dirs
, path
);
269 else if (!FcStrCmp (name
, FC_FONT_FILE_INVALID
))
275 font
= FcNameParse (name
);
278 if (FcDebug () & FC_DBG_CACHEV
)
279 printf (" dir cache file \"%s\"\n", file
);
280 ret
= (FcPatternAddString (font
, FC_FILE
, path
) &&
281 FcFontSetAdd (set
, font
));
283 FcPatternDestroy (font
);
286 if (path
!= path_buf
) free (path
);
292 FcCacheHash (const FcChar8
*string
)
297 while ((c
= *string
++))
303 * Verify the saved timestamp for a file
306 FcGlobalCacheCheckTime (FcGlobalCacheInfo
*info
)
310 if (stat ((char *) info
->file
, &statb
) < 0)
312 if (FcDebug () & FC_DBG_CACHE
)
313 printf (" file missing\n");
316 if (statb
.st_mtime
!= info
->time
)
318 if (FcDebug () & FC_DBG_CACHE
)
319 printf (" timestamp mismatch (was %d is %d)\n",
320 (int) info
->time
, (int) statb
.st_mtime
);
327 FcGlobalCacheReferenced (FcGlobalCache
*cache
,
328 FcGlobalCacheInfo
*info
)
330 if (!info
->referenced
)
332 info
->referenced
= FcTrue
;
334 if (FcDebug () & FC_DBG_CACHE_REF
)
335 printf ("Reference %d %s\n", cache
->referenced
, info
->file
);
340 * Break a path into dir/base elements and compute the base hash
341 * and the dir length. This is shared between the functions
342 * which walk the file caches
345 typedef struct _FcFilePathInfo
{
349 unsigned int base_hash
;
352 static FcFilePathInfo
353 FcFilePathInfoGet (const FcChar8
*path
)
358 slash
= (FcChar8
*) strrchr ((const char *) path
, '/');
362 i
.dir_len
= slash
- path
;
369 i
.dir
= (const FcChar8
*) ".";
373 i
.base_hash
= FcCacheHash (i
.base
);
378 FcGlobalCacheDirGet (FcGlobalCache
*cache
,
381 FcBool create_missing
)
383 unsigned int hash
= FcCacheHash (dir
);
384 FcGlobalCacheDir
*d
, **prev
;
386 for (prev
= &cache
->ents
[hash
% FC_GLOBAL_CACHE_DIR_HASH_SIZE
];
388 prev
= &(*prev
)->next
)
390 if (d
->info
.hash
== hash
&& d
->len
== len
&&
391 !strncmp ((const char *) d
->info
.file
,
392 (const char *) dir
, len
))
400 d
= malloc (sizeof (FcGlobalCacheDir
) + len
+ 1);
406 d
->info
.file
= (FcChar8
*) (d
+ 1);
407 strncpy ((char *) d
->info
.file
, (const char *) dir
, len
);
408 d
->info
.file
[len
] = '\0';
410 d
->info
.referenced
= FcFalse
;
412 for (i
= 0; i
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; i
++)
419 static FcGlobalCacheInfo
*
420 FcGlobalCacheDirAdd (FcGlobalCache
*cache
,
427 FcGlobalCacheSubdir
*subdir
;
428 FcGlobalCacheDir
*parent
;
431 * Add this directory to the cache
433 d
= FcGlobalCacheDirGet (cache
, dir
, strlen ((const char *) dir
), FcTrue
);
437 i
= FcFilePathInfoGet (dir
);
439 * Add this directory to the subdirectory list of the parent
441 parent
= FcGlobalCacheDirGet (cache
, i
.dir
, i
.dir_len
, FcTrue
);
444 subdir
= malloc (sizeof (FcGlobalCacheSubdir
) +
445 strlen ((const char *) i
.base
) + 1);
448 subdir
->file
= (FcChar8
*) (subdir
+ 1);
449 strcpy ((char *) subdir
->file
, (const char *) i
.base
);
450 subdir
->next
= parent
->subdirs
;
451 parent
->subdirs
= subdir
;
456 FcGlobalCacheDirDestroy (FcGlobalCacheDir
*d
)
458 FcGlobalCacheFile
*f
, *next
;
460 FcGlobalCacheSubdir
*s
, *nexts
;
462 for (h
= 0; h
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; h
++)
463 for (f
= d
->ents
[h
]; f
; f
= next
)
468 for (s
= d
->subdirs
; s
; s
= nexts
)
477 FcGlobalCacheScanDir (FcFontSet
*set
,
479 FcGlobalCache
*cache
,
482 FcGlobalCacheDir
*d
= FcGlobalCacheDirGet (cache
, dir
,
483 strlen ((const char *) dir
),
485 FcGlobalCacheFile
*f
;
488 FcGlobalCacheSubdir
*subdir
;
490 if (FcDebug() & FC_DBG_CACHE
)
491 printf ("FcGlobalCacheScanDir %s\n", dir
);
495 if (FcDebug () & FC_DBG_CACHE
)
496 printf ("\tNo dir cache entry\n");
500 if (!FcGlobalCacheCheckTime (&d
->info
))
502 if (FcDebug () & FC_DBG_CACHE
)
503 printf ("\tdir cache entry time mismatch\n");
507 dir_len
= strlen ((const char *) dir
);
508 for (h
= 0; h
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; h
++)
509 for (f
= d
->ents
[h
]; f
; f
= f
->next
)
511 if (FcDebug() & FC_DBG_CACHEV
)
512 printf ("FcGlobalCacheScanDir add file %s\n", f
->info
.file
);
513 if (!FcCacheFontSetAdd (set
, dirs
, dir
, dir_len
,
514 f
->info
.file
, f
->name
))
516 cache
->broken
= FcTrue
;
519 FcGlobalCacheReferenced (cache
, &f
->info
);
521 for (subdir
= d
->subdirs
; subdir
; subdir
= subdir
->next
)
523 if (!FcCacheFontSetAdd (set
, dirs
, dir
, dir_len
,
524 subdir
->file
, FC_FONT_FILE_DIR
))
526 cache
->broken
= FcTrue
;
531 FcGlobalCacheReferenced (cache
, &d
->info
);
537 * Locate the cache entry for a particular file
540 FcGlobalCacheFileGet (FcGlobalCache
*cache
,
545 FcFilePathInfo i
= FcFilePathInfoGet (file
);
546 FcGlobalCacheDir
*d
= FcGlobalCacheDirGet (cache
, i
.dir
,
548 FcGlobalCacheFile
*f
, *match
= 0;
553 for (f
= d
->ents
[i
.base_hash
% FC_GLOBAL_CACHE_FILE_HASH_SIZE
]; f
; f
= f
->next
)
555 if (f
->info
.hash
== i
.base_hash
&&
556 !strcmp ((const char *) f
->info
.file
, (const char *) i
.base
))
570 * Add a file entry to the cache
572 static FcGlobalCacheInfo
*
573 FcGlobalCacheFileAdd (FcGlobalCache
*cache
,
580 FcFilePathInfo i
= FcFilePathInfoGet (path
);
581 FcGlobalCacheDir
*d
= FcGlobalCacheDirGet (cache
, i
.dir
,
583 FcGlobalCacheFile
*f
, **prev
;
587 for (prev
= &d
->ents
[i
.base_hash
% FC_GLOBAL_CACHE_FILE_HASH_SIZE
];
589 prev
= &(*prev
)->next
)
591 if (f
->info
.hash
== i
.base_hash
&&
593 !strcmp ((const char *) f
->info
.file
, (const char *) i
.base
))
604 if (f
->info
.referenced
)
609 f
= malloc (sizeof (FcGlobalCacheFile
) +
610 strlen ((char *) i
.base
) + 1 +
611 strlen ((char *) name
) + 1);
616 f
->info
.hash
= i
.base_hash
;
617 f
->info
.file
= (FcChar8
*) (f
+ 1);
619 f
->info
.referenced
= FcFalse
;
621 f
->name
= f
->info
.file
+ strlen ((char *) i
.base
) + 1;
622 strcpy ((char *) f
->info
.file
, (const char *) i
.base
);
623 strcpy ((char *) f
->name
, (const char *) name
);
628 FcGlobalCacheCreate (void)
630 FcGlobalCache
*cache
;
633 cache
= malloc (sizeof (FcGlobalCache
));
636 for (h
= 0; h
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; h
++)
639 cache
->referenced
= 0;
640 cache
->updated
= FcFalse
;
641 cache
->broken
= FcFalse
;
646 FcGlobalCacheDestroy (FcGlobalCache
*cache
)
648 FcGlobalCacheDir
*d
, *next
;
651 for (h
= 0; h
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; h
++)
653 for (d
= cache
->ents
[h
]; d
; d
= next
)
656 FcGlobalCacheDirDestroy (d
);
663 * Cache file syntax is quite simple:
665 * "file_name" id time "font_name" \n
669 FcGlobalCacheLoad (FcGlobalCache
*cache
,
670 const FcChar8
*cache_file
)
673 FcChar8 file_buf
[8192], *file
;
676 FcChar8 name_buf
[8192], *name
;
677 FcGlobalCacheInfo
*info
;
679 f
= fopen ((char *) cache_file
, "r");
683 cache
->updated
= FcFalse
;
686 while ((file
= FcCacheReadString (f
, file_buf
, sizeof (file_buf
))) &&
687 FcCacheReadInt (f
, &id
) &&
688 FcCacheReadTime (f
, &time
) &&
689 (name
= FcCacheReadString (f
, name_buf
, sizeof (name_buf
))))
691 if (FcDebug () & FC_DBG_CACHEV
)
692 printf ("FcGlobalCacheLoad \"%s\" \"%20.20s\"\n", file
, name
);
693 if (!FcStrCmp (name
, FC_FONT_FILE_DIR
))
694 info
= FcGlobalCacheDirAdd (cache
, file
, time
, FcFalse
);
696 info
= FcGlobalCacheFileAdd (cache
, file
, id
, time
, name
, FcFalse
);
698 cache
->broken
= FcTrue
;
701 if (FcDebug () & FC_DBG_CACHE_REF
)
702 printf ("FcGlobalCacheLoad entry %d %s\n",
703 cache
->entries
, file
);
704 if (file
!= file_buf
)
706 if (name
!= name_buf
)
711 if (file
&& file
!= file_buf
)
713 if (name
&& name
!= name_buf
)
719 FcGlobalCacheUpdate (FcGlobalCache
*cache
,
724 const FcChar8
*match
;
726 FcGlobalCacheInfo
*info
;
730 if (stat ((char *) file
, &statb
) < 0)
732 if (S_ISDIR (statb
.st_mode
))
733 info
= FcGlobalCacheDirAdd (cache
, file
, statb
.st_mtime
,
736 info
= FcGlobalCacheFileAdd (cache
, file
, id
, statb
.st_mtime
,
740 FcGlobalCacheReferenced (cache
, info
);
741 cache
->updated
= FcTrue
;
744 cache
->broken
= FcTrue
;
749 FcGlobalCacheSave (FcGlobalCache
*cache
,
750 const FcChar8
*cache_file
)
753 int dir_hash
, file_hash
;
754 FcGlobalCacheDir
*dir
;
755 FcGlobalCacheFile
*file
;
758 if (!cache
->updated
&& cache
->referenced
== cache
->entries
)
764 /* Set-UID programs can't safely update the cache */
765 if (getuid () != geteuid ())
768 atomic
= FcAtomicCreate (cache_file
);
771 if (!FcAtomicLock (atomic
))
773 f
= fopen ((char *) FcAtomicNewFile(atomic
), "w");
777 for (dir_hash
= 0; dir_hash
< FC_GLOBAL_CACHE_DIR_HASH_SIZE
; dir_hash
++)
779 for (dir
= cache
->ents
[dir_hash
]; dir
; dir
= dir
->next
)
781 if (!dir
->info
.referenced
)
783 if (!FcCacheWriteString (f
, dir
->info
.file
))
785 if (PUTC (' ', f
) == EOF
)
787 if (!FcCacheWriteInt (f
, 0))
789 if (PUTC (' ', f
) == EOF
)
791 if (!FcCacheWriteTime (f
, dir
->info
.time
))
793 if (PUTC (' ', f
) == EOF
)
795 if (!FcCacheWriteString (f
, (FcChar8
*) FC_FONT_FILE_DIR
))
797 if (PUTC ('\n', f
) == EOF
)
800 for (file_hash
= 0; file_hash
< FC_GLOBAL_CACHE_FILE_HASH_SIZE
; file_hash
++)
802 for (file
= dir
->ents
[file_hash
]; file
; file
= file
->next
)
804 if (!file
->info
.referenced
)
806 if (!FcCacheWritePath (f
, dir
->info
.file
, file
->info
.file
))
808 if (PUTC (' ', f
) == EOF
)
810 if (!FcCacheWriteInt (f
, file
->id
< 0 ? 0 : file
->id
))
812 if (PUTC (' ', f
) == EOF
)
814 if (!FcCacheWriteTime (f
, file
->info
.time
))
816 if (PUTC (' ', f
) == EOF
)
818 if (!FcCacheWriteString (f
, file
->name
))
820 if (PUTC ('\n', f
) == EOF
)
827 if (fclose (f
) == EOF
)
830 if (!FcAtomicReplaceOrig (atomic
))
833 FcAtomicUnlock (atomic
);
834 FcAtomicDestroy (atomic
);
836 cache
->updated
= FcFalse
;
842 FcAtomicDeleteNew (atomic
);
844 FcAtomicUnlock (atomic
);
846 FcAtomicDestroy (atomic
);
852 FcDirCacheValid (const FcChar8
*dir
)
854 FcChar8
*cache_file
= FcStrPlus (dir
, (FcChar8
*) "/" FC_DIR_CACHE_FILE
);
855 struct stat file_stat
, dir_stat
;
857 if (stat ((char *) dir
, &dir_stat
) < 0)
859 FcStrFree (cache_file
);
862 if (stat ((char *) cache_file
, &file_stat
) < 0)
864 FcStrFree (cache_file
);
867 FcStrFree (cache_file
);
869 * If the directory has been modified more recently than
870 * the cache file, the cache is not valid
872 if (dir_stat
.st_mtime
- file_stat
.st_mtime
> 0)
878 FcDirCacheReadDir (FcFontSet
*set
, FcStrSet
*dirs
, const FcChar8
*dir
)
880 FcChar8
*cache_file
= FcStrPlus (dir
, (FcChar8
*) "/" FC_DIR_CACHE_FILE
);
885 FcChar8 file_buf
[8192], *file
;
886 FcChar8 name_buf
[8192], *name
;
887 FcBool ret
= FcFalse
;
892 if (FcDebug () & FC_DBG_CACHE
)
893 printf ("FcDirCacheReadDir cache_file \"%s\"\n", cache_file
);
895 f
= fopen ((char *) cache_file
, "r");
898 if (FcDebug () & FC_DBG_CACHE
)
899 printf (" no cache file\n");
903 if (!FcDirCacheValid (dir
))
905 if (FcDebug () & FC_DBG_CACHE
)
906 printf (" cache file older than directory\n");
910 base
= (FcChar8
*) strrchr ((char *) cache_file
, '/');
914 dir_len
= base
- cache_file
;
918 while ((file
= FcCacheReadString (f
, file_buf
, sizeof (file_buf
))) &&
919 FcCacheReadInt (f
, &id
) &&
920 (name
= FcCacheReadString (f
, name_buf
, sizeof (name_buf
))))
922 if (!FcCacheFontSetAdd (set
, dirs
, cache_file
, dir_len
,
925 if (file
!= file_buf
)
927 if (name
!= name_buf
)
931 if (FcDebug () & FC_DBG_CACHE
)
932 printf (" cache loaded\n");
936 if (file
&& file
!= file_buf
)
938 if (name
&& name
!= name_buf
)
949 * return the path from the directory containing 'cache' to 'file'
952 static const FcChar8
*
953 FcFileBaseName (const FcChar8
*cache
, const FcChar8
*file
)
955 const FcChar8
*cache_slash
;
957 cache_slash
= (const FcChar8
*) strrchr ((const char *) cache
, '/');
958 if (cache_slash
&& !strncmp ((const char *) cache
, (const char *) file
,
959 (cache_slash
+ 1) - cache
))
960 return file
+ ((cache_slash
+ 1) - cache
);
965 FcDirCacheWriteDir (FcFontSet
*set
, FcStrSet
*dirs
, const FcChar8
*dir
)
967 FcChar8
*cache_file
= FcStrPlus (dir
, (FcChar8
*) "/" FC_DIR_CACHE_FILE
);
971 const FcChar8
*file
, *base
;
979 if (FcDebug () & FC_DBG_CACHE
)
980 printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file
);
982 f
= fopen ((char *) cache_file
, "w");
985 if (FcDebug () & FC_DBG_CACHE
)
986 printf (" can't create \"%s\"\n", cache_file
);
990 list
= FcStrListCreate (dirs
);
994 while ((dir
= FcStrListNext (list
)))
996 base
= FcFileBaseName (cache_file
, dir
);
997 if (!FcCacheWriteString (f
, base
))
999 if (PUTC (' ', f
) == EOF
)
1001 if (!FcCacheWriteInt (f
, 0))
1003 if (PUTC (' ', f
) == EOF
)
1005 if (!FcCacheWriteString (f
, FC_FONT_FILE_DIR
))
1007 if (PUTC ('\n', f
) == EOF
)
1011 for (n
= 0; n
< set
->nfont
; n
++)
1013 font
= set
->fonts
[n
];
1014 if (FcPatternGetString (font
, FC_FILE
, 0, (FcChar8
**) &file
) != FcResultMatch
)
1016 base
= FcFileBaseName (cache_file
, file
);
1017 if (FcPatternGetInteger (font
, FC_INDEX
, 0, &id
) != FcResultMatch
)
1019 if (FcDebug () & FC_DBG_CACHEV
)
1020 printf (" write file \"%s\"\n", base
);
1021 if (!FcCacheWriteString (f
, base
))
1023 if (PUTC (' ', f
) == EOF
)
1025 if (!FcCacheWriteInt (f
, id
))
1027 if (PUTC (' ', f
) == EOF
)
1029 name
= FcNameUnparse (font
);
1032 ret
= FcCacheWriteString (f
, name
);
1036 if (PUTC ('\n', f
) == EOF
)
1040 FcStrListDone (list
);
1042 if (fclose (f
) == EOF
)
1047 if (FcDebug () & FC_DBG_CACHE
)
1048 printf (" cache written\n");
1052 FcStrListDone (list
);
1056 unlink ((char *) cache_file
);